diff --git a/Externals.cfg b/Externals.cfg index 0810b533c2..cb2a61885b 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -32,9 +32,9 @@ protocol = git required = True [UFS_UTILS] -hash = 26cd024 +tag = ufs_utils_1_6_0 local_path = sorc/ufs_utils.fd -repo_url = https://github.com/ufs-community/UFS_UTILS.git +repo_url = https://github.com/NOAA-EMC/UFS_UTILS.git protocol = git required = True diff --git a/env/HERA.env b/env/HERA.env index e8efa3f8b3..f63b24be65 100755 --- a/env/HERA.env +++ b/env/HERA.env @@ -151,6 +151,7 @@ elif [ $step = "fcst" ]; then export NTHREADS_REMAP=${nth_remap:-2} [[ $NTHREADS_REMAP -gt $nth_max ]] && export NTHREADS_REMAP=$nth_max export APRUN_REMAP="$launcher" + export I_MPI_DAPL_UD="enable" elif [ $step = "efcs" ]; then diff --git a/env/JET.env b/env/JET.env index 113c2d1b37..c294c3f5cc 100755 --- a/env/JET.env +++ b/env/JET.env @@ -15,12 +15,14 @@ step=$1 if [[ "$PARTITION_BATCH" = "xjet" ]]; then export npe_node_max=24 -elif [[ "$PARTITION_BATCH" = "vjet" ]]; then +elif [[ "$PARTITION_BATCH" = "vjet" || "$PARTITION_BATCH" = "sjet" ]]; then export npe_node_max=16 +elif [[ "$PARTITION_BATCH" = "kjet" ]]; then + export npe_node_max=40 fi export launcher="srun -l --export=ALL" -# Configure STACK +# Configure MPI environment export OMP_STACKSIZE=2048000 export NTHSTACK=1024000000 @@ -35,35 +37,45 @@ if [ $step = "prep" -o $step = "prepbufr" ]; then nth_max=$(($npe_node_max / $npe_node_prep)) export POE="NO" - export BACK=${BACK:-"NO"} + export BACK="NO" export sys_tp="JET" elif [ $step = "waveinit" -o $step = "waveprep" -o $step = "wavepostsbs" -o $step = "wavepostbndpnt" -o $step = "wavepostbndpntbll" -o $step = "wavepostpnt" ]; then + export mpmd="--multi-prog" + export CFP_MP="YES" if [ $step = "waveprep" ]; then export MP_PULSE=0 ; fi export wavempexec=${launcher} export wave_mpmd=${mpmd} elif [ $step = "anal" ]; then + export MKL_NUM_THREADS=4 + export MKL_CBWR=AUTO + + export CFP_MP=${CFP_MP:-"YES"} + export USE_CFP=${USE_CFP:-"YES"} + export APRUNCFP="$launcher -n \$ncmd --multi-prog" + nth_max=$(($npe_node_max / $npe_node_anal)) - export NTHREADS_GSI=${nth_gsi:-$nth_max} + export NTHREADS_GSI=${nth_anal:-$nth_max} [[ $NTHREADS_GSI -gt $nth_max ]] && export NTHREADS_GSI=$nth_max - export APRUN_GSI="$launcher ${npe_gsi:-${npe_anal:-$PBS_NP}}" + export APRUN_GSI="$launcher" export NTHREADS_CALCINC=${nth_calcinc:-1} [[ $NTHREADS_CALCINC -gt $nth_max ]] && export NTHREADS_CALCINC=$nth_max - export APRUN_CALCINC="$launcher \$ncmd" + export APRUN_CALCINC="$launcher" export NTHREADS_CYCLE=${nth_cycle:-12} [[ $NTHREADS_CYCLE -gt $npe_node_max ]] && export NTHREADS_CYCLE=$npe_node_max npe_cycle=${ntiles:-6} - export APRUN_CYCLE="$launcher $npe_cycle" + export APRUN_CYCLE="$launcher -n $npe_cycle" + export NTHREADS_GAUSFCANL=1 npe_gausfcanl=${npe_gausfcanl:-1} - export APRUN_GAUSFCANL="$launcher $npe_gausfcanl" + export APRUN_GAUSFCANL="$launcher -n $npe_gausfcanl" export NTHREADS_CHGRES=${nth_echgres:-1} [[ $NTHREADS_CHGRES -gt $npe_node_max ]] && export NTHREADS_CHGRES=$npe_node_max @@ -75,27 +87,44 @@ elif [ $step = "gldas" ]; then export NTHREADS_GLDAS=${nth_gldas:-$nth_max} [[ $NTHREADS_GLDAS -gt $nth_max ]] && export NTHREADS_GLDAS=$nth_max - export APRUN_GLDAS="$launcher $npe_gldas" + export APRUN_GLDAS="$launcher -n $npe_gldas" export NTHREADS_GAUSSIAN=${nth_gaussian:-1} [[ $NTHREADS_GAUSSIAN -gt $nth_max ]] && export NTHREADS_GAUSSIAN=$nth_max - export APRUN_GAUSSIAN="$launcher $npe_gaussian" + export APRUN_GAUSSIAN="$launcher -n $npe_gaussian" + +# Must run data processing with exactly the number of tasks as time +# periods being processed. + + npe_gldas_data_proc=$(($gldas_spinup_hours + 12)) + export APRUN_GLDAS_DATA_PROC="$launcher -n $npe_gldas_data_proc --multi-prog" elif [ $step = "eobs" ]; then + export MKL_NUM_THREADS=4 + export MKL_CBWR=AUTO + nth_max=$(($npe_node_max / $npe_node_eobs)) - export NTHREADS_GSI=${nth_gsi:-$nth_max} + export NTHREADS_GSI=${nth_eobs:-$nth_max} [[ $NTHREADS_GSI -gt $nth_max ]] && export NTHREADS_GSI=$nth_max - export APRUN_GSI="$launcher ${npe_gsi:-${npe_eobs:-$PBS_NP}}" + export APRUN_GSI="$launcher" + + export CFP_MP=${CFP_MP:-"YES"} + export USE_CFP=${USE_CFP:-"YES"} + export APRUNCFP="$launcher -n \$ncmd --multi-prog" elif [ $step = "eupd" ]; then nth_max=$(($npe_node_max / $npe_node_eupd)) - export NTHREADS_ENKF=${nth_enkf:-$nth_max} + export NTHREADS_ENKF=${nth_eupd:-$nth_max} [[ $NTHREADS_ENKF -gt $nth_max ]] && export NTHREADS_ENKF=$nth_max - export APRUN_ENKF="$launcher ${npe_enkf:-${npe_eupd:-$PBS_NP}}" + export APRUN_ENKF="$launcher" + + export CFP_MP=${CFP_MP:-"YES"} + export USE_CFP=${USE_CFP:-"YES"} + export APRUNCFP="$launcher -n \$ncmd --multi-prog" elif [ $step = "fcst" ]; then @@ -111,24 +140,28 @@ elif [ $step = "fcst" ]; then export NTHREADS_FV3=${nth_fv3:-$nth_max} [[ $NTHREADS_FV3 -gt $nth_max ]] && export NTHREADS_FV3=$nth_max export cores_per_node=$npe_node_max - export APRUN_FV3="$launcher" + if [[ $CDUMP == "gfs" ]]; then + npe_fcst=$npe_fcst_gfs + fi + export APRUN_FV3="$launcher -n $npe_fcst" export NTHREADS_REGRID_NEMSIO=${nth_regrid_nemsio:-1} [[ $NTHREADS_REGRID_NEMSIO -gt $nth_max ]] && export NTHREADS_REGRID_NEMSIO=$nth_max - export APRUN_REGRID_NEMSIO="$launcher $LEVS" + export APRUN_REGRID_NEMSIO="$launcher" export NTHREADS_REMAP=${nth_remap:-2} [[ $NTHREADS_REMAP -gt $nth_max ]] && export NTHREADS_REMAP=$nth_max - export APRUN_REMAP="$launcher ${npe_remap:-${npe_fcst:-$PBS_NP}}" + export APRUN_REMAP="$launcher" + export I_MPI_DAPL_UD="enable" elif [ $step = "efcs" ]; then nth_max=$(($npe_node_max / $npe_node_efcs)) - export NTHREADS_FV3=${nth_fv3:-$nth_max} + export NTHREADS_FV3=${nth_efcs:-$nth_max} [[ $NTHREADS_FV3 -gt $nth_max ]] && export NTHREADS_FV3=$nth_max export cores_per_node=$npe_node_max - export APRUN_FV3="$launcher ${npe_fv3:-${npe_efcs:-$PBS_NP}}" + export APRUN_FV3="$launcher -n $npe_efcs" export NTHREADS_REGRID_NEMSIO=${nth_regrid_nemsio:-1} [[ $NTHREADS_REGRID_NEMSIO -gt $nth_max ]] && export NTHREADS_REGRID_NEMSIO=$nth_max @@ -140,11 +173,11 @@ elif [ $step = "post" ]; then export NTHREADS_NP=${nth_np:-1} [[ $NTHREADS_NP -gt $nth_max ]] && export NTHREADS_NP=$nth_max - export APRUN_NP="$launcher" + export APRUN_NP="$launcher --epilog=/apps/local/bin/report-mem" ## JKH export NTHREADS_DWN=${nth_dwn:-1} [[ $NTHREADS_DWN -gt $nth_max ]] && export NTHREADS_DWN=$nth_max - export APRUN_NP="$launcher" + export APRUN_DWN="$launcher --epilog=/apps/local/bin/report-mem" ## JKH elif [ $step = "ecen" ]; then @@ -152,7 +185,7 @@ elif [ $step = "ecen" ]; then export NTHREADS_ECEN=${nth_ecen:-$nth_max} [[ $NTHREADS_ECEN -gt $nth_max ]] && export NTHREADS_ECEN=$nth_max - export APRUN_ECEN="$launcher ${npe_ecen:-$PBS_NP}" + export APRUN_ECEN="$launcher" export NTHREADS_CHGRES=${nth_chgres:-12} [[ $NTHREADS_CHGRES -gt $npe_node_max ]] && export NTHREADS_CHGRES=$npe_node_max @@ -160,7 +193,7 @@ elif [ $step = "ecen" ]; then export NTHREADS_CALCINC=${nth_calcinc:-1} [[ $NTHREADS_CALCINC -gt $nth_max ]] && export NTHREADS_CALCINC=$nth_max - export APRUN_CALCINC="$launcher ${npe_ecen:-$PBS_NP}" + export APRUN_CALCINC="$launcher" elif [ $step = "esfc" ]; then @@ -168,12 +201,11 @@ elif [ $step = "esfc" ]; then export NTHREADS_ESFC=${nth_esfc:-$nth_max} [[ $NTHREADS_ESFC -gt $nth_max ]] && export NTHREADS_ESFC=$nth_max - export APRUN_ESFC="$launcher ${npe_esfc:-$PBS_NP}" + export APRUN_ESFC="$launcher -n $npe_esfc" - export NTHREADS_CYCLE=${nth_cycle:-12} + export NTHREADS_CYCLE=${nth_cycle:-14} [[ $NTHREADS_CYCLE -gt $npe_node_max ]] && export NTHREADS_CYCLE=$npe_node_max - export APRUN_CYCLE="$launcher $npe_esfc" - + export APRUN_CYCLE="$launcher -n $npe_esfc" elif [ $step = "epos" ]; then @@ -181,7 +213,7 @@ elif [ $step = "epos" ]; then export NTHREADS_EPOS=${nth_epos:-$nth_max} [[ $NTHREADS_EPOS -gt $nth_max ]] && export NTHREADS_EPOS=$nth_max - export APRUN_EPOS="$launcher ${npe_epos:-$PBS_NP}" + export APRUN_EPOS="$launcher" elif [ $step = "init" ]; then @@ -193,11 +225,11 @@ elif [ $step = "postsnd" ]; then export NTHREADS_POSTSND=${nth_postsnd:-1} [[ $NTHREADS_POSTSND -gt $nth_max ]] && export NTHREADS_POSTSND=$nth_max - export APRUN_POSTSND="$launcher $npe_postsnd" + export APRUN_POSTSND="$launcher" export NTHREADS_POSTSNDCFP=${nth_postsndcfp:-1} [[ $NTHREADS_POSTSNDCFP -gt $nth_max ]] && export NTHREADS_POSTSNDCFP=$nth_max - export APRUN_POSTSNDCFP="$launcher $npe_postsndcfp" + export APRUN_POSTSNDCFP="$launcher" elif [ $step = "awips" ]; then @@ -206,5 +238,4 @@ elif [ $step = "awips" ]; then elif [ $step = "gempak" ]; then echo "WARNING: $step is not enabled on $machine!" - fi diff --git a/env/ORION.env b/env/ORION.env index 079f623f40..be89113c14 100755 --- a/env/ORION.env +++ b/env/ORION.env @@ -17,7 +17,6 @@ export npe_node_max=40 export launcher="srun -l --export=ALL" # Configure MPI environment -export I_MPI_ADJUST_ALLREDUCE=5 export MPI_BUFS_PER_PROC=2048 export MPI_BUFS_PER_HOST=2048 export MPI_GROUP_MAX=256 @@ -147,6 +146,7 @@ elif [ $step = "fcst" ]; then export NTHREADS_REMAP=${nth_remap:-2} [[ $NTHREADS_REMAP -gt $nth_max ]] && export NTHREADS_REMAP=$nth_max export APRUN_REMAP="$launcher" + export I_MPI_DAPL_UD="enable" elif [ $step = "efcs" ]; then diff --git a/env/WCOSS_DELL_P3.env b/env/WCOSS_DELL_P3.env index b7761de2cc..680549c4b2 100755 --- a/env/WCOSS_DELL_P3.env +++ b/env/WCOSS_DELL_P3.env @@ -141,6 +141,7 @@ elif [ $step = "fcst" ]; then export NTHREADS_REMAP=${nth_remap:-2} [[ $NTHREADS_REMAP -gt $nth_max ]] && export NTHREADS_REMAP=$nth_max export APRUN_REMAP="$launcher ${npe_remap:-${npe_fcst:-$PBS_NP}}" + export I_MPI_DAPL_UD="enable" elif [ $step = "efcs" ]; then diff --git a/jobs/rocoto/arch.sh b/jobs/rocoto/arch.sh index 7d17f12c87..e6ce577d1c 100755 --- a/jobs/rocoto/arch.sh +++ b/jobs/rocoto/arch.sh @@ -204,6 +204,18 @@ if [ $CDUMP = "gfs" ]; then targrp_list="$targrp_list ice" fi + # Aerosols + if [ $DO_AERO = "YES" ]; then + for targrp in chem; do + htar -P -cvf $ATARDIR/$CDATE/${targrp}.tar $(cat $ARCH_LIST/${targrp}.txt) + status=$? + if [ $status -ne 0 -a $CDATE -ge $firstday ]; then + echo "HTAR $CDATE ${targrp}.tar failed" + exit $status + fi + done + fi + #for restarts if [ $SAVEFCSTIC = "YES" ]; then targrp_list="$targrp_list gfs_restarta" diff --git a/jobs/rocoto/vrfy.sh b/jobs/rocoto/vrfy.sh deleted file mode 100755 index 9564b5f309..0000000000 --- a/jobs/rocoto/vrfy.sh +++ /dev/null @@ -1,199 +0,0 @@ -#!/bin/ksh -x - -############################################################### -## Abstract: -## Inline verification and diagnostics driver script -## RUN_ENVIR : runtime environment (emc | nco) -## HOMEgfs : /full/path/to/workflow -## EXPDIR : /full/path/to/config/files -## CDATE : current analysis date (YYYYMMDDHH) -## CDUMP : cycle name (gdas / gfs) -## PDY : current date (YYYYMMDD) -## cyc : current cycle (HH) -############################################################### - -############################################################### -echo -echo "=============== START TO SOURCE FV3GFS WORKFLOW MODULES ===============" -. $HOMEgfs/ush/load_fv3gfs_modules.sh -status=$? -[[ $status -ne 0 ]] && exit $status - - -############################################################### -echo -echo "=============== START TO SOURCE RELEVANT CONFIGS ===============" -configs="base vrfy" -for config in $configs; do - . $EXPDIR/config.${config} - status=$? - [[ $status -ne 0 ]] && exit $status -done - - -############################################################### -echo -echo "=============== START TO SOURCE MACHINE RUNTIME ENVIRONMENT ===============" -. $BASE_ENV/${machine}.env vrfy -status=$? -[[ $status -ne 0 ]] && exit $status - -############################################################### -export COMPONENT=${COMPONENT:-atmos} -export CDATEm1=$($NDATE -24 $CDATE) -export PDYm1=$(echo $CDATEm1 | cut -c1-8) - -export pid=${pid:-$$} -export jobid=${job}.${pid} -export COMIN="$ROTDIR/$CDUMP.$PDY/$cyc/$COMPONENT" -export DATAROOT="$RUNDIR/$CDATE/$CDUMP/vrfy.${jobid}" -[[ -d $DATAROOT ]] && rm -rf $DATAROOT -mkdir -p $DATAROOT - - -############################################################### -echo -echo "=============== START TO GENERATE QUARTER DEGREE GRIB1 FILES ===============" -if [ $MKPGB4PRCP = "YES" -a $CDUMP = "gfs" ]; then - if [ ! -d $ARCDIR ]; then mkdir $ARCDIR ; fi - nthreads_env=${OMP_NUM_THREADS:-1} # get threads set in env - export OMP_NUM_THREADS=1 - cd $COMIN - fhmax=${vhr_rain:-$FHMAX_GFS} - fhr=0 - while [ $fhr -le $fhmax ]; do - fhr2=$(printf %02i $fhr) - fhr3=$(printf %03i $fhr) - fname=${CDUMP}.t${cyc}z.sfluxgrbf$fhr3.grib2 - fileout=$ARCDIR/pgbq${fhr2}.${CDUMP}.${CDATE}.grib2 - $WGRIB2 $fname -match "(:PRATE:surface:)|(:TMP:2 m above ground:)" -grib $fileout - (( fhr = $fhr + 6 )) - done - export OMP_NUM_THREADS=$nthreads_env # revert to threads set in env -fi - - -############################################################### -echo -echo "=============== START TO RUN MOS ===============" -if [ $RUNMOS = "YES" -a $CDUMP = "gfs" ]; then - $RUNGFSMOSSH $PDY$cyc -fi - - -############################################################### -echo -echo "=============== START TO RUN FIT2OBS VERIFICATION ===============" -if [ $VRFYFITS = "YES" -a $CDUMP = $CDFNL -a $CDATE != $SDATE ]; then - - export CDUMPFCST=$VDUMP - export TMPDIR="$RUNDIR/$CDATE/$CDUMP" - [[ ! -d $TMPDIR ]] && mkdir -p $TMPDIR - - xdate=$($NDATE -${VBACKUP_FITS} $CDATE) - - - export RUN_ENVIR_SAVE=$RUN_ENVIR - export RUN_ENVIR=$OUTPUT_FILE - - $PREPQFITSH $PSLOT $xdate $ROTDIR $ARCDIR $TMPDIR - - export RUN_ENVIR=$RUN_ENVIR_SAVE - -fi - - -############################################################### -echo -echo "=============== START TO RUN VSDB STEP1, VERIFY PRCIP AND GRID2OBS ===============" -if [ $CDUMP = "gfs" ]; then - - if [ $VSDB_STEP1 = "YES" -o $VRFYPRCP = "YES" -o $VRFYG2OBS = "YES" ]; then - - xdate=$(echo $($NDATE -${BACKDATEVSDB} $CDATE) | cut -c1-8) - export ARCDIR1="$NOSCRUB/archive" - export rundir="$RUNDIR/$CDUMP/$CDATE/vrfy/vsdb_exp" - export COMROT="$ARCDIR1/dummy" - - $VSDBJOBSH $VSDBSH $xdate $vlength $cyc $PSLOT $CDATE $CDUMP $gfs_cyc $rain_bucket $machine - fi -fi - -############################################################### -echo -echo "=============== START TO RUN RADMON DATA EXTRACTION ===============" -if [ $VRFYRAD = "YES" -a $CDUMP = $CDFNL -a $CDATE != $SDATE ]; then - - export EXP=$PSLOT - export COMOUT="$ROTDIR/$CDUMP.$PDY/$cyc/$COMPONENT" - export jlogfile="$ROTDIR/logs/$CDATE/${CDUMP}radmon.log" - export TANKverf_rad="$TANKverf/stats/$PSLOT/$CDUMP.$PDY" - export TANKverf_radM1="$TANKverf/stats/$PSLOT/$CDUMP.$PDYm1" - export MY_MACHINE=$machine - - $VRFYRADSH - -fi - - -############################################################### -echo -echo "=============== START TO RUN OZMON DATA EXTRACTION ===============" -if [ $VRFYOZN = "YES" -a $CDUMP = $CDFNL -a $CDATE != $SDATE ]; then - - export EXP=$PSLOT - export COMOUT="$ROTDIR/$CDUMP.$PDY/$cyc/$COMPONENT" - export jlogfile="$ROTDIR/logs/$CDATE/${CDUMP}oznmon.log" - export TANKverf_ozn="$TANKverf_ozn/stats/$PSLOT/$CDUMP.$PDY" - export TANKverf_oznM1="$TANKverf_ozn/stats/$PSLOT/$CDUMP.$PDYm1" - export MY_MACHINE=$machine - - $VRFYOZNSH - -fi - - -############################################################### -echo -echo "=============== START TO RUN MINMON ===============" -if [ $VRFYMINMON = "YES" -a $CDATE != $SDATE ]; then - - export COMOUT="$ROTDIR/$CDUMP.$PDY/$cyc/$COMPONENT" - export jlogfile="$ROTDIR/logs/$CDATE/${CDUMP}minmon.log" - export M_TANKverfM0="$M_TANKverf/stats/$PSLOT/$CDUMP.$PDY" - export M_TANKverfM1="$M_TANKverf/stats/$PSLOT/$CDUMP.$PDYm1" - export MY_MACHINE=$machine - - $VRFYMINSH - -fi - - -################################################################################ -echo -echo "=============== START TO RUN CYCLONE TRACK VERIFICATION ===============" -if [ $VRFYTRAK = "YES" ]; then - $TRACKERSH -fi - - -################################################################################ -echo -echo "=============== START TO RUN CYCLONE GENESIS VERIFICATION ===============" -if [ $VRFYGENESIS = "YES" -a $CDUMP = "gfs" ]; then - $GENESISSH -fi - - -################################################################################ -echo -echo "=============== START TO RUN CYCLONE GENESIS VERIFICATION (FSU) ===============" -if [ $VRFYFSU = "YES" -a $CDUMP = "gfs" ]; then - $GENESISFSU -fi - - -############################################################### -# Force Exit out cleanly -if [ ${KEEPDATA:-"NO"} = "NO" ] ; then rm -rf $DATAROOT ; fi -exit 0 diff --git a/jobs/rocoto/vrfy.sh b/jobs/rocoto/vrfy.sh new file mode 120000 index 0000000000..063570a2ab --- /dev/null +++ b/jobs/rocoto/vrfy.sh @@ -0,0 +1 @@ +vrfy_gsl.sh \ No newline at end of file diff --git a/jobs/rocoto/vrfy_emc.sh b/jobs/rocoto/vrfy_emc.sh new file mode 100755 index 0000000000..9564b5f309 --- /dev/null +++ b/jobs/rocoto/vrfy_emc.sh @@ -0,0 +1,199 @@ +#!/bin/ksh -x + +############################################################### +## Abstract: +## Inline verification and diagnostics driver script +## RUN_ENVIR : runtime environment (emc | nco) +## HOMEgfs : /full/path/to/workflow +## EXPDIR : /full/path/to/config/files +## CDATE : current analysis date (YYYYMMDDHH) +## CDUMP : cycle name (gdas / gfs) +## PDY : current date (YYYYMMDD) +## cyc : current cycle (HH) +############################################################### + +############################################################### +echo +echo "=============== START TO SOURCE FV3GFS WORKFLOW MODULES ===============" +. $HOMEgfs/ush/load_fv3gfs_modules.sh +status=$? +[[ $status -ne 0 ]] && exit $status + + +############################################################### +echo +echo "=============== START TO SOURCE RELEVANT CONFIGS ===============" +configs="base vrfy" +for config in $configs; do + . $EXPDIR/config.${config} + status=$? + [[ $status -ne 0 ]] && exit $status +done + + +############################################################### +echo +echo "=============== START TO SOURCE MACHINE RUNTIME ENVIRONMENT ===============" +. $BASE_ENV/${machine}.env vrfy +status=$? +[[ $status -ne 0 ]] && exit $status + +############################################################### +export COMPONENT=${COMPONENT:-atmos} +export CDATEm1=$($NDATE -24 $CDATE) +export PDYm1=$(echo $CDATEm1 | cut -c1-8) + +export pid=${pid:-$$} +export jobid=${job}.${pid} +export COMIN="$ROTDIR/$CDUMP.$PDY/$cyc/$COMPONENT" +export DATAROOT="$RUNDIR/$CDATE/$CDUMP/vrfy.${jobid}" +[[ -d $DATAROOT ]] && rm -rf $DATAROOT +mkdir -p $DATAROOT + + +############################################################### +echo +echo "=============== START TO GENERATE QUARTER DEGREE GRIB1 FILES ===============" +if [ $MKPGB4PRCP = "YES" -a $CDUMP = "gfs" ]; then + if [ ! -d $ARCDIR ]; then mkdir $ARCDIR ; fi + nthreads_env=${OMP_NUM_THREADS:-1} # get threads set in env + export OMP_NUM_THREADS=1 + cd $COMIN + fhmax=${vhr_rain:-$FHMAX_GFS} + fhr=0 + while [ $fhr -le $fhmax ]; do + fhr2=$(printf %02i $fhr) + fhr3=$(printf %03i $fhr) + fname=${CDUMP}.t${cyc}z.sfluxgrbf$fhr3.grib2 + fileout=$ARCDIR/pgbq${fhr2}.${CDUMP}.${CDATE}.grib2 + $WGRIB2 $fname -match "(:PRATE:surface:)|(:TMP:2 m above ground:)" -grib $fileout + (( fhr = $fhr + 6 )) + done + export OMP_NUM_THREADS=$nthreads_env # revert to threads set in env +fi + + +############################################################### +echo +echo "=============== START TO RUN MOS ===============" +if [ $RUNMOS = "YES" -a $CDUMP = "gfs" ]; then + $RUNGFSMOSSH $PDY$cyc +fi + + +############################################################### +echo +echo "=============== START TO RUN FIT2OBS VERIFICATION ===============" +if [ $VRFYFITS = "YES" -a $CDUMP = $CDFNL -a $CDATE != $SDATE ]; then + + export CDUMPFCST=$VDUMP + export TMPDIR="$RUNDIR/$CDATE/$CDUMP" + [[ ! -d $TMPDIR ]] && mkdir -p $TMPDIR + + xdate=$($NDATE -${VBACKUP_FITS} $CDATE) + + + export RUN_ENVIR_SAVE=$RUN_ENVIR + export RUN_ENVIR=$OUTPUT_FILE + + $PREPQFITSH $PSLOT $xdate $ROTDIR $ARCDIR $TMPDIR + + export RUN_ENVIR=$RUN_ENVIR_SAVE + +fi + + +############################################################### +echo +echo "=============== START TO RUN VSDB STEP1, VERIFY PRCIP AND GRID2OBS ===============" +if [ $CDUMP = "gfs" ]; then + + if [ $VSDB_STEP1 = "YES" -o $VRFYPRCP = "YES" -o $VRFYG2OBS = "YES" ]; then + + xdate=$(echo $($NDATE -${BACKDATEVSDB} $CDATE) | cut -c1-8) + export ARCDIR1="$NOSCRUB/archive" + export rundir="$RUNDIR/$CDUMP/$CDATE/vrfy/vsdb_exp" + export COMROT="$ARCDIR1/dummy" + + $VSDBJOBSH $VSDBSH $xdate $vlength $cyc $PSLOT $CDATE $CDUMP $gfs_cyc $rain_bucket $machine + fi +fi + +############################################################### +echo +echo "=============== START TO RUN RADMON DATA EXTRACTION ===============" +if [ $VRFYRAD = "YES" -a $CDUMP = $CDFNL -a $CDATE != $SDATE ]; then + + export EXP=$PSLOT + export COMOUT="$ROTDIR/$CDUMP.$PDY/$cyc/$COMPONENT" + export jlogfile="$ROTDIR/logs/$CDATE/${CDUMP}radmon.log" + export TANKverf_rad="$TANKverf/stats/$PSLOT/$CDUMP.$PDY" + export TANKverf_radM1="$TANKverf/stats/$PSLOT/$CDUMP.$PDYm1" + export MY_MACHINE=$machine + + $VRFYRADSH + +fi + + +############################################################### +echo +echo "=============== START TO RUN OZMON DATA EXTRACTION ===============" +if [ $VRFYOZN = "YES" -a $CDUMP = $CDFNL -a $CDATE != $SDATE ]; then + + export EXP=$PSLOT + export COMOUT="$ROTDIR/$CDUMP.$PDY/$cyc/$COMPONENT" + export jlogfile="$ROTDIR/logs/$CDATE/${CDUMP}oznmon.log" + export TANKverf_ozn="$TANKverf_ozn/stats/$PSLOT/$CDUMP.$PDY" + export TANKverf_oznM1="$TANKverf_ozn/stats/$PSLOT/$CDUMP.$PDYm1" + export MY_MACHINE=$machine + + $VRFYOZNSH + +fi + + +############################################################### +echo +echo "=============== START TO RUN MINMON ===============" +if [ $VRFYMINMON = "YES" -a $CDATE != $SDATE ]; then + + export COMOUT="$ROTDIR/$CDUMP.$PDY/$cyc/$COMPONENT" + export jlogfile="$ROTDIR/logs/$CDATE/${CDUMP}minmon.log" + export M_TANKverfM0="$M_TANKverf/stats/$PSLOT/$CDUMP.$PDY" + export M_TANKverfM1="$M_TANKverf/stats/$PSLOT/$CDUMP.$PDYm1" + export MY_MACHINE=$machine + + $VRFYMINSH + +fi + + +################################################################################ +echo +echo "=============== START TO RUN CYCLONE TRACK VERIFICATION ===============" +if [ $VRFYTRAK = "YES" ]; then + $TRACKERSH +fi + + +################################################################################ +echo +echo "=============== START TO RUN CYCLONE GENESIS VERIFICATION ===============" +if [ $VRFYGENESIS = "YES" -a $CDUMP = "gfs" ]; then + $GENESISSH +fi + + +################################################################################ +echo +echo "=============== START TO RUN CYCLONE GENESIS VERIFICATION (FSU) ===============" +if [ $VRFYFSU = "YES" -a $CDUMP = "gfs" ]; then + $GENESISFSU +fi + + +############################################################### +# Force Exit out cleanly +if [ ${KEEPDATA:-"NO"} = "NO" ] ; then rm -rf $DATAROOT ; fi +exit 0 diff --git a/jobs/rocoto/vrfy_gsl.sh b/jobs/rocoto/vrfy_gsl.sh index 331a1bcdbc..b24eb507b1 100755 --- a/jobs/rocoto/vrfy_gsl.sh +++ b/jobs/rocoto/vrfy_gsl.sh @@ -176,14 +176,15 @@ if [ $VRFYTRAK = "YES" ]; then $TRACKERSH fi -# GSL - rename tracker file and change AVNO to GCP1 ## JKH +# GSL - rename tracker file and change AVNO to $ACTFNAME ## JKH export TRACKDIR="${ROTDIR}/../../tracks" +if [ ! -d $TRACKDIR ]; then mkdir $TRACKDIR ; fi typeset -u ucatcf=$ATCFNAME -if [ -f $COMIN/avnop.t00z.cyclone.trackatcfunix ]; then - cat $COMIN/avnop.t00z.cyclone.trackatcfunix | sed s:AVNO:${ucatcf}:g > $TRACKDIR/tctrk.atcf.${CDATE}.${ATCFNAME}.txt +if [ -f $COMIN/avnop.t${cyc}z.cyclone.trackatcfunix ]; then + cat $COMIN/avnop.t${cyc}z.cyclone.trackatcfunix | sed s:AVNO:${ucatcf}:g > $TRACKDIR/tctrk.atcf.${CDATE}.${ATCFNAME}.txt cp -p $TRACKDIR/tctrk.atcf.${CDATE}.${ATCFNAME}.txt $COMIN/tctrk.atcf.${CDATE}.${ATCFNAME}.txt - rm -f $COMIN/avnop.t00z.cyclone.trackatcfunix $COMIN/avno.t00z.cyclone.trackatcfunix - echo "$COMIN/avno*.t00z.cyclone.trackatcfunix deleted...." + rm -f $COMIN/avnop.t${cyc}z.cyclone.trackatcfunix $COMIN/avno.t${cyc}z.cyclone.trackatcfunix + echo "$COMIN/avno*.t${cyc}z.cyclone.trackatcfunix deleted...." else echo "no track file created...." fi diff --git a/modulefiles/module-setup.csh.inc b/modulefiles/module-setup.csh.inc index 3a0bcdd772..aafe5c7fc5 100644 --- a/modulefiles/module-setup.csh.inc +++ b/modulefiles/module-setup.csh.inc @@ -19,6 +19,18 @@ else if ( { test -d /work } ) then source /apps/lmod/init/$__ms_shell endif module purge +else if ( { test -d /scratch1 } ) then + # We are on NOAA Hera + if ( ! { module help >& /dev/null } ) then + source /apps/lmod/lmod/init/$__ms_shell + endif + module purge +else if ( { test -d /jetmon } ) then + # We are on NOAA Jet + if ( ! { module help >& /dev/null } ) then + source /apps/lmod/lmod/init/$__ms_shell + endif + module purge else if ( { test -d /gpfs/hps -a -e /etc/SuSE-release } ) then # We are on NOAA Luna or Surge if ( ! { module help >& /dev/null } ) then diff --git a/modulefiles/module_base.hera b/modulefiles/module_base.hera index 27d0729281..4caf6dcc65 100644 --- a/modulefiles/module_base.hera +++ b/modulefiles/module_base.hera @@ -27,18 +27,17 @@ module load png/1.6.35 module load hdf5/1.10.6 module load netcdf/4.7.4 module load pio/2.5.2 -module load esmf/8_2_0_beta_snapshot_14 +module load esmf/8.2.1b04 module load fms/2021.03 module load bacio/2.4.1 -module load g2/3.4.1 -module load g2tmpl/1.9.1 +module load g2/3.4.2 +module load g2tmpl/1.10.0 module load ip/3.3.3 module load nemsio/2.5.2 module load sp/2.3.3 module load w3emc/2.7.3 module load w3nco/2.4.1 -module load upp/10.0.8 module load wgrib2/2.0.8 setenv WGRIB2 wgrib2 @@ -49,4 +48,3 @@ module load anaconda/2.3.0 # waveprep module load cdo/1.9.5 - diff --git a/modulefiles/module_base.jet b/modulefiles/module_base.jet index 1aa2475a8f..1da0c0359a 100755 --- a/modulefiles/module_base.jet +++ b/modulefiles/module_base.jet @@ -10,16 +10,55 @@ module load hpc/1.1.0 # Load intel compiler and mpi module load hpc-intel/18.0.5.274 module load hpc-impi/2018.4.274 -module load wgrib2/2.0.8 + +# Load hpss module load hpss -module load nco/4.9.1 -module load cdo/1.9.5 + +# Standard 3rd party libraries +module load jasper/2.0.25 +module load zlib/1.2.11 +module load png/1.6.35 + +# Gempak module load gempak/7.4.2 + +# HDF5 and netCDF +module load hdf5/1.10.6 +module load netcdf/4.7.4 + +# NCEP Libs that have no prerequisite libraries module load prod_util/1.2.2 module load grib_util/1.2.2 -module load g2tmpl/1.10.0 module load crtm/2.3.0 +setenv CRTM_FIX /lfs4/HFIP/hfv3gfs/glopara/crtm_v2.3.0 +module load g2tmpl/1.10.0 +module load pio/2.5.2 module load esmf/8_2_0_beta_snapshot_14 module load fms/2021.03 -module load netcdf/4.7.4 -module load rocoto +module load g2/3.4.1 +module load ip/3.3.3 +module load sp/2.3.3 + +# NCO and WGRIB2 need netCDF +module load nco/4.9.1 +module load wgrib2/2.0.8 +setenv WGRIB2 wgrib2 + +# NEMSIO needs BACIO and W3NCO +module load bacio/2.4.1 +module load w3nco/2.4.1 +module load nemsio/2.5.2 + +# W3EMC needs SIGIO and NEMSIO +module load sigio/2.3.2 +module load w3emc/2.7.3 + +# UPP needs G2, G2Tmpl, IP, SP, SIGIO, BACIO, W3NCO, NEMSIO, W3EMC, and CRTM +#JKHmodule load upp/10.0.8 + +# python +module use /contrib/anaconda/modulefiles +module load anaconda/5.3.1 + +# Load CDO for waveprep +module load cdo/1.9.5 diff --git a/modulefiles/module_base.orion b/modulefiles/module_base.orion index eb437ec525..7723b85352 100755 --- a/modulefiles/module_base.orion +++ b/modulefiles/module_base.orion @@ -26,18 +26,17 @@ module load png/1.6.35 module load hdf5/1.10.6 module load netcdf/4.7.4 module load pio/2.5.2 -module load esmf/8_2_0_beta_snapshot_14 +module load esmf/8.2.1b04 module load fms/2021.03 module load bacio/2.4.1 -module load g2/3.4.1 -module load g2tmpl/1.9.1 +module load g2/3.4.2 +module load g2tmpl/1.10.0 module load ip/3.3.3 module load nemsio/2.5.2 module load sp/2.3.3 module load w3emc/2.7.3 module load w3nco/2.4.1 -module load upp/10.0.8 module load wgrib/2.0.8 setenv WGRIB2 wgrib2 diff --git a/modulefiles/module_base.wcoss_dell_p3 b/modulefiles/module_base.wcoss_dell_p3 index ba2dcb0f61..9939a5baf3 100755 --- a/modulefiles/module_base.wcoss_dell_p3 +++ b/modulefiles/module_base.wcoss_dell_p3 @@ -34,18 +34,17 @@ module load png/1.6.35 module load hdf5/1.10.6 module load netcdf/4.7.4 module load pio/2.5.2 -module load esmf/8_2_0_beta_snapshot_14 +module load esmf/8.2.1b04 module load fms/2021.03 module load bacio/2.4.1 -module load g2/3.4.1 -module load g2tmpl/1.9.1 +module load g2/3.4.2 +module load g2tmpl/1.10.0 module load ip/3.3.3 module load nemsio/2.5.2 module load sp/2.3.3 module load w3emc/2.7.3 module load w3nco/2.4.1 -module load upp/10.0.8 module load wgrib2/2.0.8 setenv WGRIB2 wgrib2 diff --git a/modulefiles/workflow_utils.jet b/modulefiles/workflow_utils.jet index 6b1bed3440..7770fd9e7d 100644 --- a/modulefiles/workflow_utils.jet +++ b/modulefiles/workflow_utils.jet @@ -27,6 +27,8 @@ module load ip/3.3.3 module load nemsio/2.5.2 module load nemsiogfs/2.5.3 module load ncio/1.0.0 +module load landsfcutil/2.4.1 +module load wgrib2/2.0.8 module load sigio/2.3.2 module load g2/3.4.1 module load bufr/11.4.0 diff --git a/parm/chem/AERO.rc b/parm/chem/AERO.rc new file mode 100644 index 0000000000..ff40fba2aa --- /dev/null +++ b/parm/chem/AERO.rc @@ -0,0 +1,10 @@ +NX: 4 +NY: 24 + +# Atmospheric Model Configuration Parameters +# ------------------------------------------ +IOSERVER_NODES: 0 + +DYCORE: NONE + +NUM_BANDS: 30 diff --git a/parm/chem/AERO_HISTORY.rc b/parm/chem/AERO_HISTORY.rc new file mode 100644 index 0000000000..e71d970f16 --- /dev/null +++ b/parm/chem/AERO_HISTORY.rc @@ -0,0 +1,453 @@ +####################################################################### +# Create History List for Output +####################################################################### + +VERSION: 1 +EXPID: gocart +EXPDSC: GOCART2g_diagnostics_at_c360 +EXPSRC: GEOSgcm-v10.16.0 + + +COLLECTIONS: 'inst_aod' +# 'inst_du_ss' +# 'inst_ca' +# 'inst_ni' +# 'inst_su' +# 'inst_du_bin' +# 'inst_ss_bin' +# 'inst_ca_bin' +# 'inst_ni_bin' +# 'inst_su_bin' +# 'inst_2d' +# 'inst_3d' +# 'inst_aod' +# 'tavg_du_ss' +# 'tavg_du_bin' +# 'tavg_2d_rad' +# 'tavg_3d_rad' + :: + +################################################## +# The GRID_LABELS section must be after the main # +# list of COLLECTIONS for scripting purposes. # +################################################## + +GRID_LABELS: PC720x361-DC +:: + + +PC720x361-DC.GRID_TYPE: LatLon +PC720x361-DC.IM_WORLD: 720 +PC720x361-DC.JM_WORLD: 361 +PC720x361-DC.POLE: PC +PC720x361-DC.DATELINE: DC +PC720x361-DC.LM: 72 + +# --------------------- +# Aerosols/Constituents +# --------------------- +# +#### Instantaneous (hourly) output + +# +# 3d aerosols +# + inst_du_ss.format: 'CFIO' , + inst_du_ss.descr: '3d,Hourly,Instantaneous,Model-Level,Aerosol Concentrations', + inst_du_ss.template: '%y4%m2%d2_%h2%n2z.nc4', + inst_du_ss.mode: 'instantaneous', + inst_du_ss.grid_label: PC720x361-DC , + inst_du_ss.splitField: 1, + inst_du_ss.frequency: 120000 , + inst_du_ss.duration: 010000 , + inst_du_ss.ref_time: 000000 , + inst_du_ss.nbits: 10, + inst_du_ss.fields: 'DU' , 'DU' , + 'SS' , 'SS' , + :: + + tavg_du_ss.format: 'CFIO' , + tavg_du_ss.descr: '3d,Hourly,Instantaneous,Model-Level,Aerosol Concentrations', + tavg_du_ss.template: '%y4%m2%d2_%h2%n2z.nc4', + tavg_du_ss.mode: 'time-averaged', + tavg_du_ss.grid_label: PC720x361-DC , + tavg_du_ss.splitField: 1, + tavg_du_ss.frequency: 120000 , + tavg_du_ss.duration: 010000 , + tavg_du_ss.ref_time: 000000 , + tavg_du_ss.nbits: 10, + tavg_du_ss.fields: 'DU' , 'DU' , + 'SS' , 'SS' , + :: + + inst_ca.format: 'CFIO' , + inst_ca.descr: '3d,Hourly,Instantaneous,Model-Level,Aerosol Concentrations', + inst_ca.template: '%y4%m2%d2_%h2%n2z.nc4', + inst_ca.mode: 'instantaneous', + inst_ca.grid_label: PC720x361-DC , + inst_ca.frequency: 120000 , + inst_ca.duration: 010000 , + inst_ca.ref_time: 000000 , + inst_ca.nbits: 10, + inst_ca.fields: 'CAphilicCA.bc' , 'CA.bc' , + 'CAphobicCA.bc' , 'CA.bc' , + 'CAphilicCA.oc' , 'CA.oc' , + 'CAphobicCA.oc' , 'CA.oc' , + :: + + inst_ni.format: 'CFIO' , + inst_ni.descr: '3d,Hourly,Instantaneous,Model-Level,Aerosol Concentrations', + inst_ni.template: '%y4%m2%d2_%h2%n2z.nc4', + inst_ni.mode: 'instantaneous', + inst_ni.grid_label: PC720x361-DC , + inst_ni.frequency: 120000 , + inst_ni.duration: 010000 , + inst_ni.ref_time: 000000 , + inst_ni.nbits: 10, + inst_ni.fields: 'NH3' , 'NI' , + 'NH4a' , 'NI' , + 'NO3an1' , 'NI' , + 'NO3an2' , 'NI' , + 'NO3an3' , 'NI' , + :: + + inst_su.format: 'CFIO' , + inst_su.descr: '3d,Hourly,Instantaneous,Model-Level,Aerosol Concentrations', + inst_su.template: '%y4%m2%d2_%h2%n2z.nc4', + inst_su.mode: 'instantaneous', + inst_su.grid_label: PC720x361-DC , + inst_su.frequency: 120000 , + inst_su.duration: 010000 , + inst_su.ref_time: 000000 , + inst_su.nbits: 10, + inst_su.fields: 'DMS' , 'SU' , + 'SO2' , 'SU' , + 'SO4' , 'SU' , + 'MSA' , 'SU' , + :: +# +# Binned aerosols +# + + inst_du_bin.format: 'CFIO' , + inst_du_bin.descr: '2d,Hourly,Instantaneous' + inst_du_bin.template: '%y4%m2%d2_%h2%n2z.nc4', + inst_du_bin.mode: 'instantaneous' + inst_du_bin.grid_label: PC720x361-DC , + inst_du_bin.splitField: 1, + inst_du_bin.frequency: 010000 , + inst_du_bin.duration: 010000 , + inst_du_bin.ref_time: 000000 , + inst_du_bin.nbits: 10, + inst_du_bin.fields: 'DUEM' , 'DU' , + 'DUSD' , 'DU' , + 'DUDP' , 'DU' , + 'DUWT' , 'DU' , + 'DUSV' , 'DU' , + :: + + tavg_du_bin.format: 'CFIO' , + tavg_du_bin.descr: '2d,Hourly,Instantaneous' + tavg_du_bin.template: '%y4%m2%d2_%h2%n2z.nc4', + tavg_du_bin.mode: 'time-averaged' + tavg_du_bin.grid_label: PC720x361-DC , + tavg_du_bin.splitField: 1, + tavg_du_bin.frequency: 030000 , + tavg_du_bin.duration: 010000 , + tavg_du_bin.ref_time: 000000 , + tavg_du_bin.nbits: 10, + tavg_du_bin.fields: 'DUEM' , 'DU' , + 'DUSD' , 'DU' , + 'DUDP' , 'DU' , + 'DUWT' , 'DU' , + 'DUSV' , 'DU' , + :: + + inst_ss_bin.format: 'CFIO' , + inst_ss_bin.descr: '2d,Hourly,Instantaneous' + inst_ss_bin.template: '%y4%m2%d2_%h2%n2z.nc4', + inst_ss_bin.mode: 'instantaneous' + inst_ss_bin.grid_label: PC720x361-DC , + inst_ss_bin.splitField: 1, + inst_ss_bin.frequency: 060000 , + inst_ss_bin.duration: 010000 , + inst_ss_bin.ref_time: 000000 , + inst_ss_bin.nbits: 10, + inst_ss_bin.fields: 'SSEM' , 'SS' , + 'SSSD' , 'SS' , + 'SSDP' , 'SS' , + 'SSWT' , 'SS' , + 'SSSV' , 'SS' , + :: + + inst_ca_bin.format: 'CFIO' , + inst_ca_bin.descr: '3d,Hourly,Instantaneous,Model-Level' + inst_ca_bin.template: '%y4%m2%d2_%h2%n2z.nc4', + inst_ca_bin.mode: 'instantaneous' + inst_ca_bin.grid_label: PC720x361-DC , + inst_ca_bin.splitField: 1, + inst_ca_bin.frequency: 120000 , + inst_ca_bin.duration: 010000 , + inst_ca_bin.ref_time: 000000 , + inst_ca_bin.nbits: 10, + inst_ca_bin.fields: 'CAEMCA.bc' , 'CA.bc' , + 'CAEMCA.oc' , 'CA.oc' , + 'CASDCA.bc' , 'CA.bc' , + 'CASDCA.oc' , 'CA.oc' , + 'CADPCA.bc' , 'CA.bc' , + 'CADPCA.oc' , 'CA.oc' , + 'CAWTCA.bc' , 'CA.bc' , + 'CAWTCA.oc' , 'CA.oc' , + 'CASVCA.bc' , 'CA.bc' , + 'CASVCA.oc' , 'CA.oc' , + :: + + inst_ni_bin.format: 'CFIO' , + inst_ni_bin.descr: '3d,Hourly,Instantaneous,Model-Level' + inst_ni_bin.template: '%y4%m2%d2_%h2%n2z.nc4', + inst_ni_bin.mode: 'instantaneous', + inst_ni_bin.grid_label: PC720x361-DC , + inst_ni_bin.splitField: 1, + inst_ni_bin.frequency: 120000 , + inst_ni_bin.duration: 010000 , + inst_ni_bin.ref_time: 000000 , + inst_ni_bin.nbits: 10, + inst_ni_bin.fields: 'NIHT' , 'NI' , + 'NISD' , 'NI' , + 'NIDP' , 'NI' , + 'NIWT' , 'NI' , + 'NISV' , 'NI' , + :: + + inst_su_bin.format: 'CFIO' , + inst_su_bin.descr: '3d,Hourly,Instantaneous,Model-Level' + inst_su_bin.template: '%y4%m2%d2_%h2%n2z.nc4', + inst_su_bin.mode: 'instantaneous', + inst_su_bin.grid_label: PC720x361-DC , + inst_su_bin.splitField: 1, + inst_su_bin.frequency: 120000 , + inst_su_bin.duration: 010000 , + inst_su_bin.ref_time: 000000 , + inst_su_bin.nbits: 10, + inst_su_bin.fields: 'SUEM' , 'SU', + 'SUSD' , 'SU', + 'SUDP' , 'SU', + 'SUWT' , 'SU', + 'SUSV' , 'SU', + :: + +# +# Other 2d diagnostics +# + inst_2d.format: 'CFIO' , + inst_2d.descr: '3d,Hourly,Instantaneous' + inst_2d.template: '%y4%m2%d2_%h2%n2z.nc4', + inst_2d.archive: '%c/Y%y4', + inst_2d.mode: 'instantaneous' + inst_2d.frequency: 030000, + inst_2d.duration: 030000, + inst_2d.ref_time: 000000, + inst_2d.grid_label: PC720x361-DC + inst_2d.fields: 'DUSMASS' , 'DU' , + 'DUCMASS' , 'DU' , + 'DUSMASS25' , 'DU' , + 'DUCMASS25' , 'DU' , + 'DUAERIDX' , 'DU' , + 'DUFLUXU' , 'DU' , + 'DUFLUXV' , 'DU' , + 'DUANGSTR' , 'DU' , + 'SSSMASS' , 'SS' , + 'SSCMASS' , 'SS' , + 'SSSMASS25' , 'SS' , + 'SSCMASS25' , 'SS' , + 'SSAERIDX' , 'SS' , + 'SSANGSTR' , 'SS' , + 'SSFLUXU' , 'SS' , + 'SSFLUXV' , 'SS' , + 'CAEMANCA.bc' , 'CA.bc' , + 'CAEMANCA.oc' , 'CA.oc' , + 'CAEMBBCA.bc' , 'CA.bc' , + 'CAEMBBCA.oc' , 'CA.oc' , + 'CAEMBFCA.bc' , 'CA.bc' , + 'CAEMBFCA.oc' , 'CA.oc' , + 'CAEMBGCA.bc' , 'CA.bc' , + 'CAEMBGCA.oc' , 'CA.oc' , + 'CAHYPHILCA.bc' , 'CA.bc' , + 'CAHYPHILCA.oc' , 'CA.oc' , + 'CAPSOACA.bc' , 'CA.bc' , + 'CAPSOACA.oc' , 'CA.oc' , + 'CASMASSCA.bc' , 'CA.bc' , + 'CASMASSCA.oc' , 'CA.oc' , + 'CACMASSCA.bc' , 'CA.bc' , + 'CACMASSCA.oc' , 'CA.oc' , + 'CAANGSTRCA.bc' , 'CA.bc' , + 'CAANGSTRCA.oc' , 'CA.oc' , + 'CAFLUXUCA.bc' , 'CA.bc' , + 'CAFLUXUCA.oc' , 'CA.oc' , + 'CAFLUXVCA.bc' , 'CA.bc' , + 'CAFLUXVCA.oc' , 'CA.oc' , + 'CAAERIDXCA.bc' , 'CA.bc' , + 'CAAERIDXCA.oc' , 'CA.oc' , + 'NIPNO3AQ' , 'NI' , + 'NIPNH4AQ' , 'NI' , + 'NIPNH3AQ' , 'NI' , + 'NH3EM' , 'NI' , + 'NH3DP' , 'NI' , + 'NH3WT' , 'NI' , + 'NH3SV' , 'NI' , + 'NH4SD' , 'NI' , + 'NH4DP' , 'NI' , + 'NH4WT' , 'NI' , + 'NH4SV' , 'NI' , + 'HNO3SMASS' , 'NI' , + 'NH3SMASS' , 'NI' , + 'NH4SMASS' , 'NI' , + 'NISMASS' , 'NI' , + 'NISMASS25' , 'NI' , + 'HNO3CMASS' , 'NI' , + 'NH3CMASS' , 'NI' , + 'NH4CMASS' , 'NI' , + 'NICMASS' , 'NI' , + 'NICMASS25' , 'NI' , + 'NIANGSTR' , 'NI' , + 'NIFLUXU' , 'NI' , + 'NIFLUXV' , 'NI' , + 'SUPSO2' , 'SU' , + 'SUPSO4' , 'SU' , + 'SUPSO4G' , 'SU' , + 'SUPSO4AQ' , 'SU' , + 'SUPSO4WT' , 'SU' , + 'SUPMSA' , 'SU' , + 'SO2SMASS' , 'SU' , + 'SO2CMASS' , 'SU' , + 'SO4SMASS' , 'SU' , + 'SO4CMASS' , 'SU' , + 'DMSSMASS' , 'SU' , + 'DMSCMASS' , 'SU' , + 'MSASMASS' , 'SU' , + 'MSACMASS' , 'SU' , + 'SUANGSTR' , 'SU' , + 'SUFLUXU' , 'SU' , + 'SUFLUXV' , 'SU' , + 'SO4EMAN' , 'SU' , + 'SO2EMAN' , 'SU' , + 'SO2EMBB' , 'SU' , + 'SO2EMVN' , 'SU' , + 'SO2EMVE' , 'SU' , + :: + +# +# 3d diagnostics +# + inst_3d.format: 'CFIO' , + inst_3d.template: '%y4%m2%d2_%h2%n2z.nc4' , + inst_3d.archive: '%c/Y%y4' , + inst_3d.mode: 'instantaneous' + inst_3d.frequency: 060000, + inst_3d.duration: 010000, + inst_3d.ref_time: 000000, + inst_3d.grid_label: PC720x361-DC + inst_3d.fields: 'DUMASS' , 'DU', + 'DUMASS25' , 'DU', + 'DUCONC' , 'DU', + 'SSMASS' , 'SS', + 'SSMASS25' , 'SS', + 'SSCONC' , 'SS', + 'CAMASSCA.bc' , 'CA.bc' , + 'CACONCCA.bc' , 'CA.bc' , + 'CAMASSCA.oc' , 'CA.oc' , + 'CACONCCA.oc' , 'CA.oc' , + 'SO4MASS' , 'SU', + 'SO4SAREA' , 'SU', + 'SO4SNUM' , 'SU', + 'SUCONC' , 'SU', + 'PSO2' , 'SU', + 'PMSA' , 'SU', + 'PSO4' , 'SU', + 'PSO4G' , 'SU', + 'PSO4WET' , 'SU', + 'PSO4AQ' , 'SU', + 'DMS' , 'SU', + 'SO2' , 'SU', + 'SO4' , 'SU', + 'MSA' , 'SU', + :: + + +# +# Radiation-related diagnostics +# + inst_aod.format: 'CFIO' , + inst_aod.template: '%y4%m2%d2_%h2%n2z.nc4' , + inst_aod.archive: '%c/Y%y4' , + inst_aod.mode: 'instantaneous' + inst_aod.frequency: 060000, + inst_aod.duration: 010000, + inst_aod.ref_time: 000000, + inst_aod.grid_label: PC720x361-DC + inst_aod.fields: 'CAEXTTAUCA.bc' , 'CA.bc' , 'AOD_BC', + 'CAEXTTAUCA.oc' , 'CA.oc' , 'AOD_OC', + 'DUEXTTAU' , 'DU' , 'AOD_DU', + 'NIEXTTAU' , 'NI' , 'AOD_NI', + 'SSEXTTAU' , 'SS' , 'AOD_SS', + 'SUEXTTAU' , 'SU' , 'AOD_SU', + 'TOTEXTTAU' , 'GOCART2G' , 'AOD' , + :: + + + tavg_2d_rad.format: 'CFIO' , + tavg_2d_rad.template: '%y4%m2%d2_%h2%n2z.nc4', + tavg_2d_rad.archive: '%c/Y%y4', + tavg_2d_rad.mode: 'time-averaged', + tavg_2d_rad.frequency: 120000, + tavg_2d_rad.duration: 120000, + tavg_2d_rad.ref_time: 000000, + tavg_2d_rad.grid_label: PC720x361-DC + tavg_2d_rad.fields: 'CAEXTTAUCA.bc' , 'CA.bc' , + 'CAEXTTAUCA.oc' , 'CA.oc' , + 'CASCATAUCA.bc' , 'CA.bc' , + 'CASCATAUCA.oc' , 'CA.oc' , + 'DUEXTTAU' , 'DU' , + 'DUSCATAU' , 'DU' , + 'DUEXTT25' , 'DU' , + 'DUSCAT25' , 'DU' , + 'DUEXTTFM' , 'DU' , + 'DUSCATFM' , 'DU' , + 'NIEXTTFM' , 'NI' , + 'NISCATFM' , 'NI' , + 'NIEXTT25' , 'NI' , + 'NISCAT25' , 'NI' , + 'NIEXTTAU' , 'NI' , + 'NISCATAU' , 'NI' , + 'SSEXTTAU' , 'SS' , + 'SSSCATAU' , 'SS' , + 'SSEXTT25' , 'SS' , + 'SSSCAT25' , 'SS' , + 'SSEXTTFM' , 'SS' , + 'SSSCATFM' , 'SS' , + 'SUEXTTAU' , 'SU' , + 'SUSCATAU' , 'SU' , + :: + + tavg_3d_rad.format: 'CFIO' , + tavg_3d_rad.template: '%y4%m2%d2_%h2%n2z.nc4', + tavg_3d_rad.archive: '%c/Y%y4', + tavg_3d_rad.mode: 'time-averaged', + tavg_3d_rad.frequency: 120000, + tavg_3d_rad.duration: 120000, + tavg_3d_rad.ref_time: 000000, + tavg_3d_rad.grid_label: PC720x361-DC + tavg_3d_rad.splitField: 1, + tavg_3d_rad.fields: 'CAEXTCOEFCA.bc' , 'CA.bc' , + 'CAEXTCOEFCA.oc' , 'CA.oc' , + 'CASCACOEFCA.bc' , 'CA.bc' , + 'CASCACOEFCA.oc' , 'CA.oc' , + 'DUEXTCOEF' , 'DU' , + 'DUSCACOEF' , 'DU' , + 'NIEXTCOEF' , 'NI' , + 'NISCACOEF' , 'NI' , + 'SSEXTCOEF' , 'SS' , + 'SSSCACOEF' , 'SS' , + 'SUEXTCOEF' , 'SU' , + 'SUSCACOEF' , 'SU' , + :: diff --git a/parm/chem/AGCM.rc b/parm/chem/AGCM.rc new file mode 100644 index 0000000000..9539b49655 --- /dev/null +++ b/parm/chem/AGCM.rc @@ -0,0 +1,5 @@ +# ------------------------------------------ +# Atmospheric Model Configuration Parameters +# ------------------------------------------ +# +# This file must be present but empty for UFS Aerosols diff --git a/parm/chem/CA2G_instance_CA.bc.rc b/parm/chem/CA2G_instance_CA.bc.rc new file mode 100644 index 0000000000..f6411e2c9d --- /dev/null +++ b/parm/chem/CA2G_instance_CA.bc.rc @@ -0,0 +1,41 @@ +# +# Resource file for Black Carbon parameters. +# + +nbins: 2 + +aerosol_radBands_optics_file: ExtData/optics/opticsBands_BC.v1_3.RRTMG.nc +aerosol_monochromatic_optics_file: ExtData/monochromatic/optics_BC.v1_3.nc + +# Aircraft emission factor: convert input unit to kg C +aircraft_fuel_emission_factor: 1.0000 + +# Heights [m] of LTO, CDS and CRS aviation emissions layers +aviation_vertical_layers: 0.0 100.0 9.0e3 10.0e3 + +# Initially hydrophobic portion +hydrophobic_fraction: 0.8 + +# Scavenging efficiency per bin [km-1] (NOT USED UNLESS RAS IS CALLED) +fscav: 0.0 0.4 + +# Dry particle density [kg m-3] +particle_density: 1800 1800 + +# Molecular weight of species [kg mole-1] +molecular_weight: 0.18 0.18 + +# Number of particles per kg mass +fnum: 1.50e19 1.50e19 + +# Number median radius [um] +particle_radius_microns: 0.35 0.35 + +rhFlag: 0 + +# Sigma of lognormal number distribution +sigma: 2.0 2.0 + +pressure_lid_in_hPa: 0.01 + +point_emissions_srcfilen: /dev/null diff --git a/parm/chem/CA2G_instance_CA.br.rc b/parm/chem/CA2G_instance_CA.br.rc new file mode 100644 index 0000000000..41360831f3 --- /dev/null +++ b/parm/chem/CA2G_instance_CA.br.rc @@ -0,0 +1,48 @@ +# +# Resource file for BR parameters. +# + +aerosol_radBands_optics_file: ExtData/optics/opticsBands_BRC.v1_5.RRTMG.nc +aerosol_monochromatic_optics_file: ExtData/monochromatic/optics_BRC.v1_5.nc + +# Aircraft emission factor: convert input unit to kg C +aircraft_fuel_emission_factor: 1.0000 + +# Heights [m] of LTO, CDS and CRS aviation emissions layers +aviation_vertical_layers: 0.0 100.0 9.0e3 10.0e3 + +# Fraction of biogenic VOCs emissions for SOA production +monoterpenes_emission_fraction: 0.0 +isoprene_emission_fraction: 0.0 + +# Ratio of POM/BRC -> convert source masses from carbon to POM +pom_ca_ratio: 1.8 + +# Initially hydrophobic portion +hydrophobic_fraction: 0.5 + +# Scavenging efficiency per bin [km-1] (NOT USED UNLESS RAS IS CALLED) +fscav: 0.0 0.4 + +# particle radius +particle_radius_microns: 0.35 0.35 + +rhFlag: 0 + +# Dry particle density [kg m-3] +particle_density: 1800 1800 + +# Molecular weight of species [kg mole-1] +molecular_weight: 0.18 0.18 + +# Number of particles per kg mass +fnum: 9.76e17 9.76e17 + +# Sigma of lognormal number distribution +sigma: 2.20 2.20 + +nbins: 2 + +pressure_lid_in_hPa: 0.01 + +point_emissions_srcfilen: /dev/null diff --git a/parm/chem/CA2G_instance_CA.oc.rc b/parm/chem/CA2G_instance_CA.oc.rc new file mode 100644 index 0000000000..6e3f5ef978 --- /dev/null +++ b/parm/chem/CA2G_instance_CA.oc.rc @@ -0,0 +1,48 @@ +# +# Resource file for Organic Carbon parameters. +# + +aerosol_radBands_optics_file: ExtData/optics/opticsBands_OC.v1_3.RRTMG.nc +aerosol_monochromatic_optics_file: ExtData/monochromatic/optics_OC.v1_3.nc + +# Aircraft emission factor: convert input unit to kg C +aircraft_fuel_emission_factor: 1.0000 + +# Heights [m] of LTO, CDS and CRS aviation emissions layers +aviation_vertical_layers: 0.0 100.0 9.0e3 10.0e3 + +# Fraction of biogenic VOCs emissions for SOA production +monoterpenes_emission_fraction: 0.05 +isoprene_emission_fraction: 0.03 + +# Ratio of POM/OC -> convert source masses from carbon to POM +pom_ca_ratio: 1.8 + +# particle radius +particle_radius_microns: 0.35 0.35 + +rhFlag: 0 + +# Initially hydrophobic portion +hydrophobic_fraction: 0.5 + +# Scavenging efficiency per bin [km-1] (NOT USED UNLESS RAS IS CALLED) +fscav: 0.0 0.4 + +# Dry particle density [kg m-3] +particle_density: 1800 1800 + +# Molecular weight of species [kg mole-1] +molecular_weight: 0.18 0.18 + +# Number of particles per kg mass +fnum: 9.76e17 9.76e17 + +# Sigma of lognormal number distribution +sigma: 2.20 2.20 + +pressure_lid_in_hPa: 0.01 + +nbins: 2 + +point_emissions_srcfilen: /dev/null diff --git a/parm/chem/CAP.rc b/parm/chem/CAP.rc new file mode 100644 index 0000000000..d8b0352e55 --- /dev/null +++ b/parm/chem/CAP.rc @@ -0,0 +1,79 @@ +MAPLROOT_COMPNAME: AERO + ROOT_NAME: AERO +ROOT_CF: AERO.rc +HIST_CF: AERO_HISTORY.rc +EXTDATA_CF: AERO_ExtData.rc + +REPORT_THROUGHPUT: .false. + +USE_SHMEM: 0 + +MAPL_ENABLE_TIMERS: NO +MAPL_ENABLE_MEMUTILS: NO +PRINTSPEC: 0 # (0: OFF, 1: IMPORT & EXPORT, 2: IMPORT, 3: EXPORT) + + +# Meteorological fields imported from atmospheric model +# ----------------------------------------------------- +CAP_IMPORTS: + FROCEAN + FRACI + FRSNOW + LWI + U10M + V10M + USTAR + TS + DZ + FRLAKE + AREA + ZPBL + SH + Z0H + CN_PRCP + NCN_PRCP + AIRDENS + DELP + T + RH2 + ZLE + PLE + PFL_LSAN + PFI_LSAN + U + V + WET1 + SLC + FCLD +:: + + +# Prognostic Tracers Table +# GOCARTname,GOCARTcomp. AtmTracerName +#--------------------------------------- +CAP_EXPORTS: + SS,SS seas* + DU,DU dust* + DMS,SU dms + MSA,SU msa + SO2,SU so2 + SO4,SU so4 + CAphobicCA.bc,CA.bc bc1 + CAphilicCA.bc,CA.bc bc2 + CAphobicCA.oc,CA.oc oc1 + CAphilicCA.oc,CA.oc oc2 + NH3,NI nh3 + NH4a,NI nh4a + NO3an1,NI no3an1 + NO3an2,NI no3an2 + NO3an3,NI no3an3 +:: + + +# Diagnostic Tracers Table (only PM10 & PM25 available) +# InternalName AtmTracerName +#--------------------------------------- +CAP_DIAGNOSTICS: + PM10 pm10 + PM25 pm25 +:: diff --git a/parm/chem/DU2G_instance_DU.rc b/parm/chem/DU2G_instance_DU.rc new file mode 100644 index 0000000000..1afed80ce1 --- /dev/null +++ b/parm/chem/DU2G_instance_DU.rc @@ -0,0 +1,46 @@ +# +# Resource file Dust parameters. +# + +aerosol_radBands_optics_file: ExtData/optics/opticsBands_DU.v15_3.RRTMG.nc +aerosol_monochromatic_optics_file: ExtData/monochromatic/optics_DU.v15_3.nc + +particle_radius_microns: 0.73 1.4 2.4 4.5 8.0 + +radius_lower: 0.1 1.0 1.8 3.0 6.0 + +radius_upper: 1.0 1.8 3.0 6.0 10.0 + +source_fraction: 1.0 1.0 1.0 1.0 1.0 + +# units [kg/m-3] +particle_density: 2500. 2650. 2650. 2650. 2650. + +# Resolution dependent tuning constant for emissions (a,b,c,d,e,f) (Ginoux, K14) +Ch_DU: 0.2 0.2 0.07 0.07 0.07 0.056 + +# Scavenging efficiency per bin [km-1] +fscav: 0.2 0.2 0.2 0.2 0.2 # + +# Molecular weight of species [kg mole-1] +molecular_weight: 0.1 0.1 0.1 0.1 0.1 + +# Number of particles per kg mass +fnum: 2.45e14 3.28e13 6.52e12 9.89e11 1.76e11 + +rhFlag: 0 + +# Maring settling velocity correction +maringFlag: .true. + +nbins: 5 + +pressure_lid_in_hPa: 0.01 + +# Emissions methods +emission_scheme: fengsha # choose among: fengsha, ginoux, k14 + +# FENGSHA settings +alpha: 2.0 +gamma: 0.8 +vertical_to_horizontal_flux_ratio_limit: 2.e-04 diff --git a/parm/chem/ExtData.gbbepx b/parm/chem/ExtData.gbbepx new file mode 100644 index 0000000000..ab06930bd5 --- /dev/null +++ b/parm/chem/ExtData.gbbepx @@ -0,0 +1,8 @@ +#====== BIOMASS BURNING EMISSIONS ======================================= + +# GBBEPx +#-------------------------------------------------------------------------------------------------------------------------------- +SU_BIOMASS NA N Y %y4-%m2-%d2t12:00:00 none none biomass ExtData/nexus/GBBEPx/GBBEPx_all01GRID.emissions_v003_%y4%m2%d2.nc +OC_BIOMASS NA N Y %y4-%m2-%d2t12:00:00 none none biomass ExtData/nexus/GBBEPx/GBBEPx_all01GRID.emissions_v003_%y4%m2%d2.nc +BC_BIOMASS NA N Y %y4-%m2-%d2t12:00:00 none none biomass ExtData/nexus/GBBEPx/GBBEPx_all01GRID.emissions_v003_%y4%m2%d2.nc +EMI_NH3_BB NA N Y %y4-%m2-%d2t12:00:00 none none biomass ExtData/nexus/GBBEPx/GBBEPx_all01GRID.emissions_v003_%y4%m2%d2.nc diff --git a/parm/chem/ExtData.none b/parm/chem/ExtData.none new file mode 100644 index 0000000000..15ad023eb8 --- /dev/null +++ b/parm/chem/ExtData.none @@ -0,0 +1,8 @@ +#====== BIOMASS BURNING EMISSIONS ======================================= + +# NONE +#-------------------------------------------------------------------------------------------------------------------------------- +SU_BIOMASS NA N Y %y4-%m2-%d2t12:00:00 none none biomass /dev/null +OC_BIOMASS NA N Y %y4-%m2-%d2t12:00:00 none none biomass /dev/null +BC_BIOMASS NA N Y %y4-%m2-%d2t12:00:00 none none biomass /dev/null +EMI_NH3_BB NA N Y %y4-%m2-%d2t12:00:00 none none biomass /dev/null diff --git a/parm/chem/ExtData.other b/parm/chem/ExtData.other new file mode 100644 index 0000000000..56af1c7d9a --- /dev/null +++ b/parm/chem/ExtData.other @@ -0,0 +1,150 @@ +# -------------|-------|-------|--------|----------------------|--------|--------|-------------|----------| +# Import | | | Regrid | Refresh | OffSet | Scale | Variable On | File | +# Name | Units | Clim | Method | Time Template | Factor | Factor | File | Template | +# -------------|-------|-------|--------|----------------------|--------|--------|-------------|----------| + +#====== Atmospheric Parameters ======================================= +TROPP 'Pa' Y N - 0.0 1.0 TROPP /dev/null:10000. + +#====== Dust Imports ================================================= +# Ginoux input files +DU_SRC NA N Y - none none du_src ExtData/Dust/gocart.dust_source.v5a.x1152_y721.nc + +# FENGSHA input files. Note: regridding should be N or E +DU_CLAY '1' Y E - none none clayfrac ExtData/Dust/FENGSHA_SOILGRIDS2017_GEFSv12_v1.0.nc +DU_SAND '1' Y E - none none sandfrac ExtData/Dust/FENGSHA_SOILGRIDS2017_GEFSv12_v1.0.nc +DU_SILT '1' Y E - none none siltfrac /dev/null +DU_SSM '1' Y E %y4-%m2-%d2T12:00:00 none none ssm ExtData/Dust/FENGSHA_SOILGRIDS2017_GEFSv12_v1.0.nc +DU_RDRAG '1' Y E - none none drag_part ExtData/Dust/FENGSHA_SOILGRIDS2017_GEFSv12_v1.0.nc +DU_UTHRES '1' Y E - none none uthres ExtData/Dust/FENGSHA_SOILGRIDS2017_GEFSv12_v1.0.nc + +#====== Sulfate Sources ================================================= +# Anthropogenic (BF & FF) emissions -- allowed to input as two layers +SU_ANTHROL1 NA N Y %y4-%m2-%d2t12:00:00 none none SO2 ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc +SU_ANTHROL2 NA N Y %y4-%m2-%d2t12:00:00 none none SO2_elev ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc + +# Ship emissions +SU_SHIPSO2 NA N Y %y4-%m2-%d2t12:00:00 none none SO2_ship ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc +SU_SHIPSO4 NA N Y %y4-%m2-%d2t12:00:00 none none SO4_ship ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc + +# Aircraft fuel consumption +SU_AIRCRAFT NA Y Y %y4-%m2-%d2t12:00:00 none none none /dev/null + +# DMS concentration +SU_DMSO NA Y Y %y4-%m2-%d2t12:00:00 none none conc ExtData/MERRA2/sfc/DMSclim_sfcconcentration.x360_y181_t12.Lana2011.nc4 + +# Aviation emissions during the three phases of flight +SU_AVIATION_LTO NA Y Y %y4-%m2-%d2t12:00:00 none none so2_aviation ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_so2.aviation_lto.x3600_y1800_t12.2010.nc4 +SU_AVIATION_CDS NA Y Y %y4-%m2-%d2t12:00:00 none none so2_aviation ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_so2.aviation_cds.x3600_y1800_t12.2010.nc4 +SU_AVIATION_CRS NA Y Y %y4-%m2-%d2t12:00:00 none none so2_aviation ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_so2.aviation_crs.x3600_y1800_t12.2010.nc4 + +# H2O2, OH and NO3 mixing ratios +# -------------------------------------------------------------- +# If using 64 levels please replace this section with the correct values (ie replace 127 with 64) + +SU_H2O2 NA N Y %y4-%m2-%d2t12:00:00 none none h2o2 ExtData/PIESA/L127/A2_ACCMIP_gmic_MERRA_oh_h2o2_no3.x144_y91_z127_t14.%y4.nc +SU_OH NA N Y %y4-%m2-%d2t12:00:00 none none oh ExtData/PIESA/L127/A2_ACCMIP_gmic_MERRA_oh_h2o2_no3.x144_y91_z127_t14.%y4.nc +SU_NO3 NA N Y %y4-%m2-%d2t12:00:00 none none no3 ExtData/PIESA/L127/A2_ACCMIP_gmic_MERRA_oh_h2o2_no3.x144_y91_z127_t14.%y4.nc +#--------------------------------------------------------------- + +# Production of SO2 from OCS oxidation +pSO2_OCS NA Y Y %y4-%m2-%d2t12:00:00 none none biofuel /dev/null + +#SU_regionMask NA N v - none none REGION_MASK /scratch1/NCEPDEV/nems/Raffaele.Montuoro/data/NASA/ExtData/PIESA/sfc/ARCTAS.region_mask.x540_y361.2008.nc + +#=========== Carbonaceous aerosol sources =========================================== +# ORGANIC CARBON +# --------------- + +# # VOCs - OFFLINE MEGAN BIOG +OC_ISOPRENE NA N Y %y4-%m2-%d2t12:00:00 none none isoprene ExtData/nexus/MEGAN_OFFLINE_BVOC/v2019-10/%y4/MEGAN.OFFLINE.BIOVOC.%y4.emis.%y4%m2%d2.nc +OC_LIMO NA N Y %y4-%m2-%d2t12:00:00 none none limo ExtData/nexus/MEGAN_OFFLINE_BVOC/v2019-10/%y4/MEGAN.OFFLINE.BIOVOC.%y4.emis.%y4%m2%d2.nc +OC_MTPA NA N Y %y4-%m2-%d2t12:00:00 none none mtpa ExtData/nexus/MEGAN_OFFLINE_BVOC/v2019-10/%y4/MEGAN.OFFLINE.BIOVOC.%y4.emis.%y4%m2%d2.nc +OC_MTPO NA N Y %y4-%m2-%d2t12:00:00 none none mtpo ExtData/nexus/MEGAN_OFFLINE_BVOC/v2019-10/%y4/MEGAN.OFFLINE.BIOVOC.%y4.emis.%y4%m2%d2.nc + +# Biofuel Source -- Included in AeroCom anthropogenic emissions +OC_BIOFUEL NA Y Y %y4-%m2-%d2t12:00:00 none none biofuel /dev/null + +# Anthropogenic (BF & FF) emissions -- allowed to input as two layers +OC_ANTEOC1 NA N Y %y4-%m2-%d2t12:00:00 none none OC ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc +OC_ANTEOC2 NA N Y %y4-%m2-%d2t12:00:00 none none OC_elev ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc + +# EDGAR based ship emissions +OC_SHIP NA N Y %y4-%m2-%d2t12:00:00 none none OC_ship ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc + +# Aircraft fuel consumption +OC_AIRCRAFT NA N Y %y4-%m2-%d2t12:00:00 none none oc_aviation /dev/null + +# Aviation emissions during the three phases of flight +OC_AVIATION_LTO NA Y Y %y4-%m2-%d2t12:00:00 none none oc_aviation ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_oc.aviation_lto.x3600_y1800_t12.2010.nc4 +OC_AVIATION_CDS NA Y Y %y4-%m2-%d2t12:00:00 none none oc_aviation ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_oc.aviation_cds.x3600_y1800_t12.2010.nc4 +OC_AVIATION_CRS NA Y Y %y4-%m2-%d2t12:00:00 none none oc_aviation ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_oc.aviation_crs.x3600_y1800_t12.2010.nc4 + +# SOA production +pSOA_ANTHRO_VOC NA Y Y %y4-%m2-%d2t12:00:00 none none biofuel /dev/null + +#============================================================================================================ +# BLACK CARBON +# ------------ + +# Biofuel Source -- Included in AeroCom anthropogenic emissions +BC_BIOFUEL NA Y Y %y4-%m2-%d2t12:00:00 none none biofuel /dev/null + +# Anthropogenic (BF & FF) emissions -- allowed to input as two layers +BC_ANTEBC1 NA N Y %y4-%m2-%d2t12:00:00 none none BC ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc +BC_ANTEBC2 NA N Y %y4-%m2-%d2t12:00:00 none none BC_elev ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc + +# EDGAR based ship emissions +BC_SHIP NA N Y %y4-%m2-%d2t12:00:00 none none BC_ship ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc + +# Aircraft fuel consumption +BC_AIRCRAFT NA N Y %y4-%m2-%d2t12:00:00 none none bc_aviation /dev/null + +# Aviation emissions during the LTO, SDC and CRS phases of flight +BC_AVIATION_LTO NA Y Y %y4-%m2-%d2t12:00:00 none none bc_aviation ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_bc.aviation_lto.x3600_y1800_t12.2010.nc4 +BC_AVIATION_CDS NA Y Y %y4-%m2-%d2t12:00:00 none none bc_aviation ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_bc.aviation_cds.x3600_y1800_t12.2010.nc4 +BC_AVIATION_CRS NA Y Y %y4-%m2-%d2t12:00:00 none none bc_aviation ExtData/PIESA/sfc/HTAP/v2.2/htap-v2.2.emis_bc.aviation_crs.x3600_y1800_t12.2010.nc4 + +#============================================================================================================ +# BROWN CARBON +# ------------ +# Biomass burning -- QFED-v2.x +BRC_BIOMASS NA N Y %y4-%m2-%d2t12:00:00 none none biomass /dev/null + +# Terpene emission +BRC_TERPENE NA Y Y %y4-%m2-%d2t12:00:00 none none terpene /dev/null + +# Biofuel Source -- Included in AeroCom anthropogenic emissions +BRC_BIOFUEL NA Y Y %y4-%m2-%d2t12:00:00 none none biofuel /dev/null + +# Anthropogenic (BF & FF) emissions -- allowed to input as two layers +BRC_ANTEBRC1 NA Y Y %y4-%m2-%d2t12:00:00 none none anteoc1 /dev/null +BRC_ANTEBRC2 NA Y Y %y4-%m2-%d2t12:00:00 none none anteoc2 /dev/null + +# EDGAR based ship emissions +BRC_SHIP NA Y Y %y4-%m2-%d2t12:00:00 none none oc_ship /dev/null + +# Aircraft fuel consumption +BRC_AIRCRAFT NA N Y %y4-%m2-%d2t12:00:00 none none none /dev/null + +# Aviation emissions during the three phases of flight +BRC_AVIATION_LTO NA Y Y %y4-%m2-%d2t12:00:00 none none oc_aviation /dev/null +BRC_AVIATION_CDS NA Y Y %y4-%m2-%d2t12:00:00 none none oc_aviation /dev/null +BRC_AVIATION_CRS NA Y Y %y4-%m2-%d2t12:00:00 none none oc_aviation /dev/null + +# SOA production +pSOA_BIOB_VOC NA Y Y %y4-%m2-%d2t12:00:00 none none biofuel /dev/null + +# ======= Nitrate Sources ======== +EMI_NH3_AG 'kg m-2 s-1' N Y %y4-%m2-%d2T12:00:00 none none NH3_ag ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc +EMI_NH3_EN 'kg m-2 s-1' N Y %y4-%m2-%d2T12:00:00 none none NH3_en /dev/null +EMI_NH3_IN 'kg m-2 s-1' N Y %y4-%m2-%d2T12:00:00 none none NH3_in ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc +EMI_NH3_RE 'kg m-2 s-1' N Y %y4-%m2-%d2T12:00:00 none none NH3_re ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc +EMI_NH3_TR 'kg m-2 s-1' N Y %y4-%m2-%d2T12:00:00 none none NH3_tr ExtData/nexus/CEDS/v2019/%y4/CEDS.2019.emis.%y4%m2%d2.nc +EMI_NH3_OC 'kg m-2 s-1' Y Y %y4-%m2-%d2T12:00:00 none none emiss_ocn ExtData/PIESA/sfc/GEIA.emis_NH3.ocean.x576_y361.t12.20080715_12z.nc4 + +# -------------------------------------------------------------- +# If using 64 levels please replace this section with the correct values (ie replace 127 with 64) +NITRATE_HNO3 'mol mol-1' Y N %y4-%m2-%d2T12:00:00 none 0.20 hno3 ExtData/PIESA/L127/GMI.vmr_HNO3.x144_y91.t12.2006.nc4 +# -------------------------------------------------------------- +NI_regionMask NA Y V - none none REGION_MASK ExtData/PIESA/sfc/ARCTAS.region_mask.x540_y361.2008.nc diff --git a/parm/chem/ExtData.qfed b/parm/chem/ExtData.qfed new file mode 100644 index 0000000000..86ab3c86cc --- /dev/null +++ b/parm/chem/ExtData.qfed @@ -0,0 +1,8 @@ +#====== BIOMASS BURNING EMISSIONS ======================================= + +# QFED +#-------------------------------------------------------------------------------------------------------------------------------- +SU_BIOMASS NA N Y %y4-%m2-%d2t12:00:00 none none biomass ExtData/nexus/QFED/%y4/%m2/qfed2.emis_so2.006.%y4%m2%d2.nc4 +OC_BIOMASS NA N Y %y4-%m2-%d2t12:00:00 none none biomass ExtData/nexus/QFED/%y4/%m2/qfed2.emis_oc.006.%y4%m2%d2.nc4 +BC_BIOMASS NA N Y %y4-%m2-%d2t12:00:00 none none biomass ExtData/nexus/QFED/%y4/%m2/qfed2.emis_bc.006.%y4%m2%d2.nc4 +EMI_NH3_BB NA N Y %y4-%m2-%d2t12:00:00 none none biomass ExtData/nexus/QFED/%y4/%m2/qfed2.emis_nh3.006.%y4%m2%d2.nc4 diff --git a/parm/chem/GOCART2G_GridComp.rc b/parm/chem/GOCART2G_GridComp.rc new file mode 100644 index 0000000000..5ea4fa45df --- /dev/null +++ b/parm/chem/GOCART2G_GridComp.rc @@ -0,0 +1,41 @@ +# +# !RESOURCE: GOCART2G_GridComp.rc --- GOCART2G resource file +# +# DESCRIPTION: +# The GOCART2G resource file is used to control basic +# properties of the GOCART2G Grid Components. Instances are +# defined here. Default is the data component. +# +# Only the FIRST entry in the ACTIVE_INSTANCE_XX is given as +# the AERO_PROVIDER. +# +# !REVISION HISTORY: +# +# 11Oct2019 E.Sherman GOCART2G resource file has been created +#-------------------------------------------------------------------- + + + # &Label Active Constituents + +# Include the constituent in the simulation? +# ---------------------------------------------------- +ACTIVE_INSTANCES_DU: DU # DU.data +PASSIVE_INSTANCES_DU: + +ACTIVE_INSTANCES_SS: SS # SS.data +PASSIVE_INSTANCES_SS: + +ACTIVE_INSTANCES_SU: SU # SU.data +PASSIVE_INSTANCES_SU: + +ACTIVE_INSTANCES_CA: CA.oc CA.bc # CA.oc.data CA.bc.data +PASSIVE_INSTANCES_CA: + +ACTIVE_INSTANCES_NI: NI # NI.data +PASSIVE_INSTANCES_NI: + +# Set optics parameters +# --------------------- +aerosol_monochromatic_optics_wavelength_in_nm_from_LUT: 470 550 670 870 +wavelengths_for_profile_aop_in_nm: 470 550 670 870 # must be included in LUT +wavelengths_for_vertically_integrated_aop_in_nm: 470 550 670 870 # must be included in LUT diff --git a/parm/chem/NI2G_instance_NI.rc b/parm/chem/NI2G_instance_NI.rc new file mode 100644 index 0000000000..73db601073 --- /dev/null +++ b/parm/chem/NI2G_instance_NI.rc @@ -0,0 +1,33 @@ +# +# Resource file Nitrate parameters. +# + +nbins: 5 + +aerosol_radBands_optics_file: ExtData/optics/opticsBands_NI.v2_5.RRTMG.nc +aerosol_monochromatic_optics_file: ExtData/monochromatic/optics_NI.v2_5.nc + +# Scavenging efficiency per bin [km-1] +fscav: 0.0 0.4 0.4 0.4 0.4 + +# Dry particle radius [um], used for settling +particle_radius_microns: 0.0 0.2695 0.2695 2.1 7.57 + +# Dry particle density [kg m-3] +particle_density: 1000 1769 1725 2200 2650 + +# Molecular weight of species [kg mole-1] +molecular_weight: 0.18 0.18 0.18 0.18 0.18 + +# Number of particles per kg mass +fnum: 1.50e19 1.50e19 1.50e19 1.50e19 1.50e19 + +# Number median radius [um] +particle_radius_number: 0.0118 0.0118 0.0118 0.0118 0.0118 + +# Sigma of lognormal number distribution +sigma: 2.0 2.0 2.0 2.0 2.0 + +pressure_lid_in_hPa: 0.01 + +rhFlag: 0 diff --git a/parm/chem/SS2G_instance_SS.rc b/parm/chem/SS2G_instance_SS.rc new file mode 100644 index 0000000000..d8faa3efa8 --- /dev/null +++ b/parm/chem/SS2G_instance_SS.rc @@ -0,0 +1,43 @@ +# +# Resource file Sea Salt parameters +# + +aerosol_radBands_optics_file: ExtData/optics/opticsBands_SS.v3_3.RRTMG.nc +aerosol_monochromatic_optics_file: ExtData/monochromatic/optics_SS.v3_3.nc + +particle_radius_microns: 0.079 0.316 1.119 2.818 7.772 + +radius_lower: 0.03 0.1 0.5 1.5 5.0 + +radius_upper: 0.1 0.5 1.5 5.0 10.0 + +particle_density: 2200. 2200. 2200. 2200. 2200. + +# Scavenging efficiency per bin [km-1] +fscav: 0.4 0.4 0.4 0.4 0.4 + +# Emissions methods and scaling +emission_scheme: 3 # 1 for Gong 2003, 2 for ... +emission_scale: 1.0 1.0 1.0 1.0 1.0 1.0 # a global scaling factor for C96 +sstEmisFlag: 2 # Apply a correction to emissions based on SST (see code) +hoppelFlag: .false. # Apply Hoppel correction (set non-zero, see Fan and Toon 2011) +weibullFlag: .false. # Apply Weibull distribution (set non-zero, see Fan and Toon 2011) + +# Method of apply relative humidity to particle radius +rhFlag: 2 # RH swelling of Seasalt (1 for Fitzgerald 1975, + # 2 for Gerber 1985 method) + +# Molecular weight of species [kg mole-1] +molecular_weight: 0.058 0.058 0.058 0.058 0.058 + +# Number of particles per kg mass +fnum: 3.017e17 1.085e16 1.207e14 9.391e12 2.922e11 + +# Number median radius [um] +particle_radius_number: 0.066 0.176 0.885 2.061 6.901 + +nbins: 5 + +pressure_lid_in_hPa: 0.01 + + diff --git a/parm/chem/SU2G_instance_SU.rc b/parm/chem/SU2G_instance_SU.rc new file mode 100644 index 0000000000..547be88fa7 --- /dev/null +++ b/parm/chem/SU2G_instance_SU.rc @@ -0,0 +1,53 @@ +# +# Resource file for Sulfer parameters. +# + +aerosol_radBands_optics_file: ExtData/optics/opticsBands_SU.v1_3.RRTMG.nc +aerosol_monochromatic_optics_file: ExtData/monochromatic/optics_SU.v1_3.nc + +nbins: 4 + +# Volcanic pointwise sources +volcano_srcfilen: ExtData/volcanic/so2_volcanic_emissions_Carns.%y4%m2%d2.rc + +# Heights [m] of LTO, CDS and CRS aviation emissions layers +aviation_vertical_layers: 0.0 100.0 9.0e3 10.0e3 + +# Fraction of anthropogenic emissions that are SO4 +so4_anthropogenic_fraction: 0.03 + +# Aircraft emission factor: go from kg fuel to kg SO2 +aircraft_fuel_emission_factor: 0.0008 + +# Scavenging efficiency per bin [km-1] (NOT USED UNLESS RAS IS CALLED) +fscav: 0.0 0.0 0.4 0.4 + +# Dry particle radius [um], used for settling +particle_radius_microns: 0.0 0.0 0.35 0.0 + +# Type of settling to use (see Chem_SettlingMod) +rhFlag: 4 + +# Dry particle density [kg m-3] +particle_density: -1 -1 1700 -1 + +pressure_lid_in_hPa: 0.01 + +# Molecular weight of species [kg mole-1] +molecular_weight: -1 -1 0.132 -1 + +# Number of particles per kg mass +fnum: -1 -1 9.01e16 -1 + +# Number median radius [um] +particle_radius_number: -1 -1 0.0695 -1 + +# Sigma of lognormal number distribution +sigma: -1 -1 2.03 -1 + +# OH H2O2 NO3 from GMI Combined Stratosphere Troposphere (Lower case yes to enable) +# ------------------------------------------------------------------------------------- +export_H2O2: no +using_GMI_OH: .false. +using_GMI_NO3: .false. +using_GMI_H2O2: .false. diff --git a/parm/config/config.aero b/parm/config/config.aero index e69de29bb2..4049219ae9 100644 --- a/parm/config/config.aero +++ b/parm/config/config.aero @@ -0,0 +1,28 @@ +#!/bin/ksh -x +# +# UFS-Aerosols settings +# +# Directory containing GOCART configuration files. Defaults to parm/chem if unset. +AERO_CONFIG_DIR=$HOMEgfs/parm/chem +# +# Path to the input data tree +case $machine in + "WCOSS_DELL_P3") AERO_INPUTS_DIR="/gpfs/dell2/emc/modeling/noscrub/Walter.Kolczynski/global-workflow/gocart_emissions" ;; + "HERA") AERO_INPUTS_DIR="/scratch1/NCEPDEV/nems/Raffaele.Montuoro/data/NASA/ExtData";; + "ORION") AERO_INPUTS_DIR="/work2/noaa/global/wkolczyn/noscrub/global-workflow/gocart_emissions";; + *) + echo "FATAL ERROR: Machine $machine unsupported for aerosols" + exit 2 + ;; +esac +# +# Biomass burning emission dataset. Choose from: GBBEPx, QFED, NONE (default) +AERO_EMIS_FIRE=QFED +# +# Aerosol convective scavenging factors (list of string array elements) +# Element syntax: ':'. Use = * to set default factor for all aerosol tracers +# Scavenging factors are set to 0 (no scavenging) if unset +aero_conv_scav_factors="'*:0.4', 'so2:0.0', 'msa:0.0', 'dms:0.0', 'nh3:0.0', 'so4:0.5', 'bc1:0.6', 'bc2:0.6', 'dust1:0.7', 'dust2:0.7', 'dust3:0.7', 'dust4:0.7', 'dust5:0.7'" +# +# Number of diagnostic aerosol tracers (default: 0) +aero_diag_tracers=2 diff --git a/parm/config/config.base.emc.dyn b/parm/config/config.base.emc.dyn deleted file mode 120000 index 6e9cfcec1a..0000000000 --- a/parm/config/config.base.emc.dyn +++ /dev/null @@ -1 +0,0 @@ -config.base.emc.dyn_hera \ No newline at end of file diff --git a/parm/config/config.base.emc.dyn b/parm/config/config.base.emc.dyn new file mode 100755 index 0000000000..bdfc112336 --- /dev/null +++ b/parm/config/config.base.emc.dyn @@ -0,0 +1,366 @@ +#!/bin/ksh -x + +########## config.base ########## +# Common to all steps + +echo "BEGIN: config.base" + +# Machine environment +export machine="@MACHINE@" + +# EMC parallel or NCO production +export RUN_ENVIR="emc" + +# Account, queue, etc. +export ACCOUNT="@ACCOUNT@" +export QUEUE="@QUEUE@" +export QUEUE_SERVICE="@QUEUE_SERVICE@" +export PARTITION_BATCH="@PARTITION_BATCH@" + +# Project to use in mass store: +HPSS_PROJECT=emc-global + +# Directories relative to installation areas: +export HOMEgfs=@HOMEgfs@ +export PARMgfs=$HOMEgfs/parm +export FIXgfs=$HOMEgfs/fix +export USHgfs=$HOMEgfs/ush +export UTILgfs=$HOMEgfs/util +export EXECgfs=$HOMEgfs/exec +export SCRgfs=$HOMEgfs/scripts + +export FIXcice=$HOMEgfs/fix/fix_cice +export FIXmom=$HOMEgfs/fix/fix_mom6 +export FIXreg2grb2=$HOMEgfs/fix/fix_reg2grb2 + +######################################################################## + +# GLOBAL static environment parameters +export NWPROD="@NWPROD@" +export COMROOT="@COMROOT@" +export DMPDIR="@DMPDIR@" +export RTMFIX=$CRTM_FIX + +# USER specific paths +export HOMEDIR="@HOMEDIR@" +export STMP="@STMP@" +export PTMP="@PTMP@" +export NOSCRUB="@NOSCRUB@" + +# Base directories for various builds +export BASE_GIT="@BASE_GIT@" + +# Toggle to turn on/off GFS downstream processing. +export DO_BUFRSND="NO" # BUFR sounding products +export DO_GEMPAK="NO" # GEMPAK products +export DO_AWIPS="NO" # AWIPS products +export WAFSF="NO" # WAFS products +export DO_VRFY="YES" # VRFY step + +# NO for retrospective parallel; YES for real-time parallel +# arch.sh uses REALTIME for MOS. Need to set REALTIME=YES +# if want MOS written to HPSS. Should update arch.sh to +# use RUNMOS flag (currently in config.vrfy) +export REALTIME="YES" + +# Experiment mode (cycled or free-forecast) +export MODE="@MODE@" # cycled/free + +#################################################### +# DO NOT ADD MACHINE DEPENDENT STUFF BELOW THIS LINE +# IF YOU HAVE TO MAKE MACHINE SPECIFIC CHANGES BELOW +# FEEL FREE TO MOVE THEM ABOVE THIS LINE TO KEEP IT +# CLEAR +#################################################### +# Build paths relative to $HOMEgfs +export FIXgsi="$HOMEgfs/fix/fix_gsi" +export HOMEfv3gfs="$HOMEgfs/sorc/fv3gfs.fd" +export HOMEpost="$HOMEgfs" +export HOMEobsproc_prep="$BASE_GIT/obsproc/obsproc_prep.v5.5.0_hpc-stack" +export HOMEobsproc_network="$BASE_GIT/obsproc/obsproc_global.v3.4.2_hpc-stack" +export HOMEobsproc_global=$HOMEobsproc_network +export BASE_VERIF="$BASE_GIT/verif/global/tags/vsdb" + +# CONVENIENT utility scripts and other environment parameters +export NCP="/bin/cp -p" +export NMV="/bin/mv" +export NLN="/bin/ln -sf" +export VERBOSE="YES" +export KEEPDATA="NO" +export CHGRP_RSTPROD="@CHGRP_RSTPROD@" +export CHGRP_CMD="@CHGRP_CMD@" +export NEMSIOGET="$HOMEgfs/exec/nemsio_get" +export NCDUMP="$NETCDF/bin/ncdump" +export NCLEN="$HOMEgfs/ush/getncdimlen" + +# Machine environment, jobs, and other utility scripts +export BASE_ENV="$HOMEgfs/env" +export BASE_JOB="$HOMEgfs/jobs/rocoto" + +# EXPERIMENT specific environment parameters +export SDATE=@SDATE@ +export FDATE=@FDATE@ +export EDATE=@EDATE@ +export EXP_WARM_START="@EXP_WARM_START@" +export assim_freq=6 +export PSLOT="@PSLOT@" +export EXPDIR="@EXPDIR@/$PSLOT" +export ROTDIR="@ROTDIR@/$PSLOT" +export ROTDIR_DUMP="YES" #Note: A value of "NO" does not currently work +export DUMP_SUFFIX="" +if [[ "$CDATE" -ge "2019092100" && "$CDATE" -le "2019110700" ]]; then + export DUMP_SUFFIX="p" # Use dumps from NCO GFS v15.3 parallel +fi +export RUNDIR="$STMP/RUNDIRS/$PSLOT" +export DATAROOT="$RUNDIR/$CDATE/$CDUMP" +export ARCDIR="$NOSCRUB/archive/$PSLOT" +export ICSDIR="@ICSDIR@" +export ATARDIR="@ATARDIR@" + +# Commonly defined parameters in JJOBS +export envir=${envir:-"prod"} +export NET="gfs" +export RUN=${RUN:-${CDUMP:-"gfs"}} +export COMINatmos=${ROTDIR}/${CDUMP}.${PDY}/${cyc}/atmos +export COMOUTatmos=${ROTDIR}/${CDUMP}.${PDY}/${cyc}/atmos +export COMINwave=${ROTDIR}/${CDUMP}.${PDY}/${cyc}/wave +export COMOUTwave=${ROTDIR}/${CDUMP}.${PDY}/${cyc}/wave + +export jlogfile="${EXPDIR}/logs/jlogfile" +export ERRSCRIPT=${ERRSCRIPT:-'eval [[ $err = 0 ]]'} +export LOGSCRIPT=${LOGSCRIPT:-""} +#export ERRSCRIPT=${ERRSCRIPT:-"err_chk"} +#export LOGSCRIPT=${LOGSCRIPT:-"startmsg"} +export REDOUT="1>" +export REDERR="2>" + +export SENDECF=${SENDECF:-"NO"} +export SENDCOM=${SENDCOM:-"NO"} +export SENDSDM=${SENDSDM:-"NO"} +export SENDDBN_NTC=${SENDDBN_NTC:-"NO"} +export SENDDBN=${SENDDBN:-"NO"} +export DBNROOT=${DBNROOT:-${UTILROOT}/fakedbn} + +# APP settings +export APP=@APP@ + +# Defaults: +export DO_COUPLED="NO" +export DO_WAVE="NO" +export DO_OCN="NO" +export DO_ICE="NO" +export DO_AERO=@DO_AERO@ +export CCPP_SUITE="FV3_GFS_v17_p8" +export WAVE_CDUMP="" # When to include wave suite: gdas, gfs, or both +export cplwav2atm=".false." + +aero_nems_string="" +if [[ $DO_AERO = "YES" ]]; then + aero_nems_string="_aero" +else + aero_nems_string="" +fi + +case "${APP}" in + ATM) + echo "APP=ATM; will use defaults" + export confignamevarfornems="atm${aero_nems_string}" + ;; + ATMW) + export DO_COUPLED="YES" + export DO_WAVE="YES" + export WAVE_CDUMP="both" + export confignamevarfornems="leapfrog_atm${aero_nems_string}_wav" + ;; + S2S) + export DO_COUPLED="YES" + export DO_OCN="YES" + export DO_ICE="YES" + export CCPP_SUITE="FV3_GFS_v17_coupled_p8" + export confignamevarfornems="cpld${aero_nems_string}" + ;; + S2SW) + export DO_COUPLED="YES" + export DO_WAVE="YES" + export DO_OCN="YES" + export DO_ICE="YES" + export CCPP_SUITE="FV3_GFS_v17_coupled_p8" + export WAVE_CDUMP="both" + export cplwav2atm=".true." + export confignamevarfornems="cpld${aero_nems_string}_wave" + source $EXPDIR/config.defaults.s2sw + ;; + *) + echo "Unrecognized APP: ${1}" + exit 1 + ;; +esac + +# Set operational resolution +export OPS_RES="C768" # Do not change + +# Resolution specific parameters +export LEVS=128 +export CASE="@CASECTL@" +export CASE_ENKF="@CASEENS@" +case "$CASE" in + "C48") export OCNRES=400;; + "C96") export OCNRES=100;; + "C192") export OCNRES=050;; + "C384") export OCNRES=025;; + "C768") export OCNRES=025;; + *) export OCNRES=025;; +esac +export ICERES=$OCNRES + +# Surface cycle update frequency +if [[ "$CDUMP" == "gdas" ]] ; then + export FHCYC=1 + export FTSFS=10 +elif [[ "$CDUMP" == "gfs" ]] ; then + export FHCYC=24 +fi + +# Output frequency of the forecast model (for cycling) +export FHMIN=0 +export FHMAX=9 +export FHOUT=3 + +# Cycle to run EnKF (set to BOTH for both gfs and gdas) +export EUPD_CYC="gdas" + +# GFS cycle info +export gfs_cyc=@gfs_cyc@ # 0: no GFS cycle, 1: 00Z only, 2: 00Z and 12Z only, 4: all 4 cycles. + +# GFS output and frequency +export FHMIN_GFS=0 + +export FHMAX_GFS_00=${FHMAX_GFS_00:-384} +export FHMAX_GFS_06=${FHMAX_GFS_06:-384} +export FHMAX_GFS_12=${FHMAX_GFS_12:-384} +export FHMAX_GFS_18=${FHMAX_GFS_18:-384} +export FHMAX_GFS=$(eval echo \${FHMAX_GFS_$cyc}) + +export FHOUT_GFS=${FHOUT_GFS:-3} +export FHMAX_HF_GFS=${FHMAX_HF_GFS:-0} +export FHOUT_HF_GFS=${FHOUT_HF_GFS:-1} +export STEP_GFS="24" +export ILPOST=1 # gempak output frequency up to F120 + +# GFS restart interval in hours +export restart_interval_gfs=0 + +# I/O QUILTING, true--use Write Component; false--use GFDL FMS +# if quilting=true, choose OUTPUT_GRID as cubed_sphere_grid in netcdf or gaussian_grid +# if gaussian_grid, set OUTPUT_FILE for nemsio or netcdf +# WRITE_DOPOST=true, use inline POST +export QUILTING=".true." +export OUTPUT_GRID="gaussian_grid" +export OUTPUT_FILE="netcdf" +export WRITE_DOPOST=".true." +export WRITE_NSFLIP=".true." + +# suffix options depending on file format +if [ $OUTPUT_FILE = "netcdf" ]; then + export SUFFIX=".nc" + export NEMSIO_IN=".false." + export NETCDF_IN=".true." +else + export SUFFIX=".nemsio" + export NEMSIO_IN=".true." + export NETCDF_IN=".false." +fi + +# IAU related parameters +export DOIAU="YES" # Enable 4DIAU for control with 3 increments +export IAUFHRS="3,6,9" +export IAU_FHROT=$(echo $IAUFHRS | cut -c1) +export IAU_DELTHRS=6 +export IAU_OFFSET=6 +export DOIAU_ENKF=${DOIAU:-"YES"} # Enable 4DIAU for EnKF ensemble +export IAUFHRS_ENKF="3,6,9" +export IAU_DELTHRS_ENKF=6 +# Check if cycle is cold starting, DOIAU off, or free-forecast mode +if [[ "$MODE" = "cycled" && "$SDATE" = "$CDATE" && $EXP_WARM_START = ".false." ]] || [[ "$DOIAU" = "NO" ]] || [[ "$MODE" = "forecast-only" && $EXP_WARM_START = ".false." ]] ; then + export IAU_OFFSET=0 + export IAU_FHROT=0 +fi + +# Use Jacobians in eupd and thereby remove need to run eomg +export lobsdiag_forenkf=".true." + +# run GLDAS to spin up land ICs +export DO_GLDAS="YES" +export gldas_cyc=00 + +# if [[ "$SDATE" -lt "2019020100" ]]; then # no rtofs in GDA +# export DO_WAVE="NO" +# echo "WARNING: Wave suite turned off due to lack of RTOFS in GDA for SDATE" +# fi + +# Microphysics Options: 99-ZhaoCarr, 8-Thompson; 6-WSM6, 10-MG, 11-GFDL +export imp_physics=8 + +# Shared parameters +# Hybrid related +export DOHYBVAR="YES" +export NMEM_ENKF=@NMEM_ENKF@ +export SMOOTH_ENKF="NO" +export l4densvar=".true." +export lwrite4danl=".true." + +# EnKF output frequency +if [ $DOHYBVAR = "YES" ]; then + export FHMIN_ENKF=3 + export FHMAX_ENKF=9 + if [ $l4densvar = ".true." ]; then + export FHOUT=1 + export FHOUT_ENKF=1 + else + export FHOUT_ENKF=3 + fi +fi + +# turned on nsst in anal and/or fcst steps, and turn off rtgsst +export DONST="YES" +if [ $DONST = "YES" ]; then export FNTSFA=" "; fi + +# The switch to apply SST elevation correction or not +export nst_anl=.true. + +# Analysis increments to zero in CALCINCEXEC +export INCREMENTS_TO_ZERO="'liq_wat_inc','icmr_inc'" + +if [ $OUTPUT_FILE = "nemsio" ]; then + export DO_CALC_INCREMENT="YES" + export DO_CALC_ANALYSIS="NO" +fi + +# Stratospheric increments to zero +export INCVARS_ZERO_STRAT="'sphum_inc','liq_wat_inc','icmr_inc'" +export INCVARS_EFOLD="5" + +# Swith to generate netcdf or binary diagnostic files. If not specified, +# script default to binary diagnostic files. Set diagnostic file +# variables here since used in both DA and vrfy jobs +export netcdf_diag=".true." +export binary_diag=".false." + +# Verification options +export DO_METP="YES" # Run METPLUS jobs - set METPLUS settings in config.metp +export DO_VSDB="YES" # Run VSDB package - set VSDB settings in config.vrfy + +# Archiving options +export HPSSARCH="@HPSSARCH@" # save data to HPSS archive +export LOCALARCH="@LOCALARCH@" # save data to local archive +if [[ $HPSSARCH = "YES" ]] && [[ $LOCALARCH = "YES" ]]; then + echo "Both HPSS and local archiving selected. Please choose one or the other." + exit 2 +fi +export ARCH_CYC=00 # Archive data at this cycle for warm_start capability +export ARCH_WARMICFREQ=4 # Archive frequency in days for warm_start capability +export ARCH_FCSTICFREQ=1 # Archive frequency in days for gdas and gfs forecast-only capability + +export DELETE_COM_IN_ARCHIVE_JOB="YES" # NO=retain ROTDIR. YES default in arch.sh and earc.sh. + +echo "END: config.base" diff --git a/parm/config/config.base.emc.dyn_hera b/parm/config/config.base.emc.dyn_hera index c69254f5c7..e715e36bd3 100755 --- a/parm/config/config.base.emc.dyn_hera +++ b/parm/config/config.base.emc.dyn_hera @@ -150,9 +150,12 @@ export DO_WAVE="NO" export DO_OCN="NO" export DO_ICE="NO" export DO_AERO="NO" +export CCPP_SUITE="FV3_GFS_v17_p8" # GFS v17 prototype p8 +#### export CCPP_SUITE="FV3_GFS_v17_p8_gf" # GFS v17 prototype p8 + GF +#### export CCPP_SUITE="FV3_GFS_v17_p8_mynn" # GFS v17 prototype p8 + MYNN #### export CCPP_SUITE="FV3_GFS_v16" # GFS v16 #### export CCPP_SUITE="FV3_RAP_cires_ugwp" # RUC LSM, MYNN, GF, CIRES UGWP -export CCPP_SUITE="FV3_RAP_noah_sfcdiff_unified_ugwp" # NOAH LSM, MYNN, GF, no GF shallow, Unified UGWP +#### export CCPP_SUITE="FV3_RAP_noah_sfcdiff_unified_ugwp" # NOAH LSM, MYNN, GF, no GF shallow, Unified UGWP #### export CCPP_SUITE="FV3_RAP_noah_sfcdiff_ugwpv1" # NOAH LSM, MYNN, GF, no GF shallow, UGWPv1 #### export CCPP_SUITE="FV3_GFS_v16_gf" # GFS v16 + GF #### export CCPP_SUITE="FV3_GFS_v16_mynn" # GFS v16 + MYNN @@ -307,11 +310,7 @@ export gldas_cyc=00 # fi # Microphysics Options: 99-ZhaoCarr, 8-Thompson; 6-WSM6, 10-MG, 11-GFDL -if [[ "$CCPP_SUITE" == "FV3_RAP_cires_ugwp" || "$CCPP_SUITE" == "FV3_RAP_noah_sfcdiff_unified_ugwp" || "$CCPP_SUITE" == "FV3_RAP_noah_sfcdiff_ugwpv1" || "$CCPP_SUITE" == "FV3_GFS_v16_thompson" ]] ; then - export imp_physics=8 -else - export imp_physics=11 -fi +export imp_physics=8 # Shared parameters # Hybrid related diff --git a/parm/config/config.base.emc.dyn_jet b/parm/config/config.base.emc.dyn_jet index 449cf6ef48..588ac526ad 100755 --- a/parm/config/config.base.emc.dyn_jet +++ b/parm/config/config.base.emc.dyn_jet @@ -150,9 +150,12 @@ export DO_WAVE="NO" export DO_OCN="NO" export DO_ICE="NO" export DO_AERO="NO" +export CCPP_SUITE="FV3_GFS_v17_p8" # GFS v17 prototype p8 +#### export CCPP_SUITE="FV3_GFS_v17_p8_gf" # GFS v17 prototype p8 + GF +#### export CCPP_SUITE="FV3_GFS_v17_p8_mynn" # GFS v17 prototype p8 + MYNN #### export CCPP_SUITE="FV3_GFS_v16" # GFS v16 #### export CCPP_SUITE="FV3_RAP_cires_ugwp" # RUC LSM, MYNN, GF, CIRES UGWP -export CCPP_SUITE="FV3_RAP_noah_sfcdiff_unified_ugwp" # NOAH LSM, MYNN, GF, no GF shallow, Unified UGWP +#### export CCPP_SUITE="FV3_RAP_noah_sfcdiff_unified_ugwp" # NOAH LSM, MYNN, GF, no GF shallow, Unified UGWP #### export CCPP_SUITE="FV3_RAP_noah_sfcdiff_ugwpv1" # NOAH LSM, MYNN, GF, no GF shallow, UGWPv1 #### export CCPP_SUITE="FV3_GFS_v16_gf" # GFS v16 + GF #### export CCPP_SUITE="FV3_GFS_v16_mynn" # GFS v16 + MYNN @@ -307,11 +310,7 @@ export gldas_cyc=00 # fi # Microphysics Options: 99-ZhaoCarr, 8-Thompson; 6-WSM6, 10-MG, 11-GFDL -if [[ "$CCPP_SUITE" == "FV3_RAP_cires_ugwp" || "$CCPP_SUITE" == "FV3_RAP_noah_sfcdiff_unified_ugwp" || "$CCPP_SUITE" == "FV3_RAP_noah_sfcdiff_ugwpv1" || "$CCPP_SUITE" == "FV3_GFS_v16_thompson" ]] ; then - export imp_physics=8 -else - export imp_physics=11 -fi +export imp_physics=8 # Shared parameters # Hybrid related diff --git a/parm/config/config.base.nco.static b/parm/config/config.base.nco.static index 7c47c27ad4..d9d2764a08 100755 --- a/parm/config/config.base.nco.static +++ b/parm/config/config.base.nco.static @@ -212,7 +212,7 @@ export DO_WAVE=YES export WAVE_CDUMP="both" # Microphysics Options: 99-ZhaoCarr, 8-Thompson; 6-WSM6, 10-MG, 11-GFDL -export imp_physics=11 +export imp_physics=8 # Shared parameters # Hybrid related diff --git a/parm/config/config.defaults.s2sw b/parm/config/config.defaults.s2sw index 559782c7c5..e52c0c5d88 100644 --- a/parm/config/config.defaults.s2sw +++ b/parm/config/config.defaults.s2sw @@ -12,20 +12,17 @@ FHOUT_GFS=6 FHOUT_HF_GFS=-1 # config.fcst -IALB=2 -IEMS=2 -DO_CA="YES" -min_seaice="1e-6" +min_seaice="1.0e-6" use_cice_alb=".true." -export iopt_sfc="3" # config.fv3 DELTIM=300 -layout_x_gfs=8 -layout_y_gfs=12 +layout_x_gfs=24 +layout_y_gfs=16 WRITE_GROUP_GFS=1 WRTTASK_PER_GROUP_GFS=80 WRTIOBUF="32M" +MEDPETS=300 # config.wave waveGRD='gwes_30m' diff --git a/parm/config/config.efcs b/parm/config/config.efcs index 8026d5bbbe..89b7684fc3 100755 --- a/parm/config/config.efcs +++ b/parm/config/config.efcs @@ -18,11 +18,13 @@ if [ $QUILTING = ".true." ]; then export npe_efcs=$npe_fv3 fi -# Only use serial I/O for ensemble on Hera -if [[ "$machine" == "HERA" ]]; then - export OUTPUT_FILETYPE_ATM="netcdf" - export OUTPUT_FILETYPE_SFC="netcdf" -fi +# Only use serial I/O for ensemble on Hera and Orion (lustre?) +case $machine in + "HERA" | "ORION") + export OUTPUT_FILETYPE_ATM="netcdf" + export OUTPUT_FILETYPE_SFC="netcdf" + ;; +esac # Number of enkf members per fcst job export NMEM_EFCSGRP=2 @@ -73,12 +75,13 @@ export cplwav=.false. # ocean model resolution case "$CASE_ENKF" in - "C48") export OCNRES=100;; + "C48") export OCNRES=400;; "C96") export OCNRES=100;; "C192") export OCNRES=050;; "C384") export OCNRES=025;; "C768") export OCNRES=025;; *) export OCNRES=025;; esac +export ICERES=$OCNRES echo "END: config.efcs" diff --git a/parm/config/config.eobs b/parm/config/config.eobs index 8fa99c10fb..e46dde2f34 100755 --- a/parm/config/config.eobs +++ b/parm/config/config.eobs @@ -14,7 +14,7 @@ export RERUN_EOMGGRP="YES" export npe_gsi=$npe_eobs # GSI namelist options related to observer for EnKF -export OBSINPUT_INVOBS="dmesh(1)=225.0,dmesh(2)=225.0" +export OBSINPUT_INVOBS="dmesh(1)=225.0,dmesh(2)=225.0,dmesh(3)=225.0,dmesh(4)=100.0" export OBSQC_INVOBS="tcp_width=60.0,tcp_ermin=2.0,tcp_ermax=12.0" if [ $LEVS = "128" ]; then export GRIDOPTS_INVOBS="nlayers(63)=1,nlayers(64)=1," diff --git a/parm/config/config.fcst b/parm/config/config.fcst index 07ab9735ba..cba5cdbbaa 100755 --- a/parm/config/config.fcst +++ b/parm/config/config.fcst @@ -26,6 +26,8 @@ done # Get task specific resources . $EXPDIR/config.resources fcst +export domains_stack_size="16000000" + if [ $DONST = "YES" ]; then . $EXPDIR/config.nsst @@ -39,14 +41,14 @@ export esmf_profile=".false." export OCN_model="mom6" export ICE_model="cice6" export WAV_model="ww3" -export CHM_model="gsdchem" +export CHM_model="gocart" # cpl defaults export cpl=".false." export cplflx=".false." export cplice=".false." -export cplgocart=".false." +export cplchm=".false." export cplwav=".false." # cpl changes based on APP @@ -55,12 +57,13 @@ if [ $DO_COUPLED = "YES" ]; then export cpl=".true." fi if [ $DO_AERO = "YES" ]; then - export cplgocart=".true." + export cplchm=".true." fi if [ $DO_ICE = "YES" ]; then export cplice=".true." + export cplflx=".true." fi -if [ $DO_OCN = "YES" -o $DO_ICE = "YES" ]; then +if [ $DO_OCN = "YES" ]; then export cplflx=".true." fi if [ $DO_WAVE = "YES" ]; then @@ -89,19 +92,80 @@ export h2o_phys=".true." # Options of stratosphere O3 physics reaction coefficients export new_o3forc="YES" -# export launch_level=$(echo "$LEVS/2.35" |bc) +export gwd_opt=2 + +# --GFS.v16 uGWD.v0, used for suite FV3_GFS_v16 and UFS p6 etc +# do_ugwp=T: use unified CGWD and OGWD, and turbulent orographic form drag (TOFD) +# do_ugwp=F: use unified CGWD but old OGWD, TOFD is not uded. +if [ $gwd_opt -eq 1 ]; then + export knob_ugwp_version=0 + export do_ugwp=".false." + export do_tofd=".false." + export launch_level=$(echo "$LEVS/2.35" |bc) +fi + + +# -- uGWD.v1, for suite FV3_GFS_v17 and FV3_GFS_v17p8b etc +if [ $gwd_opt -eq 2 ]; then + + #--used for UFS p7 and p8a + #export knob_ugwp_version=1 + #export do_ugwp=".false." + #export do_tofd=".false." + #export do_ugwp_v0=".false." + #export do_ugwp_v1=".true." + #export do_ugwp_v0_orog_only=".false." + #export do_ugwp_v0_nst_only=".false." + #export do_gsl_drag_ls_bl=".true." + #export do_gsl_drag_ss=".true." + #export do_gsl_drag_tofd=".true." + #export do_ugwp_v1_orog_only=".false." + + #--used for UFS p8b + export knob_ugwp_version=0 + export do_ugwp=".false." + export do_tofd=".false." + export do_ugwp_v0=".true." + export do_ugwp_v1=".false." + export do_ugwp_v0_orog_only=".false." + export do_ugwp_v0_nst_only=".false." + export do_gsl_drag_ls_bl=".false." + export do_gsl_drag_ss=".true." + export do_gsl_drag_tofd=".true." + export do_ugwp_v1_orog_only=".false." + export launch_level=$(echo "$LEVS/2.35" |bc) + + #--used for GSL suite +##JKH export knob_ugwp_version=0 +##JKH export do_ugwp=".false." +##JKH export do_tofd="true." +##JKH export do_ugwp_v0=".true." +##JKH export do_ugwp_v1=".false." +##JKH export do_ugwp_v0_orog_only=".false." +##JKH export do_ugwp_v0_nst_only=".false." +##JKH export do_gsl_drag_ls_bl=".false." +##JKH export do_gsl_drag_ss=".true." +##JKH export do_gsl_drag_tofd=".true." +##JKH export do_ugwp_v1_orog_only=".false." +##JKH export launch_level=$(echo "$LEVS/2.35" |bc) +fi + + + # Sponge layer settings export tau=10.0 export rf_cutoff=7.5e2 export d2_bg_k1=0.20 +### JKH if [[ "$CCPP_SUITE" == "FV3_RAP_cires_ugwp" || "$CCPP_SUITE" == "FV3_RAP_noah_sfcdiff_unified_ugwp" ]] ; then - export d2_bg_k2=0.15 ### JKH - 10dec + export d2_bg_k2=0.15 ### JKH - 10dec + export dz_min=2 + export dt_inner=40. ### JKH - 10dec else - export d2_bg_k2=0.04 + export d2_bg_k2=0.04 + export dz_min=6 fi -export dz_min=2 -export dt_inner=40. ### JKH - 10dec if [ $LEVS = "128" ]; then export n_sponge=42; fi #127 layer if [ $LEVS = "65" ]; then if [ "CCPP_SUITE" = "FV3_RAP_cires_ugwp" -o "CCPP_SUITE" = "FV3_RAP_noah_sfcdiff_unified_ugwp" ]; then @@ -119,23 +183,27 @@ fi # PBL/turbulence schemes export hybedmf=".false." -if [[ "$CCPP_SUITE" == "FV3_RAP_cires_ugwp" || "$CCPP_SUITE" == "FV3_RAP_noah_sfcdiff_unified_ugwp" || "$CCPP_SUITE" == "FV3_GFS_v16_mynn" ]] ; then - export satmedmf=".false." - export isatmedmf=0 - export shal_cnv=".true." - export do_mynnedmf=".true." - if [[ "$CCPP_SUITE" == "FV3_RAP_cires_ugwp" ]] ; then - export do_mynnsfclay=".true." - else - export do_mynnsfclay=".false." - fi - export icloud_bl=1 - export bl_mynn_tkeadvect=.true. - export bl_mynn_edmf=1 - export bl_mynn_edmf_mom=1 +if [[ "$CCPP_SUITE" == "FV3_RAP_cires_ugwp" || "$CCPP_SUITE" == "FV3_RAP_noah_sfcdiff_unified_ugwp" || "$CCPP_SUITE" == "FV3_GFS_v16_mynn" || "$CCPP_SUITE" == "FV3_GFS_v17_p8_mynn" ]] ; then + export satmedmf=".false." + export isatmedmf=0 + if [[ "$CCPP_SUITE" == "FV3_GFS_v17_p8_mynn" ]] ; then + export shal_cnv=".false." + else + export shal_cnv=".true." + fi + export do_mynnedmf=".true." + if [[ "$CCPP_SUITE" == "FV3_RAP_cires_ugwp" ]] ; then + export do_mynnsfclay=".true." + else + export do_mynnsfclay=".false." + fi + export icloud_bl=1 + export bl_mynn_tkeadvect=.true. + export bl_mynn_edmf=1 + export bl_mynn_edmf_mom=1 else - export satmedmf=".true." - export isatmedmf=1 + export satmedmf=".true." + export isatmedmf=1 fi tbf="" @@ -150,18 +218,22 @@ export icliq_sw=2 ; #cloud optical coeffs from AER's newer version v3.9-v4.0 f export isubc_sw=2 export isubc_lw=2 -export IALB=${IALB:-1} -export IEMS=${IEMS:-1} -export ISOL=2 -export ICO2=2 +export iopt_sfc="3" # Convection Options: 2-SASAS, 3-GF -if [[ "$CCPP_SUITE" == "FV3_RAP_cires_ugwp" || "$CCPP_SUITE" == "FV3_RAP_noah_sfcdiff_unified_ugwp" || "$CCPP_SUITE" == "FV3_GFS_v16_gf" ]] ; then - export imfdeepcnv=3 - export imfshalcnv=-1 ## JKH - no shallow GF +if [[ "$CCPP_SUITE" == "FV3_RAP_cires_ugwp" || "$CCPP_SUITE" == "FV3_RAP_noah_sfcdiff_unified_ugwp" ]] ; then + export imfdeepcnv=3 + export imfshalcnv=-1 ## JKH - no shallow GF +elif [[ "$CCPP_SUITE" == "FV3_GFS_v16_gf" || "$CCPP_SUITE" == "FV3_GFS_v17_p8_gf" ]] ; then + export imfdeepcnv=3 + export imfshalcnv=3 else - export imfdeepcnv=2 - export imfshalcnv=2 + export imfdeepcnv=2 + if [[ "$CCPP_SUITE" == "FV3_GFS_v17_p8_mynn" ]] ; then + export imfshalcnv=-1 + else + export imfshalcnv=2 + fi fi # Microphysics configuration @@ -181,9 +253,13 @@ elif [ $imp_physics -eq 6 ]; then # WSM6 export nwat=6 elif [ $imp_physics -eq 8 ]; then # Thompson - export ncld=2 - export FIELD_TABLE="$HOMEgfs/parm/parm_fv3diag/field_table_thompson${tbf}" export nwat=6 + export cal_pre=".false." + export random_clds=".false." + export effr_in=".true." + export ttendlim="-999" + export dddmp=0.1 + export d4_bg=0.12 if [[ "$CCPP_SUITE" == "FV3_RAP_cires_ugwp" || "$CCPP_SUITE" == "FV3_RAP_noah_sfcdiff_unified_ugwp" || "$CCPP_SUITE" == "FV3_GFS_v16_thompson" ]] ; then export ncld=5 export FIELD_TABLE="$HOMEgfs/parm/parm_fv3diag/field_table_thompson_aero_tke" @@ -191,21 +267,12 @@ elif [ $imp_physics -eq 8 ]; then # Thompson export lradar=.true. ## GSL namelist changes - export cal_pre=".false." - export random_clds=".false." - export effr_in=.true. export vtdm4_nh_nonmono=0.03 ### JKH - 10dec export nord=3 ### JKH - 10dec - export dddmp=0.1 - export d4_bg=0.12 export dt_inner=40. ### JKH - 10dec if [[ "$CCPP_SUITE" == "FV3_RAP_cires_ugwp" || "$CCPP_SUITE" == "FV3_RAP_noah_sfcdiff_unified_ugwp" || "$CCPP_SUITE" == "FV3_RAP_noah_sfcdiff_ugwpv1" ]] ; then export k_split=6 export n_split=2 - #JKH-10dec export ttendlim=0.005 - export ttendlim=-999. ### JKH - 10dec - else - export ttendlim=-999. ## JKH - Thmp subcyc fi export kord_tm=-11 ### JKH - 10dec @@ -217,7 +284,14 @@ elif [ $imp_physics -eq 8 ]; then # Thompson export hord_xx_nh_nonmono=6 ### JKH - 10dec else export ncld=2 - export FIELD_TABLE="$HOMEgfs/parm/parm_fv3diag/field_table_thompson${tbf}" + export FIELD_TABLE="$HOMEgfs/parm/parm_fv3diag/field_table_thompson_noaero_tke" + export ltaerosol=".false." + export lradar=".false." + export dt_inner=$((DELTIM/2)) + export hord_mt_nh_nonmono=5 + export hord_xx_nh_nonmono=5 + export vtdm4_nh_nonmono=0.02 + export nord=2 fi elif [ $imp_physics -eq 11 ]; then # GFDL @@ -253,7 +327,7 @@ export DO_SPPT=${DO_SPPT:-"NO"} export DO_SKEB=${DO_SKEB:-"NO"} export DO_SHUM=${DO_SHUM:-"NO"} export DO_LAND_PERT=${DO_LAND_PERT:-"NO"} -export DO_CA=${DO_CA:-"NO"} +export DO_CA=${DO_CA:-"YES"} export DO_OCN_SPPT=${DO_OCN_SPPT:-"NO"} export DO_OCN_PERT_EPBL=${DO_OCN_PERT_EPBL:-"NO"} @@ -266,7 +340,8 @@ export min_lakeice="0.15" export min_seaice=${min_seaice:-"0.15"} export use_cice_alb=${use_cice_alb:-".false."} -export FSICL="99999" +export FSICL="0" +export FSICS="0" #--------------------------------------------------------------------- @@ -366,16 +441,20 @@ elif [[ "$CDUMP" == "gfs" ]] ; then # GFS cycle specific parameters fi -if [ $DO_AERO = "YES" ]; then # temporary settings for aerosol coupling - export DIAG_TABLE="$HOMEgfs/parm/parm_fv3diag/diag_table_aer" - export FIELD_TABLE="$HOMEgfs/parm/parm_fv3diag/field_table_aer" - export CHM_CFGDIR="not_set" - export CHM_INPDIR="not_set" - export dnats="$(( $dnats + 0 ))" -elif [[ $DO_COUPLED = "YES" ]] ; then # coupled model +if [[ $DO_COUPLED = "YES" ]] ; then # coupled model export DIAG_TABLE="$HOMEgfs/parm/parm_fv3diag/diag_table_cpl" fi +if [ $DO_AERO = "YES" ]; then # temporary settings for aerosol coupling + export AERO_DIAG_TABLE="${AERO_DIAG_TABLE:-$HOMEgfs/parm/parm_fv3diag/diag_table.aero}" + export AERO_FIELD_TABLE="${AERO_FIELD_TABLE:-$HOMEgfs/parm/parm_fv3diag/field_table.aero}" + export AERO_EMIS_FIRE=$( echo "${AERO_EMIS_FIRE:-none}" | awk '{ print tolower($1) }' ) + export AERO_CONFIG_DIR="${AERO_CONFIG_DIR:-$HOMEgfs/parm/chem}" + export AERO_INPUTS_DIR="${AERO_INPUTS_DIR:-}" + export fscav_aero="${aero_conv_scav_factors:-${fscav_aero}}" + export dnats_aero="${aero_diag_tracers:-0}" +fi + # Remember config.efcs will over-ride these values for ensemble forecasts # if these variables are re-defined there. # Otherwise, the ensemble forecast will inherit from config.fcst diff --git a/parm/config/config.fv3 b/parm/config/config.fv3 index 06bcf1d95b..e6efd81b06 100755 --- a/parm/config/config.fv3 +++ b/parm/config/config.fv3 @@ -38,7 +38,6 @@ elif [[ "$machine" = "ORION" ]]; then export npe_node_max=40 fi - # (Standard) Model resolution dependent variables case $case_in in "C48") @@ -91,15 +90,14 @@ case $case_in in export WRTIOBUF="8M" ;; "C384") - export DELTIM=${DELTIM:-240} + export DELTIM=${DELTIM:-300} export layout_x=6 export layout_y=8 - export layout_x_gfs=${layout_x_gfs:-6} - export layout_y_gfs=${layout_y_gfs:-8} + export layout_x_gfs=${layout_x_gfs:-8} + export layout_y_gfs=${layout_y_gfs:-12} export nth_fv3=2 export nth_fv3_gfs=${nth_fv3_gfs:-2} - export cdmbgwd="1.0,2.2,1.0,1.0" # mountain blocking, ogwd, cgwd, cgwd src scaling - if [[ "$CCPP_SUITE" == "FV3_RAP_noah_sfcdiff_ugwpv1" ]]; then export cdmbgwd="1.0,1.0,1.0,1.0"; fi + export cdmbgwd="1.1,0.72,1.0,1.0" # mountain blocking, ogwd, cgwd, cgwd src scaling export WRITE_GROUP=1 export WRTTASK_PER_GROUP=$npe_node_max export WRITE_GROUP_GFS=${WRITE_GROUP_GFS:-2} diff --git a/parm/config/config.ocn b/parm/config/config.ocn index e8ecd1aca1..fbb750d90b 100644 --- a/parm/config/config.ocn +++ b/parm/config/config.ocn @@ -1,10 +1,11 @@ #!/bin/ksh -x -case "$CASE" in - "C48") export OCNRES=100;; - "C96") export OCNRES=100;; - "C192") export OCNRES=050;; - "C384") export OCNRES=025;; - "C768") export OCNRES=025;; - *) export OCNRES=025;; -esac +# OCNRES is currently being set in config.base +# case "$CASE" in +# "C48") export OCNRES=400;; +# "C96") export OCNRES=100;; +# "C192") export OCNRES=050;; +# "C384") export OCNRES=025;; +# "C768") export OCNRES=025;; +# *) export OCNRES=025;; +# esac diff --git a/parm/config/config.resources b/parm/config/config.resources index 9db26854dc..08ceeef6c2 100755 --- a/parm/config/config.resources +++ b/parm/config/config.resources @@ -198,8 +198,14 @@ elif [ $step = "fcst" ]; then (( ATMPETS = layout_x * layout_y * 6 )) - # Mediator only uses the atm model PETS - export MEDPETS=$ATMPETS + # Mediator only uses the atm model PETS or less + export MEDPETS=${MEDPETS:-ATMPETS} + + if [[ $DO_AERO == "YES" ]]; then + # Aerosol model only uses the atm model PETS + export CHMPETS=$ATMPETS + # Aerosol model runs on same PETs as ATM, so don't add to $NTASKS_TOT + fi # If using in-line post, add the write tasks to the ATMPETS if [[ $QUILTING == ".true." ]]; then @@ -256,11 +262,6 @@ elif [ $step = "fcst" ]; then (( NTASKS_TOT = NTASKS_TOT + ICEPETS )) fi - if [[ $DO_AERO == "YES" ]]; then - # Aerosol model runs on same PETS as ATM model - export CHMPETS=$MEDPETS - fi - if [[ $CDUMP == "gfs" ]]; then export npe_fcst_gfs=$NTASKS_TOT else diff --git a/parm/config/config.vrfy b/parm/config/config.vrfy index 82d0d8f9f5..adea840e7b 100755 --- a/parm/config/config.vrfy +++ b/parm/config/config.vrfy @@ -8,11 +8,11 @@ echo "BEGIN: config.vrfy" # Get task specific resources . $EXPDIR/config.resources vrfy -export DO_VSDB=${DO_VSDB:-"NO"} # Switch to run VSDB package; set in config.base +export DO_VSDB=${DO_VSDB:-"NO"} # Switch to run VSDB package; set in config.base -export VDUMP="gfs" # Verifying dump -export CDUMPFCST="gdas" # Fit-to-obs with GDAS/GFS prepbufr -export CDFNL="gdas" # Scores verification against GDAS/GFS analysis +export VDUMP="gfs" # Verifying dump +export CDUMPFCST="gdas" # Fit-to-obs with GDAS/GFS prepbufr +export CDFNL="gdas" # Scores verification against GDAS/GFS analysis export MKPGB4PRCP="NO" # Make 0.25-deg pgb files in ARCDIR for precip verification ## JKH export VRFYFITS="NO" # Fit to observations ## JKH @@ -144,6 +144,10 @@ fi export ens_tracker_ver=v1.1.15.4 export HOMEens_tracker=$BASE_GIT/TC_tracker/TC_tracker.${ens_tracker_ver} +## JKH +if [ $machine = "JET" ] ; then + export HOMEens_tracker=$HOMEgfs/sorc/ens_tracker.${ens_tracker_ver} +fi if [ "$VRFYTRAK" = "YES" ]; then @@ -155,7 +159,12 @@ if [ "$VRFYTRAK" = "YES" ]; then export FHOUT_CYCLONE=6 export FHMAX_CYCLONE=$(( FHMAX_GFS<240 ? FHMAX_GFS : 240 )) fi - export COMINsyn=${COMINsyn:-${COMROOT}/gfs/prod/syndat} + ## JKH + if [ $machine = "JET" ]; then + export COMINsyn=${COMINsyn:-/mnt/lfs4/HFIP/hwrf-data/hwrf-input/SYNDAT-PLUS} + else + export COMINsyn=${COMINsyn:-${COMROOT}/gfs/prod/syndat} + fi fi diff --git a/parm/mom6/MOM_input_template_025 b/parm/mom6/MOM_input_template_025 index aa682602ee..3abbf2191b 100644 --- a/parm/mom6/MOM_input_template_025 +++ b/parm/mom6/MOM_input_template_025 @@ -98,7 +98,7 @@ WRITE_GEOM = 2 ! default = 1 ! If =0, never write the geometry and vertical grid files. If =1, write the ! geometry and vertical grid files only for a new simulation. If =2, always ! write the geometry and vertical grid files. Other values are invalid. -SAVE_INITIAL_CONDS = True ! [Boolean] default = False +SAVE_INITIAL_CONDS = False ! [Boolean] default = False ! If true, write the initial conditions to a file given by IC_OUTPUT_FILE. ! === module MOM_hor_index === @@ -406,6 +406,9 @@ GILL_EQUATORIAL_LD = True ! [Boolean] default = False ! radius, otherwise, if false, use Pedlosky's definition. These definitions ! differ by a factor of 2 in front of the beta term in the denominator. Gill's ! is the more appropriate definition. +INTERNAL_WAVE_SPEED_BETTER_EST = False ! [Boolean] default = True + ! If true, use a more robust estimate of the first mode wave speed as the + ! starting point for iterations. ! === module MOM_set_visc === CHANNEL_DRAG = True ! [Boolean] default = False @@ -728,6 +731,11 @@ NSTAR = 0.06 ! [nondim] default = 0.2 ! The portion of the buoyant potential energy imparted by surface fluxes that is ! available to drive entrainment at the base of mixed layer when that energy is ! positive. +EPBL_MLD_BISECTION = True ! [Boolean] default = False + ! If true, use bisection with the iterative determination of the self-consistent + ! mixed layer depth. Otherwise use the false position after a maximum and + ! minimum bound have been evaluated and the returned value or bisection before + ! this. MSTAR_CONV_ADJ = 0.667 ! [nondim] default = 0.0 ! Coefficient used for reducing mstar during convection due to reduction of ! stable density gradient. diff --git a/parm/mom6/MOM_input_template_050 b/parm/mom6/MOM_input_template_050 index 2662e976ed..4e703a4bfd 100644 --- a/parm/mom6/MOM_input_template_050 +++ b/parm/mom6/MOM_input_template_050 @@ -98,7 +98,7 @@ WRITE_GEOM = 2 ! default = 1 ! If =0, never write the geometry and vertical grid files. If =1, write the ! geometry and vertical grid files only for a new simulation. If =2, always ! write the geometry and vertical grid files. Other values are invalid. -SAVE_INITIAL_CONDS = True ! [Boolean] default = False +SAVE_INITIAL_CONDS = False ! [Boolean] default = False ! If true, write the initial conditions to a file given by IC_OUTPUT_FILE. ! === module MOM_hor_index === @@ -419,6 +419,9 @@ GILL_EQUATORIAL_LD = True ! [Boolean] default = False ! radius, otherwise, if false, use Pedlosky's definition. These definitions ! differ by a factor of 2 in front of the beta term in the denominator. Gill's ! is the more appropriate definition. +INTERNAL_WAVE_SPEED_BETTER_EST = False ! [Boolean] default = True + ! If true, use a more robust estimate of the first mode wave speed as the + ! starting point for iterations. ! === module MOM_set_visc === CHANNEL_DRAG = True ! [Boolean] default = False @@ -754,6 +757,11 @@ MSTAR2_COEF1 = 0.29 ! [nondim] default = 0.3 MSTAR2_COEF2 = 0.152 ! [nondim] default = 0.085 ! Coefficient in computing mstar when only rotation limits the total mixing ! (used if EPBL_MSTAR_SCHEME = OM4) +EPBL_MLD_BISECTION = True ! [Boolean] default = False + ! If true, use bisection with the iterative determination of the self-consistent + ! mixed layer depth. Otherwise use the false position after a maximum and + ! minimum bound have been evaluated and the returned value or bisection before + ! this. NSTAR = 0.06 ! [nondim] default = 0.2 ! The portion of the buoyant potential energy imparted by surface fluxes that is ! available to drive entrainment at the base of mixed layer when that energy is @@ -822,15 +830,6 @@ LT_MOD_LAC5 = 0.22 ! [nondim] default = 0.95 ! === module MOM_regularize_layers === ! === module MOM_opacity === -VAR_PEN_SW = True ! [Boolean] default = False - ! If true, use one of the CHL_A schemes specified by - ! OPACITY_SCHEME to determine the e-folding depth of - ! incoming short wave radiation. -CHL_FILE = @[CHLCLIM] ! CHL_FILE is the file containing chl_a concentrations in - ! the variable CHL_A. It is used when VAR_PEN_SW and - ! CHL_FROM_FILE are true. -CHL_VARNAME = "chlor_a" ! default = "CHL_A" - ! Name of CHL_A variable in CHL_FILE. PEN_SW_NBANDS = 3 ! default = 1 ! The number of bands of penetrating shortwave radiation. diff --git a/parm/mom6/MOM_input_template_100 b/parm/mom6/MOM_input_template_100 index 6c65fe3d24..1716f6fabd 100644 --- a/parm/mom6/MOM_input_template_100 +++ b/parm/mom6/MOM_input_template_100 @@ -72,7 +72,7 @@ WRITE_GEOM = 2 ! default = 1 ! If =0, never write the geometry and vertical grid files. If =1, write the ! geometry and vertical grid files only for a new simulation. If =2, always ! write the geometry and vertical grid files. Other values are invalid. -SAVE_INITIAL_CONDS = True ! [Boolean] default = False +SAVE_INITIAL_CONDS = False ! [Boolean] default = False ! If true, write the initial conditions to a file given by IC_OUTPUT_FILE. ! === module MOM_oda_incupd === @@ -430,6 +430,9 @@ VISC_RES_FN_POWER = 2 ! [nondim] default = 100 ! used, although even integers are more efficient to calculate. Setting this ! greater than 100 results in a step-function being used. This function affects ! lateral viscosity, Kh, and not KhTh. +INTERNAL_WAVE_SPEED_BETTER_EST = False ! [Boolean] default = True + ! If true, use a more robust estimate of the first mode wave speed as the + ! starting point for iterations. ! === module MOM_set_visc === CHANNEL_DRAG = True ! [Boolean] default = False diff --git a/parm/parm_fv3diag/diag_table b/parm/parm_fv3diag/diag_table index 4617eab270..6fdc90c6e3 100644 --- a/parm/parm_fv3diag/diag_table +++ b/parm/parm_fv3diag/diag_table @@ -10,8 +10,8 @@ "gfs_dyn", "snowwat", "snmr", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "rainwat", "rwmr", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "graupel", "grle", "fv3_history", "all", .false., "none", 2 -#"gfs_dyn", "ice_nc", "nccice", "fv3_history", "all", .false., "none", 2 -#"gfs_dyn", "rain_nc", "nconrd", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "ice_nc", "nccice", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "rain_nc", "nconrd", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "o3mr", "o3mr", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "cld_amt", "cld_amt", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "delp", "dpres", "fv3_history", "all", .false., "none", 2 @@ -110,6 +110,14 @@ "gfs_phys", "u10m", "ugrd10m", "fv3_history2d", "all", .false., "none", 2 "gfs_phys", "v10m", "vgrd10m", "fv3_history2d", "all", .false., "none", 2 +"gfs_phys", "pahi", "pahi", "fv3_history2d", "all", .false., "none", 2 +"gfs_phys", "pah_ave", "pah_ave", "fv3_history2d", "all", .false., "none", 2 +"gfs_phys", "ecan_acc", "ecan_acc", "fv3_history2d", "all", .false., "none", 2 +"gfs_phys", "etran_acc", "etran_acc", "fv3_history2d", "all", .false., "none", 2 +"gfs_phys", "edir_acc", "edir_acc", "fv3_history2d", "all", .false., "none", 2 +"gfs_phys", "wa_acc", "wa_acc", "fv3_history2d", "all", .false., "none", 2 +"gfs_sfc", "lfrac", "lfrac", "fv3_history2d", "all", .false., "none", 2 + "gfs_sfc", "crain", "crain", "fv3_history2d", "all", .false., "none", 2 "gfs_sfc", "tprcp", "tprcp", "fv3_history2d", "all", .false., "none", 2 "gfs_phys", "rainc", "cnvprcp", "fv3_history2d", "all", .false., "none", 2 diff --git a/parm/parm_fv3diag/diag_table.aero b/parm/parm_fv3diag/diag_table.aero new file mode 100644 index 0000000000..683c50cc4a --- /dev/null +++ b/parm/parm_fv3diag/diag_table.aero @@ -0,0 +1,28 @@ +### +# chemical tracers advected by FV3 +### +"gfs_dyn", "so2", "so2", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "so4", "so4", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "dms", "dms", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "msa", "msa", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "bc1", "bc1", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "bc2", "bc2", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "oc1", "oc1", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "oc2", "oc2", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "dust1", "dust1", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "dust2", "dust2", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "dust3", "dust3", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "dust4", "dust4", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "dust5", "dust5", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "seas1", "seas1", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "seas2", "seas2", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "seas3", "seas3", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "seas4", "seas4", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "seas5", "seas5", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "nh3", "nh3", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "nh4a", "nh4a", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "no3an1", "no3an1", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "no3an2", "no3an2", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "no3an3", "no3an3", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "pm25", "pm25", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "pm10", "pm10", "fv3_history", "all", .false., "none", 2 diff --git a/parm/parm_fv3diag/diag_table_cpl b/parm/parm_fv3diag/diag_table_cpl index e2e792a6b0..584718ce15 100644 --- a/parm/parm_fv3diag/diag_table_cpl +++ b/parm/parm_fv3diag/diag_table_cpl @@ -108,8 +108,8 @@ "gfs_dyn", "snowwat", "snmr", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "rainwat", "rwmr", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "graupel", "grle", "fv3_history", "all", .false., "none", 2 -#"gfs_dyn", "ice_nc", "nccice", "fv3_history", "all", .false., "none", 2 -#"gfs_dyn", "rain_nc", "nconrd", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "ice_nc", "nccice", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "rain_nc", "nconrd", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "o3mr", "o3mr", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "cld_amt", "cld_amt", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "delp", "dpres", "fv3_history", "all", .false., "none", 2 diff --git a/parm/parm_fv3diag/diag_table_da b/parm/parm_fv3diag/diag_table_da index 57c106531e..d34c0fd752 100644 --- a/parm/parm_fv3diag/diag_table_da +++ b/parm/parm_fv3diag/diag_table_da @@ -10,8 +10,8 @@ "gfs_dyn", "snowwat", "snmr", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "rainwat", "rwmr", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "graupel", "grle", "fv3_history", "all", .false., "none", 2 -#"gfs_dyn", "ice_nc", "nccice", "fv3_history", "all", .false., "none", 2 -#"gfs_dyn", "rain_nc", "nconrd", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "ice_nc", "nccice", "fv3_history", "all", .false., "none", 2 +"gfs_dyn", "rain_nc", "nconrd", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "o3mr", "o3mr", "fv3_history", "all", .false., "none", 2 #"gfs_dyn", "cld_amt", "cld_amt", "fv3_history", "all", .false., "none", 2 "gfs_dyn", "delp", "dpres", "fv3_history", "all", .false., "none", 2 diff --git a/parm/parm_fv3diag/field_table.aero b/parm/parm_fv3diag/field_table.aero new file mode 100644 index 0000000000..d917dd786c --- /dev/null +++ b/parm/parm_fv3diag/field_table.aero @@ -0,0 +1,127 @@ +# prognostic aerosols + "TRACER", "atmos_mod", "so2" + "longname", "so2 mixing ratio" + "units", "ppm" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "so4" + "longname", "sulfate mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "dms" + "longname", "DMS mixing ratio" + "units", "ppm" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "msa" + "longname", "msa mixing ratio" + "units", "ppm" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "bc1" + "longname", "hydrophobic black carbon mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "bc2" + "longname", "hydrophillic black carbon mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "oc1" + "longname", "hydrophobic organic carbon mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "oc2" + "longname", "hydrophillic organic carbon mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "dust1" + "longname", "fine dust1 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "dust2" + "longname", "fine dust2 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "dust3" + "longname", "coarse dust3 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "dust4" + "longname", "coarse dust4 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "dust5" + "longname", "coarse dust5 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "seas1" + "longname", "seasalt1 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "seas2" + "longname", "seasalt2 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "seas3" + "longname", "seasalt3 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "seas4" + "longname", "seasalt4 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "seas5" + "longname", "seasalt5 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "nh3" + "longname", "primary NH3 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "nh4a" + "longname", "primary NH4a mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "no3an1" + "longname", "primary NO3an1 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "no3an2" + "longname", "primary NO3an2 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "no3an3" + "longname", "primary NO3an3 mixing ratio" + "units", "ug/kg" + "tracer_usage", "chemistry" + "profile_type", "fixed", "surface_value=0.0" / +# diagnostic PM tracers + "TRACER", "atmos_mod", "pm25" + "longname", "primary PM25 mixing ratio" + "units", "ug/m3" + "tracer_usage", "chemistry", "type=diagnostic" + "profile_type", "fixed", "surface_value=0.0" / + "TRACER", "atmos_mod", "pm10" + "longname", "primary PM10 mixing ratio" + "units", "ug/m3" + "tracer_usage", "chemistry", "type=diagnostic" + "profile_type", "fixed", "surface_value=0.0" / diff --git a/parm/parm_fv3diag/field_table_thompson_noaero_tke b/parm/parm_fv3diag/field_table_thompson_noaero_tke new file mode 100644 index 0000000000..bc96278faa --- /dev/null +++ b/parm/parm_fv3diag/field_table_thompson_noaero_tke @@ -0,0 +1,65 @@ +# added by FRE: sphum must be present in atmos +# specific humidity for moist runs + "TRACER", "atmos_mod", "sphum" + "longname", "specific humidity" + "units", "kg/kg" + "profile_type", "fixed", "surface_value=3.e-6" / +# prognostic cloud water mixing ratio + "TRACER", "atmos_mod", "liq_wat" + "longname", "cloud water mixing ratio" + "units", "kg/kg" + "profile_type", "fixed", "surface_value=1.e30" / +# prognostic ice water mixing ratio + "TRACER", "atmos_mod", "ice_wat" + "longname", "cloud ice mixing ratio" + "units", "kg/kg" + "profile_type", "fixed", "surface_value=1.e30" / +# prognostic rain water mixing ratio + "TRACER", "atmos_mod", "rainwat" + "longname", "rain water mixing ratio" + "units", "kg/kg" + "profile_type", "fixed", "surface_value=1.e30" / +# prognostic snow water mixing ratio + "TRACER", "atmos_mod", "snowwat" + "longname", "snow water mixing ratio" + "units", "kg/kg" + "profile_type", "fixed", "surface_value=1.e30" / +# prognostic Grau water mixing ratio + "TRACER", "atmos_mod", "graupel" + "longname", "graupel mixing ratio" + "units", "kg/kg" + "profile_type", "fixed", "surface_value=1.e30" / +# prognostic cloud water number concentration - not for non-aerosol runs +# "TRACER", "atmos_mod", "water_nc" +# "longname", "cloud liquid water number concentration" +# "units", "/kg" +# "profile_type", "fixed", "surface_value=0.0" / +# prognostic cloud ice number concentration + "TRACER", "atmos_mod", "ice_nc" + "longname", "cloud ice water number concentration" + "units", "/kg" + "profile_type", "fixed", "surface_value=0.0" / +# prognostic rain number concentration + "TRACER", "atmos_mod", "rain_nc" + "longname", "rain number concentration" + "units", "/kg" + "profile_type", "fixed", "surface_value=0.0" / +# prognostic ozone mixing ratio tracer + "TRACER", "atmos_mod", "o3mr" + "longname", "ozone mixing ratio" + "units", "kg/kg" + "profile_type", "fixed", "surface_value=1.e30" / +# water- and ice-friendly aerosols (Thompson) - not for non-aerosol runs +# "TRACER", "atmos_mod", "liq_aero" +# "longname", "water-friendly aerosol number concentration" +# "units", "/kg" +# "profile_type", "fixed", "surface_value=0.0" / +# "TRACER", "atmos_mod", "ice_aero" +# "longname", "ice-friendly aerosol number concentration" +# "units", "/kg" +# "profile_type", "fixed", "surface_value=0.0" / +# prognostic subgrid scale turbulent kinetic energy + "TRACER", "atmos_mod", "sgs_tke" + "longname", "subgrid scale turbulent kinetic energy" + "units", "m2/s2" + "profile_type", "fixed", "surface_value=0.0" / diff --git a/scripts/exgfs_nceppost_cpl.sh b/scripts/exgfs_nceppost_cpl.sh index 487bf7d516..1cddb79e71 100755 --- a/scripts/exgfs_nceppost_cpl.sh +++ b/scripts/exgfs_nceppost_cpl.sh @@ -114,7 +114,7 @@ then # produce flux file, the default will be /nwprod/parm/gfs_cntrl.parm if [ $GRIBVERSION = 'grib2' ]; then # use grib2 nomonic table in product g2tmpl directory as default - export POSTGRB2TBL=${POSTGRB2TBL:-${G2TMPL_SRC}/params_grib2_tbl_new} + export POSTGRB2TBL=${POSTGRB2TBL:-${g2tmpl_ROOT}/share/params_grib2_tbl_new} export PostFlatFile=${PostFlatFile:-$PARMpost/postxconfig-NT-GFS-ANL.txt} export CTLFILE=$PARMpost/postcntrl_gfs_anl.xml fi diff --git a/scripts/exglobal_forecast.sh b/scripts/exglobal_forecast.sh index 1de88e1cc1..4c398e5055 100755 --- a/scripts/exglobal_forecast.sh +++ b/scripts/exglobal_forecast.sh @@ -101,7 +101,7 @@ confignamevarfornems=${confignamevarfornems:-'atm'} cpl=${cpl:-.false.} cplflx=${cplflx:-.false.} # default off,import from outside source cplwav=${cplwav:-.false.} # ? how to control 1-way/2-way? -cplchem=${cplchem:-.false.} # Chemistry model +cplchm=${cplchm:-.false.} # Chemistry model cplice=${cplice:-.false.} # ICE model OCNTIM=${OCNTIM:-1800} @@ -156,6 +156,7 @@ esac #no post determination set up for data atmosphere [[ $cplflx = .true. ]] && MOM6_postdet [[ $cplwav = .true. ]] && WW3_postdet [[ $cplice = .true. ]] && CICE_postdet +[[ $cplchm = .true. ]] && GOCART_postdet echo "MAIN: Post-determination set up of run type finished" echo "MAIN: Writing name lists and model configuration" @@ -168,7 +169,7 @@ esac #no namelist for data atmosphere [[ $cplflx = .true. ]] && MOM6_nml [[ $cplwav = .true. ]] && WW3_nml [[ $cplice = .true. ]] && CICE_nml -[[ $cplchem = .true. ]] && GOCART_rc +[[ $cplchm = .true. ]] && GOCART_rc case $RUN in 'data') DATM_model_configure;; diff --git a/scripts/exglobal_forecast.sh_gfsv16 b/scripts/exglobal_forecast.sh_gfsv16 deleted file mode 100755 index 6c1b5c0c3d..0000000000 --- a/scripts/exglobal_forecast.sh_gfsv16 +++ /dev/null @@ -1,1422 +0,0 @@ -#!/bin/ksh -################################################################################ -# UNIX Script Documentation Block -# Script name: exglobal_forecast.sh -# Script description: Runs a global FV3GFS model forecast -# -# Author: Fanglin Yang Org: NCEP/EMC Date: 2016-11-15 -# Abstract: This script runs a single GFS forecast with FV3 dynamical core. -# This script is created based on a C-shell script that GFDL wrote -# for the NGGPS Phase-II Dycore Comparison Project. -# -# Script history log: -# 2016-11-15 Fanglin Yang First Version. -# 2017-02-09 Rahul Mahajan Added warm start and restructured the code. -# 2017-03-10 Fanglin Yang Updated for running forecast on Cray. -# 2017-03-24 Fanglin Yang Updated to use NEMS FV3GFS with IPD4 -# 2017-05-24 Rahul Mahajan Updated for cycling with NEMS FV3GFS -# 2017-09-13 Fanglin Yang Updated for using GFDL MP and Write Component -# 2019-03-05 Rahul Mahajan Implemented IAU -# 2019-03-21 Fanglin Yang Add restart capability for running gfs fcst from a break point. -# 2019-12-12 Henrique Alves Added wave model blocks for coupled run -# 2020-01-31 Henrique Alves Added IAU capability for wave component -# 2020-06-02 Fanglin Yang restore restart capability when IAU is turned on. -# -# $Id$ -# -# Attributes: -# Language: Portable Operating System Interface (POSIX) Shell -# Machine: WCOSS-CRAY, Theia -################################################################################ - -# Set environment. -VERBOSE=${VERBOSE:-"YES"} -if [ $VERBOSE = "YES" ] ; then - echo $(date) EXECUTING $0 $* >&2 - set -x -fi - -machine=${machine:-"WCOSS_C"} -machine=$(echo $machine | tr '[a-z]' '[A-Z]') - -# Cycling and forecast hour specific parameters -CDUMPwave="${CDUMP}wave" -CASE=${CASE:-C768} -CDATE=${CDATE:-2017032500} -CDUMP=${CDUMP:-gdas} -FHMIN=${FHMIN:-0} -FHMAX=${FHMAX:-9} -FHOUT=${FHOUT:-3} -FHZER=${FHZER:-6} -FHCYC=${FHCYC:-24} -FHMAX_HF=${FHMAX_HF:-0} -FHOUT_HF=${FHOUT_HF:-1} -NSOUT=${NSOUT:-"-1"} -FDIAG=$FHOUT -if [ $FHMAX_HF -gt 0 -a $FHOUT_HF -gt 0 ]; then FDIAG=$FHOUT_HF; fi -WRITE_DOPOST=${WRITE_DOPOST:-".false."} -restart_interval=${restart_interval:-0} -rst_invt1=`echo $restart_interval |cut -d " " -f 1` - -PDY=$(echo $CDATE | cut -c1-8) -cyc=$(echo $CDATE | cut -c9-10) - -# Directories. -pwd=$(pwd) -NWPROD=${NWPROD:-${NWROOT:-$pwd}} -HOMEgfs=${HOMEgfs:-$NWPROD} -FIX_DIR=${FIX_DIR:-$HOMEgfs/fix} -FIX_AM=${FIX_AM:-$FIX_DIR/fix_am} -export FIX_AER=${FIX_AER:-$FIX_DIR/fix_aer} -export FIX_LUT=${FIX_LUT:-$FIX_DIR/fix_lut} -FIXfv3=${FIXfv3:-$FIX_DIR/fix_fv3_gmted2010} -DATA=${DATA:-$pwd/fv3tmp$$} # temporary running directory -ROTDIR=${ROTDIR:-$pwd} # rotating archive directory -ICSDIR=${ICSDIR:-$pwd} # cold start initial conditions -DMPDIR=${DMPDIR:-$pwd} # global dumps for seaice, snow and sst analysis - -# Model resolution specific parameters -DELTIM=${DELTIM:-225} -layout_x=${layout_x:-8} -layout_y=${layout_y:-16} -LEVS=${LEVS:-65} - -# Utilities -NCP=${NCP:-"/bin/cp -p"} -NLN=${NLN:-"/bin/ln -sf"} -NMV=${NMV:-"/bin/mv"} -SEND=${SEND:-"YES"} #move final result to rotating directory -ERRSCRIPT=${ERRSCRIPT:-'eval [[ $err = 0 ]]'} -KEEPDATA=${KEEPDATA:-"NO"} - -# Other options -MEMBER=${MEMBER:-"-1"} # -1: control, 0: ensemble mean, >0: ensemble member $MEMBER -ENS_NUM=${ENS_NUM:-1} # Single executable runs multiple members (e.g. GEFS) -PREFIX_ATMINC=${PREFIX_ATMINC:-""} # allow ensemble to use recentered increment - -# IAU options -DOIAU=${DOIAU:-"NO"} -IAUFHRS=${IAUFHRS:-0} -IAU_DELTHRS=${IAU_DELTHRS:-0} -IAU_OFFSET=${IAU_OFFSET:-0} - -# Model specific stuff -FCSTEXECDIR=${FCSTEXECDIR:-$HOMEgfs/sorc/fv3gfs.fd/NEMS/exe} -FCSTEXEC=${FCSTEXEC:-fv3_gfs.x} -PARM_FV3DIAG=${PARM_FV3DIAG:-$HOMEgfs/parm/parm_fv3diag} -PARM_POST=${PARM_POST:-$HOMEgfs/parm/post} - -# Wave coupling parameter defaults to false -cplwav=${cplwav:-.false.} - -# Model config options -APRUN_FV3=${APRUN_FV3:-${APRUN_FCST:-${APRUN:-""}}} -NTHREADS_FV3=${NTHREADS_FV3:-${NTHREADS_FCST:-${nth_fv3:-1}}} -cores_per_node=${cores_per_node:-${npe_node_max:-24}} -ntiles=${ntiles:-6} -NTASKS_FV3=${NTASKS_FV3:-$npe_fv3} - -TYPE=${TYPE:-"nh"} # choices: nh, hydro -MONO=${MONO:-"non-mono"} # choices: mono, non-mono -RUN_CCPP=${RUN_CCPP:-"NO"} - -QUILTING=${QUILTING:-".true."} -OUTPUT_GRID=${OUTPUT_GRID:-"gaussian_grid"} -OUTPUT_FILE=${OUTPUT_FILE:-"nemsio"} -WRITE_NEMSIOFLIP=${WRITE_NEMSIOFLIP:-".true."} -WRITE_FSYNCFLAG=${WRITE_FSYNCFLAG:-".true."} -affix="nemsio" -[[ "$OUTPUT_FILE" = "netcdf" ]] && affix="nc" - -rCDUMP=${rCDUMP:-$CDUMP} - -#------------------------------------------------------------------ -# setup the runtime environment -if [ $machine = "WCOSS_C" ] ; then - HUGEPAGES=${HUGEPAGES:-hugepages4M} - . $MODULESHOME/init/sh 2>/dev/null - module load iobuf craype-$HUGEPAGES 2>/dev/null - export MPICH_GNI_COLL_OPT_OFF=${MPICH_GNI_COLL_OPT_OFF:-MPI_Alltoallv} - export MKL_CBWR=AVX2 - export WRTIOBUF=${WRTIOBUF:-"4M"} - export NC_BLKSZ=${NC_BLKSZ:-"4M"} - export IOBUF_PARAMS="*nemsio:verbose:size=${WRTIOBUF},*:verbose:size=${NC_BLKSZ}" -fi - -#------------------------------------------------------- -if [ ! -d $ROTDIR ]; then mkdir -p $ROTDIR; fi -mkdata=NO -if [ ! -d $DATA ]; then - mkdata=YES - mkdir -p $DATA -fi -cd $DATA || exit 8 -mkdir -p $DATA/INPUT - -if [ $cplwav = ".true." ]; then - if [ $CDUMP = "gdas" ]; then - RSTDIR_WAVE=$ROTDIR/${CDUMP}.${PDY}/${cyc}/wave/restart - else - RSTDIR_WAVE=${RSTDIR_WAVE:-$ROTDIR/${CDUMP}.${PDY}/${cyc}/wave/restart} - fi - if [ ! -d $RSTDIR_WAVE ]; then mkdir -p $RSTDIR_WAVE ; fi - $NLN $RSTDIR_WAVE restart_wave -fi - -if [ $CDUMP = "gfs" -a $rst_invt1 -gt 0 ]; then - RSTDIR_ATM=${RSTDIR:-$ROTDIR}/${CDUMP}.${PDY}/${cyc}/atmos/RERUN_RESTART - if [ ! -d $RSTDIR_ATM ]; then mkdir -p $RSTDIR_ATM ; fi - $NLN $RSTDIR_ATM RESTART -else - mkdir -p $DATA/RESTART -fi - -#------------------------------------------------------- -# determine if restart IC exists to continue from a previous forecast -RERUN="NO" -filecount=$(find $RSTDIR_ATM -type f | wc -l) -if [ $CDUMP = "gfs" -a $rst_invt1 -gt 0 -a $FHMAX -gt $rst_invt1 -a $filecount -gt 10 ]; then - reverse=$(echo "${restart_interval[@]} " | tac -s ' ') - for xfh in $reverse ; do - yfh=$((xfh-(IAU_OFFSET/2))) - SDATE=$($NDATE +$yfh $CDATE) - PDYS=$(echo $SDATE | cut -c1-8) - cycs=$(echo $SDATE | cut -c9-10) - flag1=$RSTDIR_ATM/${PDYS}.${cycs}0000.coupler.res - flag2=$RSTDIR_ATM/coupler.res - if [ -s $flag1 ]; then - CDATE_RST=$SDATE - [[ $RERUN = "YES" ]] && break - mv $flag1 ${flag1}.old - if [ -s $flag2 ]; then mv $flag2 ${flag2}.old ;fi - RERUN="YES" - [[ $xfh = $rst_invt1 ]] && RERUN="NO" - fi - done -fi - -#------------------------------------------------------- -# member directory -if [ $MEMBER -lt 0 ]; then - prefix=$CDUMP - rprefix=$rCDUMP - memchar="" -else - prefix=enkf$CDUMP - rprefix=enkf$rCDUMP - memchar=mem$(printf %03i $MEMBER) -fi -memdir=$ROTDIR/${prefix}.$PDY/$cyc/atmos/$memchar -if [ ! -d $memdir ]; then mkdir -p $memdir; fi - -GDATE=$($NDATE -$assim_freq $CDATE) -gPDY=$(echo $GDATE | cut -c1-8) -gcyc=$(echo $GDATE | cut -c9-10) -gmemdir=$ROTDIR/${rprefix}.$gPDY/$gcyc/atmos/$memchar -sCDATE=$($NDATE -3 $CDATE) - -if [[ "$DOIAU" = "YES" ]]; then - sCDATE=$($NDATE -3 $CDATE) - sPDY=$(echo $sCDATE | cut -c1-8) - scyc=$(echo $sCDATE | cut -c9-10) - tPDY=$gPDY - tcyc=$gcyc -else - sCDATE=$CDATE - sPDY=$PDY - scyc=$cyc - tPDY=$sPDY - tcyc=$cyc -fi - -#------------------------------------------------------- -# initial conditions -warm_start=${warm_start:-".false."} -read_increment=${read_increment:-".false."} -res_latlon_dynamics="''" - -# Determine if this is a warm start or cold start -if [ -f $gmemdir/RESTART/${sPDY}.${scyc}0000.coupler.res ]; then - export warm_start=".true." -fi - -# turn IAU off for cold start -DOIAU_coldstart=${DOIAU_coldstart:-"NO"} -if [ $DOIAU = "YES" -a $warm_start = ".false." ] || [ $DOIAU_coldstart = "YES" -a $warm_start = ".true." ]; then - export DOIAU="NO" - echo "turning off IAU" - DOIAU_coldstart="YES" - IAU_OFFSET=0 - sCDATE=$CDATE - sPDY=$PDY - scyc=$cyc - tPDY=$sPDY - tcyc=$cyc -fi - -#------------------------------------------------------- -if [ $warm_start = ".true." -o $RERUN = "YES" ]; then -#------------------------------------------------------- -#............................. - if [ $RERUN = "NO" ]; then -#............................. - - # Link all (except sfc_data) restart files from $gmemdir - for file in $(ls $gmemdir/RESTART/${sPDY}.${scyc}0000.*.nc); do - file2=$(echo $(basename $file)) - file2=$(echo $file2 | cut -d. -f3-) # remove the date from file - fsuf=$(echo $file2 | cut -d. -f1) - if [ $fsuf != "sfc_data" ]; then - $NLN $file $DATA/INPUT/$file2 - fi - done - - # Link sfcanl_data restart files from $memdir - for file in $(ls $memdir/RESTART/${sPDY}.${scyc}0000.*.nc); do - file2=$(echo $(basename $file)) - file2=$(echo $file2 | cut -d. -f3-) # remove the date from file - fsufanl=$(echo $file2 | cut -d. -f1) - if [ $fsufanl = "sfcanl_data" ]; then - file2=$(echo $file2 | sed -e "s/sfcanl_data/sfc_data/g") - $NLN $file $DATA/INPUT/$file2 - fi - done - - # Need a coupler.res when doing IAU - if [ $DOIAU = "YES" ]; then - rm -f $DATA/INPUT/coupler.res - cat >> $DATA/INPUT/coupler.res << EOF - 2 (Calendar: no_calendar=0, thirty_day_months=1, julian=2, gregorian=3, noleap=4) - ${gPDY:0:4} ${gPDY:4:2} ${gPDY:6:2} ${gcyc} 0 0 Model start time: year, month, day, hour, minute, second - ${sPDY:0:4} ${sPDY:4:2} ${sPDY:6:2} ${scyc} 0 0 Current model time: year, month, day, hour, minute, second -EOF - fi - - # Link increments - if [ $DOIAU = "YES" ]; then - for i in $(echo $IAUFHRS | sed "s/,/ /g" | rev); do - incfhr=$(printf %03i $i) - if [ $incfhr = "006" ]; then - increment_file=$memdir/${CDUMP}.t${cyc}z.${PREFIX_ATMINC}atminc.nc - else - increment_file=$memdir/${CDUMP}.t${cyc}z.${PREFIX_ATMINC}atmi${incfhr}.nc - fi - if [ ! -f $increment_file ]; then - echo "ERROR: DOIAU = $DOIAU, but missing increment file for fhr $incfhr at $increment_file" - echo "Abort!" - exit 1 - fi - $NLN $increment_file $DATA/INPUT/fv_increment$i.nc - IAU_INC_FILES="'fv_increment$i.nc',$IAU_INC_FILES" - done - read_increment=".false." - res_latlon_dynamics="" - else - increment_file=$memdir/${CDUMP}.t${cyc}z.${PREFIX_INC}atminc.nc - if [ -f $increment_file ]; then - $NLN $increment_file $DATA/INPUT/fv_increment.nc - read_increment=".true." - res_latlon_dynamics="fv_increment.nc" - fi - fi - -#............................. - else ##RERUN - - export warm_start=".true." - PDYT=$(echo $CDATE_RST | cut -c1-8) - cyct=$(echo $CDATE_RST | cut -c9-10) - for file in $(ls $RSTDIR_ATM/${PDYT}.${cyct}0000.*); do - file2=$(echo $(basename $file)) - file2=$(echo $file2 | cut -d. -f3-) - $NLN $file $DATA/INPUT/$file2 - done - - hour_rst=`$NHOUR $CDATE_RST $CDATE` - IAU_FHROT=$((IAU_OFFSET+hour_rst)) - if [ $DOIAU = "YES" ]; then - IAUFHRS=-1 - IAU_DELTHRS=0 - IAU_INC_FILES="''" - fi - - rst_list_rerun="" - xfh=$restart_interval_gfs - while [ $xfh -le $FHMAX_GFS ]; do - rst_list_rerun="$rst_list_rerun $xfh" - xfh=$((xfh+restart_interval_gfs)) - done - restart_interval="$rst_list_rerun" - - fi -#............................. - -else ## cold start - - for file in $(ls $memdir/INPUT/*.nc); do - file2=$(echo $(basename $file)) - fsuf=$(echo $file2 | cut -c1-3) - if [ $fsuf = "gfs" -o $fsuf = "sfc" ]; then - $NLN $file $DATA/INPUT/$file2 - fi - done - -#------------------------------------------------------- -fi -#------------------------------------------------------- - -nfiles=$(ls -1 $DATA/INPUT/* | wc -l) -if [ $nfiles -le 0 ]; then - echo "Initial conditions must exist in $DATA/INPUT, ABORT!" - msg="Initial conditions must exist in $DATA/INPUT, ABORT!" - postmsg "$jlogfile" "$msg" - exit 1 -fi - -# If doing IAU, change forecast hours -if [[ "$DOIAU" = "YES" ]]; then - FHMAX=$((FHMAX+6)) - if [ $FHMAX_HF -gt 0 ]; then - FHMAX_HF=$((FHMAX_HF+6)) - fi -fi - -#-------------------------------------------------------------------------- -# Grid and orography data -for n in $(seq 1 $ntiles); do - $NLN $FIXfv3/$CASE/${CASE}_grid.tile${n}.nc $DATA/INPUT/${CASE}_grid.tile${n}.nc - $NLN $FIXfv3/$CASE/${CASE}_oro_data.tile${n}.nc $DATA/INPUT/oro_data.tile${n}.nc -done -$NLN $FIXfv3/$CASE/${CASE}_mosaic.nc $DATA/INPUT/grid_spec.nc - -# GFS standard input data -IAER=${IAER:-111} -ICO2=${ICO2:-2} - -if [ ${new_o3forc:-YES} = YES ]; then - O3FORC=ozprdlos_2015_new_sbuvO3_tclm15_nuchem.f77 -else - O3FORC=global_o3prdlos.f77 -fi -H2OFORC=${H2OFORC:-"global_h2o_pltc.f77"} -$NLN $FIX_AM/${O3FORC} $DATA/global_o3prdlos.f77 -$NLN $FIX_AM/${H2OFORC} $DATA/global_h2oprdlos.f77 -$NLN $FIX_AM/global_solarconstant_noaa_an.txt $DATA/solarconstant_noaa_an.txt -$NLN $FIX_AM/global_sfc_emissivity_idx.txt $DATA/sfc_emissivity_idx.txt - -## merra2 aerosol climo -for n in 01 02 03 04 05 06 07 08 09 10 11 12; do -$NLN $FIX_AER/merra2.aerclim.2003-2014.m${n}.nc $DATA/aeroclim.m${n}.nc -done -$NLN $FIX_LUT/optics_BC.v1_3.dat $DATA/optics_BC.dat -$NLN $FIX_LUT/optics_OC.v1_3.dat $DATA/optics_OC.dat -$NLN $FIX_LUT/optics_DU.v15_3.dat $DATA/optics_DU.dat -$NLN $FIX_LUT/optics_SS.v3_3.dat $DATA/optics_SS.dat -$NLN $FIX_LUT/optics_SU.v1_3.dat $DATA/optics_SU.dat - -$NLN $FIX_AM/global_co2historicaldata_glob.txt $DATA/co2historicaldata_glob.txt -$NLN $FIX_AM/co2monthlycyc.txt $DATA/co2monthlycyc.txt -if [ $ICO2 -gt 0 ]; then - for file in $(ls $FIX_AM/fix_co2_proj/global_co2historicaldata*) ; do - $NLN $file $DATA/$(echo $(basename $file) | sed -e "s/global_//g") - done -fi - -$NLN $FIX_AM/global_climaeropac_global.txt $DATA/aerosol.dat -if [ $IAER -gt 0 ] ; then - for file in $(ls $FIX_AM/global_volcanic_aerosols*) ; do - $NLN $file $DATA/$(echo $(basename $file) | sed -e "s/global_//g") - done -fi - -#-------------wavewave---------------------- -if [ $cplwav = ".true." ]; then - - for file in $(ls $COMINwave/rundata/rmp_src_to_dst_conserv_*) ; do - $NLN $file $DATA/ - done - $NLN $COMINwave/rundata/ww3_multi.${CDUMPwave}${WAV_MEMBER}.${cycle}.inp $DATA/ww3_multi.inp - - array=($WAVECUR_FID $WAVEICE_FID $WAVEWND_FID $waveuoutpGRD $waveGRD $waveesmfGRD $wavesbsGRD $wavepostGRD $waveinterpGRD) - grdALL=`printf "%s\n" "${array[@]}" | sort -u | tr '\n' ' '` - - for wavGRD in ${grdALL}; do - $NLN $COMINwave/rundata/${CDUMPwave}.mod_def.$wavGRD $DATA/mod_def.$wavGRD - done - - export WAVHCYC=${WAVHCYC:-6} - export WRDATE=`$NDATE -${WAVHCYC} $CDATE` - export WRPDY=`echo $WRDATE | cut -c1-8` - export WRcyc=`echo $WRDATE | cut -c9-10` - export WRDIR=${ROTDIR}/${CDUMPRSTwave}.${WRPDY}/${WRcyc}/wave/restart - export datwave=$COMOUTwave/rundata - export wavprfx=${CDUMPwave}${WAV_MEMBER} - - for wavGRD in $waveGRD ; do - if [ $RERUN = "NO" ]; then - if [ ! -f ${WRDIR}/${sPDY}.${scyc}0000.restart.${wavGRD} ]; then - echo "WARNING: NON-FATAL ERROR wave IC is missing, will start from rest" - fi - $NLN ${WRDIR}/${sPDY}.${scyc}0000.restart.${wavGRD} $DATA/restart.${wavGRD} - else - if [ ! -f ${RSTDIR_WAVE}/${PDYT}.${cyct}0000.restart.${wavGRD} ]; then - echo "WARNING: NON-FATAL ERROR wave IC is missing, will start from rest" - fi - $NLN ${RSTDIR_WAVE}/${PDYT}.${cyct}0000.restart.${wavGRD} $DATA/restart.${wavGRD} - fi - eval $NLN $datwave/${wavprfx}.log.${wavGRD}.${PDY}${cyc} log.${wavGRD} - done - - if [ "$WW3ICEINP" = "YES" ]; then - wavicefile=$COMINwave/rundata/${CDUMPwave}.${WAVEICE_FID}.${cycle}.ice - if [ ! -f $wavicefile ]; then - echo "ERROR: WW3ICEINP = ${WW3ICEINP}, but missing ice file" - echo "Abort!" - exit 1 - fi - $NLN ${wavicefile} $DATA/ice.${WAVEICE_FID} - fi - - if [ "$WW3CURINP" = "YES" ]; then - wavcurfile=$COMINwave/rundata/${CDUMPwave}.${WAVECUR_FID}.${cycle}.cur - if [ ! -f $wavcurfile ]; then - echo "ERROR: WW3CURINP = ${WW3CURINP}, but missing current file" - echo "Abort!" - exit 1 - fi - $NLN $wavcurfile $DATA/current.${WAVECUR_FID} - fi - - # Link output files - cd $DATA - eval $NLN $datwave/${wavprfx}.log.mww3.${PDY}${cyc} log.mww3 - - # Loop for gridded output (uses FHINC) - fhr=$FHMIN_WAV - while [ $fhr -le $FHMAX_WAV ]; do - YMDH=`$NDATE $fhr $CDATE` - YMD=$(echo $YMDH | cut -c1-8) - HMS="$(echo $YMDH | cut -c9-10)0000" - for wavGRD in ${waveGRD} ; do - eval $NLN $datwave/${wavprfx}.out_grd.${wavGRD}.${YMD}.${HMS} ${YMD}.${HMS}.out_grd.${wavGRD} - done - FHINC=$FHOUT_WAV - if [ $FHMAX_HF_WAV -gt 0 -a $FHOUT_HF_WAV -gt 0 -a $fhr -lt $FHMAX_HF_WAV ]; then - FHINC=$FHOUT_HF_WAV - fi - fhr=$((fhr+FHINC)) - done - - # Loop for point output (uses DTPNT) - fhr=$FHMIN_WAV - while [ $fhr -le $FHMAX_WAV ]; do - YMDH=`$NDATE $fhr $CDATE` - YMD=$(echo $YMDH | cut -c1-8) - HMS="$(echo $YMDH | cut -c9-10)0000" - eval $NLN $datwave/${wavprfx}.out_pnt.${waveuoutpGRD}.${YMD}.${HMS} ${YMD}.${HMS}.out_pnt.${waveuoutpGRD} - FHINC=$FHINCP_WAV - fhr=$((fhr+FHINC)) - done - -fi #cplwav=true -#-------------wavewave---------------------- - -# inline post fix files -if [ $WRITE_DOPOST = ".true." ]; then - $NLN $PARM_POST/post_tag_gfs${LEVS} $DATA/itag - $NLN $PARM_POST/postxconfig-NT-GFS-TWO.txt $DATA/postxconfig-NT.txt - $NLN $PARM_POST/postxconfig-NT-GFS-F00-TWO.txt $DATA/postxconfig-NT_FH00.txt - $NLN $PARM_POST/params_grib2_tbl_new $DATA/params_grib2_tbl_new -fi -#------------------------------------------------------------------ - -# changeable parameters -# dycore definitions -res=$(echo $CASE |cut -c2-5) -resp=$((res+1)) -npx=$resp -npy=$resp -npz=$((LEVS-1)) -io_layout=${io_layout:-"1,1"} -#ncols=$(( (${npx}-1)*(${npy}-1)*3/2 )) - -# spectral truncation and regular grid resolution based on FV3 resolution -JCAP_CASE=$((2*res-2)) -LONB_CASE=$((4*res)) -LATB_CASE=$((2*res)) - -JCAP=${JCAP:-$JCAP_CASE} -LONB=${LONB:-$LONB_CASE} -LATB=${LATB:-$LATB_CASE} - -LONB_IMO=${LONB_IMO:-$LONB_CASE} -LATB_JMO=${LATB_JMO:-$LATB_CASE} - -# Fix files -FNGLAC=${FNGLAC:-"$FIX_AM/global_glacier.2x2.grb"} -FNMXIC=${FNMXIC:-"$FIX_AM/global_maxice.2x2.grb"} -FNTSFC=${FNTSFC:-"$FIX_AM/RTGSST.1982.2012.monthly.clim.grb"} -FNSNOC=${FNSNOC:-"$FIX_AM/global_snoclim.1.875.grb"} -FNZORC=${FNZORC:-"igbp"} -FNALBC2=${FNALBC2:-"$FIX_AM/global_albedo4.1x1.grb"} -FNAISC=${FNAISC:-"$FIX_AM/CFSR.SEAICE.1982.2012.monthly.clim.grb"} -FNTG3C=${FNTG3C:-"$FIX_AM/global_tg3clim.2.6x1.5.grb"} -FNVEGC=${FNVEGC:-"$FIX_AM/global_vegfrac.0.144.decpercent.grb"} -FNMSKH=${FNMSKH:-"$FIX_AM/global_slmask.t1534.3072.1536.grb"} -FNVMNC=${FNVMNC:-"$FIX_AM/global_shdmin.0.144x0.144.grb"} -FNVMXC=${FNVMXC:-"$FIX_AM/global_shdmax.0.144x0.144.grb"} -FNSLPC=${FNSLPC:-"$FIX_AM/global_slope.1x1.grb"} -FNALBC=${FNALBC:-"$FIX_AM/global_snowfree_albedo.bosu.t${JCAP}.${LONB}.${LATB}.rg.grb"} -FNVETC=${FNVETC:-"$FIX_AM/global_vegtype.igbp.t${JCAP}.${LONB}.${LATB}.rg.grb"} -FNSOTC=${FNSOTC:-"$FIX_AM/global_soiltype.statsgo.t${JCAP}.${LONB}.${LATB}.rg.grb"} -FNABSC=${FNABSC:-"$FIX_AM/global_mxsnoalb.uariz.t${JCAP}.${LONB}.${LATB}.rg.grb"} -FNSMCC=${FNSMCC:-"$FIX_AM/global_soilmgldas.statsgo.t${JCAP}.${LONB}.${LATB}.grb"} - -# If the appropriate resolution fix file is not present, use the highest resolution available (T1534) -[[ ! -f $FNALBC ]] && FNALBC="$FIX_AM/global_snowfree_albedo.bosu.t1534.3072.1536.rg.grb" -[[ ! -f $FNVETC ]] && FNVETC="$FIX_AM/global_vegtype.igbp.t1534.3072.1536.rg.grb" -[[ ! -f $FNSOTC ]] && FNSOTC="$FIX_AM/global_soiltype.statsgo.t1534.3072.1536.rg.grb" -[[ ! -f $FNABSC ]] && FNABSC="$FIX_AM/global_mxsnoalb.uariz.t1534.3072.1536.rg.grb" -[[ ! -f $FNSMCC ]] && FNSMCC="$FIX_AM/global_soilmgldas.statsgo.t1534.3072.1536.grb" - -# NSST Options -# nstf_name contains the NSST related parameters -# nstf_name(1) : NST_MODEL (NSST Model) : 0 = OFF, 1 = ON but uncoupled, 2 = ON and coupled -# nstf_name(2) : NST_SPINUP : 0 = OFF, 1 = ON, -# nstf_name(3) : NST_RESV (Reserved, NSST Analysis) : 0 = OFF, 1 = ON -# nstf_name(4) : ZSEA1 (in mm) : 0 -# nstf_name(5) : ZSEA2 (in mm) : 0 -# nst_anl : .true. or .false., NSST analysis over lake -NST_MODEL=${NST_MODEL:-0} -NST_SPINUP=${NST_SPINUP:-0} -NST_RESV=${NST_RESV-0} -ZSEA1=${ZSEA1:-0} -ZSEA2=${ZSEA2:-0} -nstf_name=${nstf_name:-"$NST_MODEL,$NST_SPINUP,$NST_RESV,$ZSEA1,$ZSEA2"} -nst_anl=${nst_anl:-".false."} - - -# blocking factor used for threading and general physics performance -#nyblocks=`expr \( $npy - 1 \) \/ $layout_y ` -#nxblocks=`expr \( $npx - 1 \) \/ $layout_x \/ 32` -#if [ $nxblocks -le 0 ]; then nxblocks=1 ; fi -blocksize=${blocksize:-32} - -# the pre-conditioning of the solution -# =0 implies no pre-conditioning -# >0 means new adiabatic pre-conditioning -# <0 means older adiabatic pre-conditioning -na_init=${na_init:-1} -[[ $warm_start = ".true." ]] && na_init=0 - -# variables for controlling initialization of NCEP/NGGPS ICs -filtered_terrain=${filtered_terrain:-".true."} -gfs_dwinds=${gfs_dwinds:-".true."} - -# various debug options -no_dycore=${no_dycore:-".false."} -dycore_only=${adiabatic:-".false."} -chksum_debug=${chksum_debug:-".false."} -print_freq=${print_freq:-6} - -if [ ${TYPE} = "nh" ]; then # non-hydrostatic options - - hydrostatic=".false." - phys_hydrostatic=".false." # enable heating in hydrostatic balance in non-hydrostatic simulation - use_hydro_pressure=".false." # use hydrostatic pressure for physics - if [ $warm_start = ".true." ]; then - make_nh=".false." # restarts contain non-hydrostatic state - else - make_nh=".true." # re-initialize non-hydrostatic state - fi - -else # hydrostatic options - - hydrostatic=".true." - phys_hydrostatic=".false." # ignored when hydrostatic = T - use_hydro_pressure=".false." # ignored when hydrostatic = T - make_nh=".false." # running in hydrostatic mode - -fi - -# Conserve total energy as heat globally -consv_te=${consv_te:-1.} # range 0.-1., 1. will restore energy to orig. val. before physics - -# time step parameters in FV3 -k_split=${k_split:-2} -n_split=${n_split:-6} - -if [ $(echo $MONO | cut -c-4) = "mono" ]; then # monotonic options - - d_con=${d_con_mono:-"0."} - do_vort_damp=".false." - if [ ${TYPE} = "nh" ]; then # non-hydrostatic - hord_mt=${hord_mt_nh_mono:-"10"} - hord_xx=${hord_xx_nh_mono:-"10"} - else # hydrostatic - hord_mt=${hord_mt_hydro_mono:-"10"} - hord_xx=${hord_xx_hydro_mono:-"10"} - fi - -else # non-monotonic options - - d_con=${d_con_nonmono:-"1."} - do_vort_damp=".true." - if [ ${TYPE} = "nh" ]; then # non-hydrostatic - hord_mt=${hord_mt_nh_nonmono:-"5"} - hord_xx=${hord_xx_nh_nonmono:-"5"} - else # hydrostatic - hord_mt=${hord_mt_hydro_nonmono:-"10"} - hord_xx=${hord_xx_hydro_nonmono:-"10"} - fi - -fi - -if [ $(echo $MONO | cut -c-4) != "mono" -a $TYPE = "nh" ]; then - vtdm4=${vtdm4_nh_nonmono:-"0.06"} -else - vtdm4=${vtdm4:-"0.05"} -fi - -if [ $warm_start = ".true." ]; then # warm start from restart file - - nggps_ic=".false." - ncep_ic=".false." - external_ic=".false." - mountain=".true." - if [ $read_increment = ".true." ]; then # add increment on the fly to the restarts - res_latlon_dynamics="fv_increment.nc" - else - res_latlon_dynamics='""' - fi - -else # CHGRES'd GFS analyses - - nggps_ic=${nggps_ic:-".true."} - ncep_ic=${ncep_ic:-".false."} - external_ic=".true." - mountain=".false." - read_increment=".false." - res_latlon_dynamics='""' - -fi - -# Stochastic Physics Options -if [ ${SET_STP_SEED:-"YES"} = "YES" ]; then - ISEED_SKEB=$((CDATE*1000 + MEMBER*10 + 1)) - ISEED_SHUM=$((CDATE*1000 + MEMBER*10 + 2)) - ISEED_SPPT=$((CDATE*1000 + MEMBER*10 + 3)) -else - ISEED=${ISEED:-0} -fi -DO_SKEB=${DO_SKEB:-"NO"} -DO_SPPT=${DO_SPPT:-"NO"} -DO_SHUM=${DO_SHUM:-"NO"} - -if [ $DO_SKEB = "YES" ]; then - do_skeb=".true." -fi -if [ $DO_SHUM = "YES" ]; then - do_shum=".true." -fi -if [ $DO_SPPT = "YES" ]; then - do_sppt=".true." -fi - -# copy over the tables -DIAG_TABLE=${DIAG_TABLE:-$PARM_FV3DIAG/diag_table} -DATA_TABLE=${DATA_TABLE:-$PARM_FV3DIAG/data_table} -FIELD_TABLE=${FIELD_TABLE:-$PARM_FV3DIAG/field_table} - -# build the diag_table with the experiment name and date stamp -if [ $DOIAU = "YES" ]; then -cat > diag_table << EOF -FV3 Forecast -${gPDY:0:4} ${gPDY:4:2} ${gPDY:6:2} ${gcyc} 0 0 -EOF -cat $DIAG_TABLE >> diag_table -else -cat > diag_table << EOF -FV3 Forecast -${sPDY:0:4} ${sPDY:4:2} ${sPDY:6:2} ${scyc} 0 0 -EOF -cat $DIAG_TABLE >> diag_table -fi - -$NCP $DATA_TABLE data_table -$NCP $FIELD_TABLE field_table - -# copy CCN_ACTIVATE.BIN for Thompson microphysics -if [ $RUN_CCPP = "YES" ]; then -if [ "$CCPP_SUITE" = 'FV3_GSD_v0' -o "$CCPP_SUITE" = 'FV3_GSD_noah' -o "$CCPP_SUITE" = 'FV3_GFS_v16_thompson' ]; then - $NLN $FIX_AM/CCN_ACTIVATE.BIN CCN_ACTIVATE.BIN - $NLN $FIX_AM/freezeH2O.dat freezeH2O.dat - $NLN $FIX_AM/qr_acr_qg.dat qr_acr_qg.dat - $NLN $FIX_AM/qr_acr_qs.dat qr_acr_qs.dat -fi -fi - -# JKH copy yaml file over -cp $HOMEgfs/sorc/fv3gfs.fd/tests/parm/fd_nems.yaml fd_nems.yaml - -#------------------------------------------------------------------ -rm -f nems.configure - -if [ $cplwav = ".true." ]; then -#### ww3 version of nems.configure - -# Switch on cpl flag - cpl=.true. - -NTASKS_FV3m1=$((NTASKS_FV3-1)) -atm_petlist_bounds=" 0 $((NTASKS_FV3-1))" -wav_petlist_bounds=" $((NTASKS_FV3)) $((NTASKS_FV3m1+npe_wav))" -### atm_petlist_bounds=" 0 1511" -### atm_petlist_bounds=$atm_petlist_bounds -### wav_petlist_bounds="1512 1691" -### wav_petlist_bounds=$wav_petlist_bounds - coupling_interval_sec=${coupling_interval_sec:-1800} - rm -f nems.configure -cat > nems.configure < WAV :SrcTermProcessing=0:TermOrder=SrcSeq - WAV - @ -:: -EOF -else -#### fv3 standalone version of nems.configure -cat > nems.configure < model_configure < input.nml <> input.nml << EOF - iovr = ${iovr:-"3"} - ltaerosol = ${ltaerosol:-".false."} - lradar = ${lradar:-".false."} - ttendlim = ${ttendlim:-"0.005"} - oz_phys = ${oz_phys:-".false."} - oz_phys_2015 = ${oz_phys_2015:-".true."} - lsoil_lsm = ${lsoil_lsm:-"4"} - do_mynnedmf = ${do_mynnedmf:-".false."} - do_mynnsfclay = ${do_mynnsfclay:-".false."} - icloud_bl = ${icloud_bl:-"1"} - bl_mynn_edmf = ${bl_mynn_edmf:-"1"} - bl_mynn_tkeadvect = ${bl_mynn_tkeadvect:-".true."} - bl_mynn_edmf_mom = ${bl_mynn_edmf_mom:-"1"} - min_lakeice = ${min_lakeice:-"0.15"} - min_seaice = ${min_seaice:-"0.15"} -EOF -else - cat >> input.nml << EOF - iovr_lw = ${iovr_lw:-"3"} - iovr_sw = ${iovr_sw:-"3"} -EOF -fi - -# Add namelist for IAU -if [ $DOIAU = "YES" ]; then - cat >> input.nml << EOF - iaufhrs = ${IAUFHRS} - iau_delthrs = ${IAU_DELTHRS} - iau_inc_files= ${IAU_INC_FILES} - iau_drymassfixer = .false. -EOF -fi - -cat >> input.nml <> input.nml - -cat >> input.nml <> input.nml -if [ $MEMBER -gt 0 ]; then - - cat >> input.nml << EOF -&nam_stochy -EOF - - if [ $DO_SKEB = "YES" ]; then - cat >> input.nml << EOF - skeb = $SKEB - iseed_skeb = ${ISEED_SKEB:-$ISEED} - skeb_tau = ${SKEB_TAU:-"-999."} - skeb_lscale = ${SKEB_LSCALE:-"-999."} - skebnorm = ${SKEBNORM:-"1"} - skeb_npass = ${SKEB_nPASS:-"30"} - skeb_vdof = ${SKEB_VDOF:-"5"} -EOF - fi - - if [ $DO_SHUM = "YES" ]; then - cat >> input.nml << EOF - shum = $SHUM - iseed_shum = ${ISEED_SHUM:-$ISEED} - shum_tau = ${SHUM_TAU:-"-999."} - shum_lscale = ${SHUM_LSCALE:-"-999."} -EOF - fi - - if [ $DO_SPPT = "YES" ]; then - cat >> input.nml << EOF - sppt = $SPPT - iseed_sppt = ${ISEED_SPPT:-$ISEED} - sppt_tau = ${SPPT_TAU:-"-999."} - sppt_lscale = ${SPPT_LSCALE:-"-999."} - sppt_logit = ${SPPT_LOGIT:-".true."} - sppt_sfclimit = ${SPPT_SFCLIMIT:-".true."} - use_zmtnblck = ${use_zmtnblck:-".true."} -EOF - fi - - cat >> input.nml << EOF - $nam_stochy_nml -/ -EOF - - - cat >> input.nml << EOF -&nam_sfcperts - $nam_sfcperts_nml -/ -EOF - -else - - cat >> input.nml << EOF -&nam_stochy -/ -&nam_sfcperts -/ -EOF - -fi - - -#------------------------------------------------------------------ -# make symbolic links to write forecast files directly in memdir -cd $DATA -if [ $QUILTING = ".true." -a $OUTPUT_GRID = "gaussian_grid" ]; then - fhr=$FHMIN - while [ $fhr -le $FHMAX ]; do - FH3=$(printf %03i $fhr) - FH2=$(printf %02i $fhr) - atmi=atmf${FH3}.$affix - sfci=sfcf${FH3}.$affix - logi=logf${FH3} - pgbi=GFSPRS.GrbF${FH2} - flxi=GFSFLX.GrbF${FH2} - atmo=$memdir/${CDUMP}.t${cyc}z.atmf${FH3}.$affix - sfco=$memdir/${CDUMP}.t${cyc}z.sfcf${FH3}.$affix - logo=$memdir/${CDUMP}.t${cyc}z.logf${FH3}.txt - pgbo=$memdir/${CDUMP}.t${cyc}z.master.grb2f${FH3} - flxo=$memdir/${CDUMP}.t${cyc}z.sfluxgrbf${FH3}.grib2 - eval $NLN $atmo $atmi - eval $NLN $sfco $sfci - eval $NLN $logo $logi - if [ $WRITE_DOPOST = ".true." ]; then - eval $NLN $pgbo $pgbi - eval $NLN $flxo $flxi - fi - FHINC=$FHOUT - if [ $FHMAX_HF -gt 0 -a $FHOUT_HF -gt 0 -a $fhr -lt $FHMAX_HF ]; then - FHINC=$FHOUT_HF - fi - fhr=$((fhr+FHINC)) - done -else - for n in $(seq 1 $ntiles); do - eval $NLN nggps2d.tile${n}.nc $memdir/nggps2d.tile${n}.nc - eval $NLN nggps3d.tile${n}.nc $memdir/nggps3d.tile${n}.nc - eval $NLN grid_spec.tile${n}.nc $memdir/grid_spec.tile${n}.nc - eval $NLN atmos_static.tile${n}.nc $memdir/atmos_static.tile${n}.nc - eval $NLN atmos_4xdaily.tile${n}.nc $memdir/atmos_4xdaily.tile${n}.nc - done -fi - -# Copy namelist file #JKH -$NCP input.nml $memdir - -# Copy model_configure file #JKH -$NCP model_configure $memdir - -#------------------------------------------------------------------ -# run the executable - -$NCP $FCSTEXECDIR/$FCSTEXEC $DATA/. -export OMP_NUM_THREADS=$NTHREADS_FV3 -eval $APRUN_FV3 $DATA/$FCSTEXEC 1>&1 2>&2 -export ERR=$? -export err=$ERR -$ERRSCRIPT || exit $err - -#------------------------------------------------------------------ -if [ $SEND = "YES" ]; then - - # Copy gdas and enkf member restart files - if [ $CDUMP = "gdas" -a $rst_invt1 -gt 0 ]; then - cd $DATA/RESTART - mkdir -p $memdir/RESTART - for rst_int in $restart_interval ; do - if [ $rst_int -ge 0 ]; then - RDATE=$($NDATE +$rst_int $CDATE) - rPDY=$(echo $RDATE | cut -c1-8) - rcyc=$(echo $RDATE | cut -c9-10) - for file in $(ls ${rPDY}.${rcyc}0000.*) ; do - $NCP $file $memdir/RESTART/$file - done - fi - done - if [ $DOIAU = "YES" ] || [ $DOIAU_coldstart = "YES" ]; then - # if IAU is on, save restart at start of IAU window - rst_iau=$(( ${IAU_OFFSET} - (${IAU_DELTHRS}/2) )) - if [ $rst_iau -lt 0 ];then - rst_iau=$(( (${IAU_DELTHRS}) - ${IAU_OFFSET} )) - fi - RDATE=$($NDATE +$rst_iau $CDATE) - rPDY=$(echo $RDATE | cut -c1-8) - rcyc=$(echo $RDATE | cut -c9-10) - for file in $(ls ${rPDY}.${rcyc}0000.*) ; do - $NCP $file $memdir/RESTART/$file - done - fi - fi -fi - -#------------------------------------------------------------------ -# Clean up before leaving -if [ $mkdata = "YES" ]; then rm -rf $DATA; fi - -#------------------------------------------------------------------ -set +x -if [ $VERBOSE = "YES" ] ; then - echo $(date) EXITING $0 with return code $err >&2 -fi -exit 0 diff --git a/sorc/build_all.sh b/sorc/build_all.sh index f063f7799b..117d9bc78f 100755 --- a/sorc/build_all.sh +++ b/sorc/build_all.sh @@ -59,6 +59,7 @@ source ./machine-setup.sh > /dev/null 2>&1 . ./partial_build.sh $@ if [ $target = jet ]; then + Build_gsi=false Build_gldas=false Build_gfs_util=false Build_ww3_prepost=false diff --git a/sorc/build_ufs.sh b/sorc/build_ufs.sh index 33db2bc0af..90c1d06828 100755 --- a/sorc/build_ufs.sh +++ b/sorc/build_ufs.sh @@ -3,19 +3,13 @@ set -eux # Build ATMW by default APP="ATMW" -CCPP_SUITES="FV3_GFS_v16,FV3_GFS_v16_RRTMGP,FV3_GFS_v16_ugwpv1,FV3_RAP_noah_sfcdiff_unified_ugwp,FV3_RAP_cires_ugwp" +CCPP_SUITES="FV3_GFS_v16,FV3_GFS_v16_ugwpv1,FV3_RAP_noah_sfcdiff_unified_ugwp,FV3_GFS_v17_p8,FV3_GFS_v17_p8_gf,FV3_GFS_v17_p8_mynn" -while getopts "ac" option; do +while getopts "c" option; do case "${option}" in - a) - APP="ATMAERO" - CCPP_SUITES="FV3_GFS_v16,FV3_GFS_v16_ugwpv1" - shift - ;; c) APP="S2SW" - CCPP_SUITES="FV3_GFS_v16_coupled_nsstNoahmpUGWPv1,FV3_GFS_v16_coupled_p7_rrtmgp" - shift + CCPP_SUITES="FV3_GFS_v16_coupled_nsstNoahmpUGWPv1,FV3_GFS_v16_coupled_p7_rrtmgp,FV3_GFS_v17_coupled_p8" ;; *) echo "Unrecognized option: ${1}" @@ -27,7 +21,6 @@ done source ./machine-setup.sh > /dev/null 2>&1 cwd=$(pwd) - # Set target platform case "${target}" in hera|orion|stampede|jet|cheyenne) @@ -50,5 +43,5 @@ if [ -d build ]; then rm -R build fi mkdir -p build && cd build -cmake -DAPP=${APP} -DCCPP_SUITES=${CCPP_SUITES} .. +cmake -DAPP=${APP} -DCCPP_SUITES=${CCPP_SUITES} -DUFS_GOCART="ON" .. OMP_NUM_THREADS=1 make -j ${BUILD_JOBS:-8} VERBOSE=${BUILD_VERBOSE:-} diff --git a/sorc/checkout.sh b/sorc/checkout.sh index 1db450ac38..268d35fb97 100755 --- a/sorc/checkout.sh +++ b/sorc/checkout.sh @@ -32,13 +32,13 @@ if [[ ! -d ufs_model.fd ]] ; then #JKHgit clone https://github.com/ufs-community/ufs-weather-model ufs_model.fd >> ${logdir}/checkout-ufs_model.log 2>&1 git clone https://github.com/NOAA-GSL/ufs-weather-model ufs_model.fd >> ${logdir}/checkout-ufs_model.log 2>&1 cd ufs_model.fd - #JKH 29Mar22 branch, c31f633385d20dca9062fb16dc98d71677f19d00 - git checkout ${ufs_model_hash:-global-29Mar2022} + #JKH 23May22 branch, 7b0be423e92a4bf7f9913d3c4195a776060d90dc + git checkout ${ufs_model_hash:-7b0be42} git submodule update --init --recursive cd ${topdir} - # if [[ -d ufs_model.fd_gsl ]]; then - # rsync -avx ufs_model.fd_gsl/ ufs_model.fd/ ## copy over GSL changes not in UFS repository - # fi + if [[ -d ufs_model.fd_gsl ]]; then + rsync -avx ufs_model.fd_gsl/ ufs_model.fd/ ## copy over GSL changes not in UFS repository + fi else echo 'Skip. Directory ufs_model.fd already exists.' fi @@ -71,7 +71,7 @@ if [[ ! -d ufs_utils.fd ]] ; then rm -f ${topdir}/checkout-ufs_utils.log git clone --recursive https://github.com/ufs-community/UFS_UTILS.git ufs_utils.fd >> ${logdir}/checkout-ufs_utils.fd.log 2>&1 cd ufs_utils.fd - git checkout 26cd024 + git checkout 04ad17e cd ${topdir} if [[ -d ufs_utils.fd_gsl ]]; then rsync -avx ufs_utils.fd_gsl/ ufs_utils.fd/ ## copy over GSL changes not in UFS_UTILS repository @@ -85,7 +85,7 @@ if [[ ! -d gfs_post.fd ]] ; then rm -f ${topdir}/checkout-gfs_post.log git clone https://github.com/NOAA-EMC/UPP.git gfs_post.fd >> ${logdir}/checkout-gfs_post.log 2>&1 cd gfs_post.fd - git checkout c939eae + git checkout upp_v10.0.11 git submodule update --init CMakeModules ################################################################################ # checkout_gtg diff --git a/sorc/ens_tracker.v1.1.15.2/README.md b/sorc/ens_tracker.v1.1.15.2/README.md deleted file mode 100644 index c3c9276615..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# TC_tracker ---- June 08, 2020 ------ -This package is created for GFS/GEFS retrospective runs, and for community as well. - diff --git a/sorc/ens_tracker.v1.1.15.2/control/theia/jfv3_cyclone_genesis_00.ecf_theia b/sorc/ens_tracker.v1.1.15.2/control/theia/jfv3_cyclone_genesis_00.ecf_theia deleted file mode 100755 index 3de50e8f4f..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/theia/jfv3_cyclone_genesis_00.ecf_theia +++ /dev/null @@ -1,88 +0,0 @@ -#!/bin/ksh --login -#PBS -N gfs_TC_GEN_00 -#PBS -q batch -#PBS -d . -#PBS -e /scratch4/NCEPDEV/stmp3/Jiayi.Peng/gfs_TC_GEN_00.o -#PBS -o /scratch4/NCEPDEV/stmp3/Jiayi.Peng/gfs_TC_GEN_00.o -#PBS -A fv3-cpu -#PBS -l procs=1 -#PBS -l walltime=00:50:00 -#PBS -l vmem=10G -#PBS -S /bin/ksh -##PBS -l nodes=3:ppn=7 - -##%include -##%include - -export moduleInit=/apps/lmod/lmod/init/ksh -. $moduleInit -module use /scratch3/NCEPDEV/nwprod/lib/modulefiles -module load intel/16.1.150 -module load impi/5.1.2.150 -module load nco/4.6.0 - -module load png/v1.2.44 -module load w3emc/v2.0.5 -module load w3nco/v2.0.6 -module load bacio/v2.0.1 -module load g2/v3.1.0 -module load z/v1.2.6 -module load jasper/v1.900.1 -module load sigio/v2.0.1 -module load sp/v2.0.2 -module load ip/v2.0.0 - -module load netcdf/3.6.3 -module load pnetcdf/1.7.0 -module load hdf5/1.8.14 - -module load wgrib2/0.1.9.5.1 - -module use /scratch4/NCEPDEV/nems/noscrub/emc.nemspara/soft/modulefiles -module load prod_util -module use /scratch3/NCEPDEV/nwprod/modulefiles -module load grib_util/v1.0.5 - -set -x -export envir=dev2 -export cyc=00 -export job=gfs_genesis_${cyc} - -export NWROOT=/scratch4/NCEPDEV/ensemble/save/Jiayi.Peng -export DATAROOT=/scratch4/NCEPDEV/stmp3/Jiayi.Peng -export COMROOT=/scratch4/NCEPDEV/stmp3/Jiayi.Peng/com2 -export COMROOTp1=/scratch4/NCEPDEV/rstprod/com -export KEEPDATA=YES - -# versions file for $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -#${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JGFS_TC_GENESIS -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JFV3_CYCLONE_GENESIS_JP - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates GFS TC genesis forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/theia/jfv3_cyclone_track_00.ecf_theia b/sorc/ens_tracker.v1.1.15.2/control/theia/jfv3_cyclone_track_00.ecf_theia deleted file mode 100755 index a3bf4ad6a4..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/theia/jfv3_cyclone_track_00.ecf_theia +++ /dev/null @@ -1,88 +0,0 @@ -#!/bin/ksh --login -#PBS -N gfs_TC_00 -#PBS -q batch -#PBS -d . -#PBS -e /scratch4/NCEPDEV/stmp3/Jiayi.Peng/gfs_TC_00.o -#PBS -o /scratch4/NCEPDEV/stmp3/Jiayi.Peng/gfs_TC_00.o -#PBS -A fv3-cpu -#PBS -l procs=1 -#PBS -l walltime=00:30:00 -#PBS -l vmem=12G -#PBS -S /bin/ksh -##PBS -l nodes=3:ppn=7 - -#%include -#%include - -export moduleInit=/apps/lmod/lmod/init/ksh -. $moduleInit -module use /scratch3/NCEPDEV/nwprod/lib/modulefiles -module load intel/16.1.150 -module load impi/5.1.2.150 -module load nco/4.6.0 - -module load png/v1.2.44 -module load w3emc/v2.0.5 -module load w3nco/v2.0.6 -module load bacio/v2.0.1 -module load g2/v3.1.0 -module load z/v1.2.6 -module load jasper/v1.900.1 -module load sigio/v2.0.1 -module load sp/v2.0.2 -module load ip/v2.0.0 - -module load netcdf/3.6.3 -module load pnetcdf/1.7.0 -module load hdf5/1.8.14 - -module load wgrib2/0.1.9.5.1 - -module use /scratch4/NCEPDEV/nems/noscrub/emc.nemspara/soft/modulefiles -module load prod_util -module use /scratch3/NCEPDEV/nwprod/modulefiles -module load grib_util/v1.0.5 - -set -x -export envir=dev2 -export cyc=00 -export job=gfs_track_${cyc} - -export NWROOT=/scratch4/NCEPDEV/ensemble/save/Jiayi.Peng -export DATAROOT=/scratch4/NCEPDEV/stmp3/Jiayi.Peng -export COMROOT=/scratch4/NCEPDEV/stmp3/Jiayi.Peng/com2 -export COMROOTp1=/scratch4/NCEPDEV/rstprod/com -export KEEPDATA=YES - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -#${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JGFS_TC_TRACK -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JFV3_CYCLONE_TRACK_JP - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates GFS TC track forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/theia/jfv3gdas_cyclone_track_00.ecf_theia b/sorc/ens_tracker.v1.1.15.2/control/theia/jfv3gdas_cyclone_track_00.ecf_theia deleted file mode 100755 index a8ec11abaf..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/theia/jfv3gdas_cyclone_track_00.ecf_theia +++ /dev/null @@ -1,88 +0,0 @@ -#!/bin/ksh --login -#PBS -N gdas_TC_00 -#PBS -q batch -#PBS -d . -#PBS -e /scratch4/NCEPDEV/stmp3/Jiayi.Peng/gdas_TC_00.o -#PBS -o /scratch4/NCEPDEV/stmp3/Jiayi.Peng/gdas_TC_00.o -#PBS -A fv3-cpu -#PBS -l procs=1 -#PBS -l walltime=00:30:00 -#PBS -l vmem=10G -#PBS -S /bin/ksh -##PBS -l nodes=3:ppn=7 - -#%include -#%include - -export moduleInit=/apps/lmod/lmod/init/ksh -. $moduleInit -module use /scratch3/NCEPDEV/nwprod/lib/modulefiles -module load intel/16.1.150 -module load impi/5.1.2.150 -module load nco/4.6.0 - -module load png/v1.2.44 -module load w3emc/v2.0.5 -module load w3nco/v2.0.6 -module load bacio/v2.0.1 -module load g2/v3.1.0 -module load z/v1.2.6 -module load jasper/v1.900.1 -module load sigio/v2.0.1 -module load sp/v2.0.2 -module load ip/v2.0.0 - -module load netcdf/3.6.3 -module load pnetcdf/1.7.0 -module load hdf5/1.8.14 - -module load wgrib2/0.1.9.5.1 - -module use /scratch4/NCEPDEV/nems/noscrub/emc.nemspara/soft/modulefiles -module load prod_util -module use /scratch3/NCEPDEV/nwprod/modulefiles -module load grib_util/v1.0.5 - -set -x -export envir=dev2 -export cyc=00 -export job=gdas_track_${cyc} - -export NWROOT=/scratch4/NCEPDEV/ensemble/save/Jiayi.Peng -export DATAROOT=/scratch4/NCEPDEV/stmp3/Jiayi.Peng -export COMROOT=/scratch4/NCEPDEV/stmp3/Jiayi.Peng/com2 -export COMROOTp1=/scratch4/NCEPDEV/rstprod/com -export KEEPDATA=YES - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -#${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JGDAS_TC_TRACK -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JFV3GDAS_CYCLONE_TRACK_JP - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates GDAS TC track forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jcens_genesis_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jcens_genesis_00.ecf_wcoss deleted file mode 100755 index 1987662f6f..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jcens_genesis_00.ecf_wcoss +++ /dev/null @@ -1,74 +0,0 @@ -#BSUB -J cens_GE_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/cens_genesis_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/cens_genesis_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 0:45 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=4000] -#BSUB -R affinity[core] -#BSUB -n 21 -#BSUB -R span[ptile=7] -#BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=cens_genesis_${cyc} - -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export KEEPDATA=NO - -# versions file for $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JCENS_TC_GENESIS - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates CENS TC GENESIS forecast -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jcens_track_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jcens_track_00.ecf_wcoss deleted file mode 100755 index 756e2403bb..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jcens_track_00.ecf_wcoss +++ /dev/null @@ -1,74 +0,0 @@ -#BSUB -J cens_TC_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/cens_track_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/cens_track_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 0:45 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=4000] -#BSUB -R affinity[core] -#BSUB -n 21 -#BSUB -R span[ptile=7] -#BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=cens_track_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export KEEPDATA=NO - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JCENS_TC_TRACK - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual - -###################################################################### -#PURPOSE: Executes the job that creates CENS TC TRACK forecast -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jcmc_genesis_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jcmc_genesis_00.ecf_wcoss deleted file mode 100755 index c85a91b950..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jcmc_genesis_00.ecf_wcoss +++ /dev/null @@ -1,75 +0,0 @@ -#BSUB -J cmc_GE_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/cmc_genesis_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/cmc_genesis_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=cmc_genesis_${cyc} - -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export DCOMROOT=/dcom -export KEEPDATA=NO - -# versions file for $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JCMC_TC_GENESIS - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates CMC TC genesis forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jcmc_track_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jcmc_track_00.ecf_wcoss deleted file mode 100755 index b8fdb05b8f..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jcmc_track_00.ecf_wcoss +++ /dev/null @@ -1,75 +0,0 @@ -#BSUB -J cmc_TC_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/cmc_track_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/cmc_track_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=cmc_track_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export DCOMROOT=/dcom -export KEEPDATA=NO - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JCMC_TC_TRACK - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual - -###################################################################### -#PURPOSE: Executes the job that creates CMC TC track forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jecmwf_genesis_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jecmwf_genesis_00.ecf_wcoss deleted file mode 100755 index 21a5ff1fa2..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jecmwf_genesis_00.ecf_wcoss +++ /dev/null @@ -1,70 +0,0 @@ -#BSUB -J ecmwf_GE_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/ecmwf_genesis_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/ecmwf_genesis_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=ecmwf_genesis_${cyc} - -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export DCOMROOT=/dcom -export KEEPDATA=NO - -# versions file for $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JECMWF_TC_GENESIS - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates ECMWF TC genesis forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jecmwf_track_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jecmwf_track_00.ecf_wcoss deleted file mode 100755 index 996591c843..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jecmwf_track_00.ecf_wcoss +++ /dev/null @@ -1,69 +0,0 @@ -#BSUB -J ecmwf_TC_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/ecmwf_track_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/ecmwf_track_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=ecmwf_track_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export DCOMROOT=/dcom -export KEEPDATA=NO - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JECMWF_TC_TRACK - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates ECMWF TC track forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jeens_genesis_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jeens_genesis_00.ecf_wcoss deleted file mode 100755 index 782b415f63..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jeens_genesis_00.ecf_wcoss +++ /dev/null @@ -1,75 +0,0 @@ -#BSUB -J eens_GE_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/eens_genesis_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/eens_genesis_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 0:45 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=4000] -#BSUB -R affinity[core] -#BSUB -n 51 -#BSUB -R span[ptile=8] -#BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=eens_genesis_${cyc} - -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export DCOMROOT=/dcom -export KEEPDATA=NO - -# versions file for $tracker_ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JEENS_TC_GENESIS - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates EENS TC GENESIS forecast -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jeens_grib_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jeens_grib_00.ecf_wcoss deleted file mode 100755 index a21ce1fe6e..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jeens_grib_00.ecf_wcoss +++ /dev/null @@ -1,69 +0,0 @@ -#BSUB -J ec_grib_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/eens_grib_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/eens_grib_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 41 -#BSUB -R span[ptile=7] -#BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=eens_grib_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export DCOMROOT=/dcom -export KEEPDATA=NO - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JEENS_GRIB - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that handles EC grib1 to NCEP grib1 -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jeens_track_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jeens_track_00.ecf_wcoss deleted file mode 100755 index 8e5fc4d2b0..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jeens_track_00.ecf_wcoss +++ /dev/null @@ -1,69 +0,0 @@ -#BSUB -J eens_TC_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/eens_track_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/eens_track_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 1:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=4000] -#BSUB -R affinity[core] -#BSUB -n 51 -#BSUB -R span[ptile=8] -#BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=eens_track_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export DCOMROOT=/dcom -export KEEPDATA=NO - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JEENS_TC_TRACK - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates EENS TC TRACK forecast -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfens_genesis_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfens_genesis_00.ecf_wcoss deleted file mode 100755 index 7cb5d21e02..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfens_genesis_00.ecf_wcoss +++ /dev/null @@ -1,75 +0,0 @@ -#BSUB -J fens_GE_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/fens_genesis_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/fens_genesis_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 0:45 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=4000] -#BSUB -R affinity[core] -#BSUB -n 21 -#BSUB -R span[ptile=7] -#BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=fens_genesis_${cyc} - -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export DCOMROOT=/dcom -export KEEPDATA=NO - -# versions file for $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JFENS_TC_GENESIS - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates FENS TC GENESIS forecast -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfens_track_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfens_track_00.ecf_wcoss deleted file mode 100755 index 08577975df..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfens_track_00.ecf_wcoss +++ /dev/null @@ -1,72 +0,0 @@ -#BSUB -J fens_TC_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/fens_track_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/fens_track_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 0:45 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=4000] -#BSUB -R affinity[core] -#BSUB -n 21 -#BSUB -R span[ptile=7] -#BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export MP_LABELIO=yes -export MP_STDOUTMODE=unordered - -export envir=dev2 -export cyc=00 -export job=fens_track_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export DCOMROOT=/dcom -export KEEPDATA=NO - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JFENS_TC_TRACK - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates FENS TRACK forecast -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfv3_cyclone_genesis_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfv3_cyclone_genesis_00.ecf_wcoss deleted file mode 100755 index 28e6dfe6f1..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfv3_cyclone_genesis_00.ecf_wcoss +++ /dev/null @@ -1,75 +0,0 @@ -#BSUB -J gfs_GE_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/gfs_genesis_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/gfs_genesis_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.5 -module load prod_util/v1.0.24 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=gfs_genesis_${cyc} - -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export KEEPDATA=YES - -# versions file for $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -#${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JGFS_TC_GENESIS -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JFV3_CYCLONE_GENESIS_JP - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates GFS TC genesis forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfv3_cyclone_track_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfv3_cyclone_track_00.ecf_wcoss deleted file mode 100755 index 8b5811cf96..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfv3_cyclone_track_00.ecf_wcoss +++ /dev/null @@ -1,74 +0,0 @@ -#BSUB -J gfs_TC_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/gfs_track_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/gfs_track_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.5 -module load prod_util/v1.0.24 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=gfs_track_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 -export COMDATEROOT=/com2 - -export COMROOTp1=/com -export KEEPDATA=YES - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -#${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JGFS_TC_TRACK -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JFV3_CYCLONE_TRACK_JP - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates GFS TC track forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfv3gdas_cyclone_track_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfv3gdas_cyclone_track_00.ecf_wcoss deleted file mode 100755 index 27de9ee7e7..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jfv3gdas_cyclone_track_00.ecf_wcoss +++ /dev/null @@ -1,73 +0,0 @@ -#BSUB -J gdas_TC_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/gdas_track_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/gdas_track_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.5 -module load prod_util/v1.0.24 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=gdas_track_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 -export COMDATEROOT=/com2 - -export COMROOTp1=/com -export KEEPDATA=YES - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JFV3GDAS_CYCLONE_TRACK_JP - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates GDAS TC track forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgefs_genesis_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgefs_genesis_00.ecf_wcoss deleted file mode 100755 index 3ebea63143..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgefs_genesis_00.ecf_wcoss +++ /dev/null @@ -1,73 +0,0 @@ -#BSUB -J gefs_GE_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/gefs_genesis_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/gefs_genesis_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 0:45 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=4000] -#BSUB -R affinity[core] -#BSUB -n 21 -#BSUB -R span[ptile=7] -#BSUB -a poe -#BSUB -x -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=gefs_genesis_${cyc} - -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export KEEPDATA=NO - -# versions file for $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JGEFS_TC_GENESIS - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates GEFS TC GENESIS forecast -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgefs_track_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgefs_track_00.ecf_wcoss deleted file mode 100755 index c587f3fb15..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgefs_track_00.ecf_wcoss +++ /dev/null @@ -1,74 +0,0 @@ -#BSUB -J gefs_TC_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/gefs_track_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/gefs_track_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 0:45 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=4000] -#BSUB -R affinity[core] -#BSUB -n 21 -#BSUB -R span[ptile=7] -#BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=gefs_track_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export KEEPDATA=NO - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JGEFS_TC_TRACK - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual - -###################################################################### -#PURPOSE: Executes the job that creates GEFS TC TRACK forecast -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgfs_genesis_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgfs_genesis_00.ecf_wcoss deleted file mode 100755 index 3e3f0ff95e..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgfs_genesis_00.ecf_wcoss +++ /dev/null @@ -1,74 +0,0 @@ -#BSUB -J gfs_GE_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/gfs_genesis_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/gfs_genesis_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=gfs_genesis_${cyc} - -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export KEEPDATA=NO - -# versions file for $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JGFS_TC_GENESIS - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates GFS TC genesis forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgfs_track_avno_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgfs_track_avno_00.ecf_wcoss deleted file mode 100755 index 8829e3f528..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgfs_track_avno_00.ecf_wcoss +++ /dev/null @@ -1,73 +0,0 @@ -#BSUB -J fv3_TC_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/fv3_track_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/fv3_track_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=fv3_track_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 -export COMDATEROOT=/com2 - -export COMROOTp1=/com -export KEEPDATA=NO - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JGFS_TC_TRACK_AVNO - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates GFS TC track forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgfs_track_avnx_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgfs_track_avnx_00.ecf_wcoss deleted file mode 100755 index 5c80b09d6e..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jgfs_track_avnx_00.ecf_wcoss +++ /dev/null @@ -1,73 +0,0 @@ -#BSUB -J gfs_TC_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/gfs_track_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/gfs_track_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=gfs_track_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 -export COMDATEROOT=/com2 - -export COMROOTp1=/com -export KEEPDATA=NO - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JGFS_TC_TRACK_AVNX - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates GFS TC track forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jnavgem_genesis_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jnavgem_genesis_00.ecf_wcoss deleted file mode 100755 index a5fe41e7db..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jnavgem_genesis_00.ecf_wcoss +++ /dev/null @@ -1,74 +0,0 @@ -#BSUB -J navgem_GE_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/navgem_genesis_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/navgem_genesis_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=navgem_genesis_${cyc} - -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export KEEPDATA=NO - -# versions file for $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JNAVGEM_TC_GENESIS - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates NAVGEM TC track forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jnavgem_track_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jnavgem_track_00.ecf_wcoss deleted file mode 100755 index 012c47c552..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jnavgem_track_00.ecf_wcoss +++ /dev/null @@ -1,70 +0,0 @@ -#BSUB -J navgem_TC_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/navgem_track_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/navgem_track_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=navgem_track_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export DCOMROOT=/dcom -export KEEPDATA=NO - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JNAVGEM_TC_TRACK - -#%include -#%manual - -###################################################################### -#PURPOSE: Executes the job that creates NAVGEM TC track forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jukmet_genesis_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jukmet_genesis_00.ecf_wcoss deleted file mode 100755 index eb102f8dec..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jukmet_genesis_00.ecf_wcoss +++ /dev/null @@ -1,74 +0,0 @@ -#BSUB -J uk_GE_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/ukmet_genesis_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/ukmet_genesis_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=ukmet_genesis_${cyc} - -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export KEEPDATA=NO - -# versions file for $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JUKMET_TC_GENESIS - -if [ $? -ne 0 ]; then - ecflow_client --abort - exit -fi - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates UKMET TC genesis forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jukmet_grib_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jukmet_grib_00.ecf_wcoss deleted file mode 100755 index d622be4e10..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jukmet_grib_00.ecf_wcoss +++ /dev/null @@ -1,69 +0,0 @@ -#BSUB -J uk_grib_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/ukmet_grib_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/ukmet_grib_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=ukmet_grib_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export DCOMROOT=/dcom -export KEEPDATA=NO - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JUKMET_GRIB - -#%include -#%manual -###################################################################### -#PURPOSE: merging UKMET 8-piece-grib data into NCEP grib1 1X1 degree -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jukmet_track_00.ecf_wcoss b/sorc/ens_tracker.v1.1.15.2/control/wcoss/jukmet_track_00.ecf_wcoss deleted file mode 100755 index d37098111c..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/control/wcoss/jukmet_track_00.ecf_wcoss +++ /dev/null @@ -1,69 +0,0 @@ -#BSUB -J uk_TC_00 -#BSUB -o /ptmpp2/Jiayi.Peng/com2/ukmet_track_00.o%J -#BSUB -e /ptmpp2/Jiayi.Peng/com2/ukmet_track_00.o%J -#BSUB -L /bin/sh -#BSUB -q "dev2" -#BSUB -W 01:00 -#BSUB -cwd /ptmpp2/Jiayi.Peng/com2 -#BSUB -P GEN-T2O -##BSUB -R rusage[mem=5000] -#BSUB -R affinity[core] -#BSUB -n 1 -#BSUB -R span[ptile=1] -##BSUB -a poe -#BSUB -x - -#%include -#%include -module use /nwprod2/modulefiles -module load prod_envir/v1.0.1 -module load grib_util/v1.0.3 -module load prod_util/v1.0.15 - -module use /usrx/local/modulefiles -module load EnvVars/1.0.1 - -set -x -export envir=dev2 -export cyc=00 -export job=ukmet_track_${cyc} -#export NWROOT=/nwprod2 -export NWROOT=/ensemble/save/Jiayi.Peng - -#export DATAROOT=/tmpnwprd_p2 -export DATAROOT=/ptmpp2/Jiayi.Peng - -export COMDATEROOT=/com2 -#export COMROOT=/com2 -export COMROOT=/ptmpp2/Jiayi.Peng/com2 - -export COMROOTp1=/com -export DCOMROOT=/dcom -export KEEPDATA=NO - -# versions file for tracker $tracker.ver -VERSION_FILE=${NWROOT}/versions/tracker.ver -if [ -f $VERSION_FILE ]; then - . $VERSION_FILE -else - ecflow_client --abort - exit -fi - -# CALL executable job script here -${NWROOT}/ens_tracker.${ens_tracker_ver}/jobs/JUKMET_TC_TRACK - -#%include -#%manual -###################################################################### -#PURPOSE: Executes the job that creates UKMET TC track forecasts -###################################################################### - -###################################################################### -# Job specific troubleshooting instructions: -# see generic troubleshoot manual page -# -###################################################################### - -# include manual page below -#%end diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Dell b/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Dell deleted file mode 100644 index 5230ecb22a..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Dell +++ /dev/null @@ -1,25 +0,0 @@ -#%Module##################################################### -## Module file for ens_tracker -############################################################# - -module load ips/18.0.1.163 -module load impi/18.0.1 - -#module load w3emc/2.3.0 -module load w3nco/2.0.6 -module load bacio/2.0.2 -module load sp/2.0.2 -module load nemsio/2.2.3 -module load nemsiogfs/2.0.1 -module load sigio/2.0.1 - -module load jasper/1.900.1 -module load libpng/1.2.59 -module load zlib/1.2.11 -module load g2/3.1.0 -module load PNetCDF/1.8.1 - -module use /usrx/local/nceplibs/dev/NCEPLIBS/modulefiles -#module load hdf5_parallel/1.10.6 -module load netcdf_parallel/4.7.4 -module load w3emc_para/2.4.0 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Hera b/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Hera deleted file mode 100644 index db3ac81cd7..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Hera +++ /dev/null @@ -1,26 +0,0 @@ -#%Module for HERA ################################################ - -module use /scratch2/NCEPDEV/nwprod/NCEPLIBS/modulefiles -module use /apps/modules/modulefamilies/intel -module use /apps/modules/modulefamilies/intel_impi - -module load intel/18.0.5.274 -module load impi/2019.0.4 -module load nco/4.7.0 - -module load png/1.2.44 -module load w3emc/2.3.1 -module load w3nco/2.0.6 -module load bacio/2.0.3 -module load g2/3.1.0 - -module load z/1.2.11 -module load jasper/1.900.1 -module load sigio/2.1.1 -module load sp/2.0.2 -module load ip/2.0.0 - -#module use /scratch2/NCEPDEV/nwprod/NCEPLIBS/modulefiles -module load hdf5_parallel/1.12.0 -module load netcdf_parallel/4.7.5 -module load w3emc_para/2.4.0 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Jet b/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Jet deleted file mode 100644 index 85bba21a93..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Jet +++ /dev/null @@ -1,31 +0,0 @@ -#%Module for JET ################################################ -setenv NCEPLIBS /mnt/lfs4/HFIP/hfv3gfs/nwprod/NCEPLIBS/lib - -module use /mnt/lfs4/HFIP/hfv3gfs/nwprod/NCEPLIBS/modulefiles -module use /apps/modules/modulefamilies/intel -module use /apps/modules/modulefamilies/intel_impi - -module load intel/18.0.5.274 -module load impi/2018.4.274 -#module load nco/4.7.0 - -module load png/v1.2.44 -#module load w3emc/2.4.0 -module load w3nco/v2.0.6 -module load bacio/v2.0.2 -module load g2/v3.1.0 - -module load z/v1.2.6 -module load jasper/v1.900.1 -module load sigio/v2.1.0 -module load sp/v2.0.2 -module load ip/v3.0.1 - -module load hdf5_parallel/1.10.6 -module load netcdf_parallel/4.7.4 -module load w3emc_para/v2.4.0 - -setenv NCEPLIBS /lfs4/HFIP/hfv3gfs/gwv/l0530/lib/ -module use -a /mnt/lfs4/HFIP/hfv3gfs/gwv/l0530/lib/modulefiles -module load esmflocal/8_0_1 -module load netcdfp/4.7.4 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Orion b/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Orion deleted file mode 100644 index 59151a3f17..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Orion +++ /dev/null @@ -1,24 +0,0 @@ -#%Module for Orion ################################################ - -module use /apps/contrib/NCEPLIBS/orion/modulefiles -module use /apps/modulefiles/core - -module load intel/2020 -module load impi/2020 -#module load nco/4.7.0 - -module load png/1.2.44 -module load w3emc/2.5.0 -module load w3nco/2.1.0 -module load bacio/2.2.0 -module load g2/3.1.1 - -module load z/1.2.6 -module load jasper/1.900.2 -module load sigio/2.2.0 -module load sp/2.1.0 -module load ip/3.1.0 - -module load hdf5_parallel/1.12.0 -module load netcdf_parallel/4.7.5 -module load w3emc_para/2.4.0 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Theia b/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Theia deleted file mode 100644 index 338d2bcc52..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Theia +++ /dev/null @@ -1,27 +0,0 @@ -#%Module for THEIA ################################################ - -module use /scratch3/NCEPDEV/nwprod/lib/modulefiles -module use /apps/modules/modulefamilies/intel -module use /apps/modules/modulefamilies/intel_impi - -module load intel/16.1.150 -module load impi/5.1.2.150 -module load nco/4.6.0 - -module load png/v1.2.44 -module load w3emc/v2.0.5 -module load w3nco/v2.0.6 -module load bacio/v2.0.1 -#module load g2/v2.5.0 -module load g2/v3.1.0 - -module load z/v1.2.6 -module load jasper/v1.900.1 -module load sigio/v2.0.1 -module load sp/v2.0.2 -module load ip/v2.0.0 - -module load netcdf/3.6.3 -#module load netcdf/4.4.0 -module load pnetcdf/1.7.0 -module load hdf5/1.8.14 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Wcoss b/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Wcoss deleted file mode 100755 index d9a9297407..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Wcoss +++ /dev/null @@ -1,25 +0,0 @@ -#%Module################################################ -## -## NetCDF/4.2 above needs HDF5 for compile -## -# module load EnvVars/1.0.0 - module load EnvVars/1.0.1 -# module load ics/15.0.6 - module load ics/17.0.3 - module load lsf/9.1 - module load ibmpe/1.3.0.12 - module load png/v1.2.44 - module load w3emc/v2.0.5 - module load w3nco/v2.0.6 - module load bacio/v2.0.1 - module load g2/v3.1.0 - module load z/v1.2.6 - module load jasper/v1.900.1 - module load sigio/v2.0.1 - module load sp/v2.0.2 - module load ip/v2.0.0 - -# module load NetCDF/3.6.3 - module load NetCDF/4.2/serial - module load PNetCDF/1.5.0 - module load HDF5/1.8.9/serial diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/ecmwfensh.lst b/sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/ecmwfensh.lst deleted file mode 100644 index b947b08a07..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/ecmwfensh.lst +++ /dev/null @@ -1,4022 +0,0 @@ -Page 1 Source Listing -2020-06-22 00:51 ecmwfensh.f - -ecmwfensh.f(410): warning #8043: The extra characters in the format specification will be ignored. ['('kpdsread, ichk, jpds, jp... - write (*,'(''kpdsread, ichk, jpds, jpds, rjpds ='', ------------------------------------------------------------------^ -ecmwfensh.f(417): warning #8043: The extra characters in the format specification will be ignored. ['('kpdsread, ichk, jgds, jg... - write (*,'(''kpdsread, ichk, jgds, jgds, rjgds ='', ------------------------------------------------------------------^ -ecmwfensh.f(424): warning #8043: The extra characters in the format specification will be ignored. ['('kpdsread, ichk, jens, je... - write (*,'(''kpdsread, ichk, jens, jens, rjens ='', ------------------------------------------------------------------^ -ecmwfensh.f(431): warning #8043: The extra characters in the format specification will be ignored. ['('kpdsread, ichk, kpds, kp... - write (*,'(''kpdsread, ichk, kpds, kpds, rkpds ='', ------------------------------------------------------------------^ -ecmwfensh.f(438): warning #8043: The extra characters in the format specification will be ignored. ['('kpdsread, ichk, kgds, kg... - write (*,'(''kpdsread, ichk, kgds, kgds, rkgds ='', ------------------------------------------------------------------^ -ecmwfensh.f(445): warning #8043: The extra characters in the format specification will be ignored. ['('kpdsread, ichk, kens, ke... - write (*,'(''kpdsread, ichk, kens, kens, rkens ='', ------------------------------------------------------------------^ - -Page 2 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 1 program ecmwfensh - 2 C$$$ MAIN PROGRAM DOCUMENTATION BLOCK - 3 C - 4 C MAIN PROGRAM: ECMWFENS - 5 C PRGMMR: WOBUS ORG: NP20 DATE: 2004-09-12 - 6 C - 7 C ABSTRACT: This program converts GRIB ensemble header - 8 c extensions from ECMWF's format to NCEP's format. It - 9 c also calculates ensemble mean and spread fields. - 10 C - 11 C PROGRAM HISTORY LOG: - 12 C 97-01-17 MARCHOK original program - 13 c 99-09-29 MARCHOK converted to run on Cray5 - 14 c 00-03-17 Wobus IBM version - 15 C 01-01-16 WOBUS added DOCBLOCK, removed nonstandard - 16 c output - 17 c 04-09-12 Wobus converted for high resolution - 18 c and additional variables - 19 C - 20 C USAGE: - 21 C INPUT FILES: - 22 c unit 5 - Namelist NAMIN parameters: - 23 c resflag=2 for 2.5x2.5 - 24 c maxmem = number of members - 25 c unit 11 - input GRIB file for one forecast length - 26 c unit 21 - GRIB index file corresponding to unit 11 - 27 C - 28 C OUTPUT FILES: (INCLUDING SCRATCH FILES) - 29 C unit 6 - standard output - 30 C unit 51 - GRIB output for t200 - 31 C unit 151 - GRIB output for t200 stats - 32 C unit 52 - GRIB output for t500 - 33 C unit 152 - GRIB output for t500 stats - 34 C unit 53 - GRIB output for t700 - 35 C unit 153 - GRIB output for t700 stats - 36 C unit 54 - GRIB output for t850 - 37 C unit 154 - GRIB output for t850 stats - 38 C unit 55 - GRIB output for t2m - 39 C unit 155 - GRIB output for t2m stats - 40 C unit 56 - GRIB output for t2max - 41 C unit 156 - GRIB output for t2max stats - 42 C unit 57 - GRIB output for t2min - 43 C unit 157 - GRIB output for t2min stats - 44 C unit 58 - GRIB output for td2m - 45 C unit 158 - GRIB output for td2m stats - 46 C unit 61 - GRIB output for z200 - 47 C unit 162 - GRIB output for z200 stats - 48 C unit 62 - GRIB output for z500 - 49 C unit 162 - GRIB output for z500 stats - 50 C unit 63 - GRIB output for z700 - 51 C unit 162 - GRIB output for z700 stats - 52 C unit 64 - GRIB output for z850 - 53 C unit 162 - GRIB output for z850 stats - 54 C unit 65 - GRIB output for z1000 - 55 C unit 165 - GRIB output for z1000 stats - 56 C unit 71 - GRIB output for rh500 - 57 C unit 171 - GRIB output for rh500 stats - -Page 3 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 58 C unit 72 - GRIB output for rh700 - 59 C unit 172 - GRIB output for rh700 stats - 60 C unit 73 - GRIB output for rh850 - 61 C unit 173 - GRIB output for rh850 stats - 62 C unit 81 - GRIB output for mslp - 63 C unit 181 - GRIB output for mslp stats - 64 C unit 82 - GRIB output for psfc - 65 C unit 182 - GRIB output for psfc stats - 66 C unit 83 - GRIB output for prcp - 67 C unit 183 - GRIB output for prcp stats - 68 C unit 84 - GRIB output for tcdc - 69 C unit 184 - GRIB output for tcdc stats - 70 C unit 101 - GRIB output for u200 - 71 C unit 102 - GRIB output for v200 - 72 C unit 103 - GRIB output for u500 - 73 C unit 104 - GRIB output for v500 - 74 C unit 105 - GRIB output for u700 - 75 C unit 106 - GRIB output for v700 - 76 C unit 107 - GRIB output for u850 - 77 C unit 108 - GRIB output for v850 - 78 C unit 109 - GRIB output for u10m - 79 C unit 110 - GRIB output for v10m - 80 C - 81 C SUBPROGRAMS CALLED: (LIST ALL CALLED FROM ANYWHERE IN CODES) - 82 C UNIQUE: - - 83 c INCLUDED - create_stats, adjpds, adjext, output, output_stats, - 84 c grange, srange, getgbece, ecmext, - 85 c grib_close, grib_open, grib_open_wa, grib_open_r - 86 C LIBRARY: - 87 C W3LIB - gbyte,fi632,fi633,w3fi63,w3tagb,w3tage - 88 c BACIO - baopen,baopenwa,baopenr,baclose,baread - 89 C - 90 C EXIT STATES: - 91 C COND = 0 - SUCCESSFUL RUN - 92 C - 93 C REMARKS: - 94 c Error messages from W3LIB routines will be printed but - 95 c will not halt execution - 96 C - 97 C ATTRIBUTES: - 98 C LANGUAGE: Fortran 90 - 99 C MACHINE: IBM SP - 100 C - 101 C$$$ - 102 c - 103 c - 104 c *** 9/29/99: Due to cray-3 emergency, this program is being - 105 c implemented on Cray-5 and is being modified to be more - 106 c efficient. It doesn't worry about what order the - 107 c members are in, it just copies them over. It still - 108 c changes the height level values for the 500 mb height - 109 c and for mslp for GrADS purposes. It will leave the - 110 c precip fields as they are. - 111 c - 112 c *** 3/17/00 IBM version - 113 c - 114 c lugb logical unit of the unblocked grib data file - -Page 4 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 115 c lugi logical unit of the unblocked grib index file - 116 c - 117 c kres 1 - input file contains only high res control. - 118 c This lets the program know that you're reading - 119 c a "USD" file. - 120 c 2 - input file contains low res control and - 121 c perturbations. This lets the program know - 122 c you're reading a "USE" file, which has 1 LRC - 123 c record for each parm, plus 50 perturbations - 124 c for each parm. - 125 c (Important to know since high res control - 126 c does NOT have a PDS extension) - 127 c - 128 c memberct Keeps track of how many members have been read in - 129 c for each parameter. In this way, we can modify the - 130 c height level for each successive member (in our - 131 c kludgy system) to be 1,2,3,....,50,51. And only - 132 c index this as "2" since we are only doing this - 133 c kludgy method to 2 variables, z500 and mslp. - 134 c - 135 c - 136 c - 137 parameter(lugi=21,lugb=11,jf=512*256,nlat=181,nlon=360) - 138 parameter(numec=52) - 139 character*1 contflag - 140 character*1 gott200pds,gott500pds,gott700pds,gott850pds - 141 character*1 gott2mpds,gott2maxpds,gott2minpds,gottd2mpds - 142 character*1 gotz200pds,gotz500pds,gotz700pds,gotz850pds - 143 character*1 gotz1000pds - 144 character*1 gotrh500pds,gotrh700pds,gotrh850pds - 145 character*1 gotmslppds,gotpsfcpds,gotprcppds,gottcdcpds - 146 character*1 gotu200,gotv200 - 147 character*1 gotu500,gotv500 - 148 character*1 gotu700,gotv700 - 149 character*1 gotu850,gotv850 - 150 character*1 gotu10m,gotv10m - 151 character*8 newvar - 152 integer jpds(200),jgds(200),jens(200) - 153 integer kpds(200),kgds(200),kens(200) - 154 c for overflow test - 155 real rjpds(204),rjgds(204),rjens(204) - 156 equivalence (jpds(1),rjpds(1)) - 157 equivalence (jgds(1),rjgds(1)) - 158 equivalence (jens(1),rjens(1)) - 159 real rkpds(204),rkgds(204),rkens(204) - 160 equivalence (kpds(1),rkpds(1)) - 161 equivalence (kgds(1),rkgds(1)) - 162 equivalence (kens(1),rkens(1)) - 163 integer kholdt200pds(200) - 164 integer kholdt500pds(200) - 165 integer kholdt700pds(200) - 166 integer kholdt850pds(200) - 167 integer kholdt2mpds(200) - 168 integer kholdt2maxpds(200) - 169 integer kholdt2minpds(200) - 170 integer kholdtd2mpds(200) - 171 integer kholdz200pds(200) - -Page 5 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 172 integer kholdz500pds(200) - 173 integer kholdz700pds(200) - 174 integer kholdz850pds(200) - 175 integer kholdz1000pds(200) - 176 integer kholdrh500pds(200) - 177 integer kholdrh700pds(200) - 178 integer kholdrh850pds(200) - 179 integer kholdmslppds(200) - 180 integer kholdpsfcpds(200) - 181 integer kholdprcppds(200) - 182 integer kholdtcdcpds(200) - 183 integer memberct(2) - 184 integer t200ct,t500ct,t700ct,t850ct - 185 integer t2mct,t2maxct,t2minct,td2mct - 186 integer z200ct,z500ct,z700ct,z850ct,z1000ct - 187 integer rh500ct,rh700ct,rh850ct - 188 integer mslpct,psfcct,prcpct,tcdcct - 189 logical lb(jf) - 190 real f(jf) - 191 real t200vals(numec,nlat*nlon) - 192 real t200mean(nlat*nlon),t200spr(nlat*nlon) - 193 real t500vals(numec,nlat*nlon) - 194 real t500mean(nlat*nlon),t500spr(nlat*nlon) - 195 real t700vals(numec,nlat*nlon) - 196 real t700mean(nlat*nlon),t700spr(nlat*nlon) - 197 real t850vals(numec,nlat*nlon) - 198 real t850mean(nlat*nlon),t850spr(nlat*nlon) - 199 real t2mvals(numec,nlat*nlon) - 200 real t2mmean(nlat*nlon),t2mspr(nlat*nlon) - 201 real t2maxvals(numec,nlat*nlon) - 202 real t2maxmean(nlat*nlon),t2maxspr(nlat*nlon) - 203 real t2minvals(numec,nlat*nlon) - 204 real t2minmean(nlat*nlon),t2minspr(nlat*nlon) - 205 real td2mvals(numec,nlat*nlon) - 206 real td2mmean(nlat*nlon),td2mspr(nlat*nlon) - 207 real z200vals(numec,nlat*nlon) - 208 real z200mean(nlat*nlon),z200spr(nlat*nlon) - 209 real z500vals(numec,nlat*nlon) - 210 real z500mean(nlat*nlon),z500spr(nlat*nlon) - 211 real z700vals(numec,nlat*nlon) - 212 real z700mean(nlat*nlon),z700spr(nlat*nlon) - 213 real z850vals(numec,nlat*nlon) - 214 real z850mean(nlat*nlon),z850spr(nlat*nlon) - 215 real z1000vals(numec,nlat*nlon) - 216 real z1000mean(nlat*nlon),z1000spr(nlat*nlon) - 217 real rh500vals(numec,nlat*nlon) - 218 real rh500mean(nlat*nlon),rh500spr(nlat*nlon) - 219 real rh700vals(numec,nlat*nlon) - 220 real rh700mean(nlat*nlon),rh700spr(nlat*nlon) - 221 real rh850vals(numec,nlat*nlon) - 222 real rh850mean(nlat*nlon),rh850spr(nlat*nlon) - 223 real mslpvals(numec,nlat*nlon) - 224 real mslpmean(nlat*nlon),mslpspr(nlat*nlon) - 225 real psfcvals(numec,nlat*nlon) - 226 real psfcmean(nlat*nlon),psfcspr(nlat*nlon) - 227 real prcpvals(numec,nlat*nlon) - 228 real prcpmean(nlat*nlon),prcpspr(nlat*nlon) - -Page 6 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 229 real tcdcvals(numec,nlat*nlon) - 230 real tcdcmean(nlat*nlon),tcdcspr(nlat*nlon) - 231 namelist/namin/kres,kmaxmem - 232 c - 233 call w3tagb('ECMWFENS',2001,0017,0088,'NP20') - 234 c - 235 read (5,namin,end=1000) - 236 1000 continue - 237 c - 238 print *,' ' - 239 print *,'------------------------------------------------------' - 240 print *,'at beginning of ecmwfens.f, kres= ',kres - 241 & ,' kmaxmem= ',kmaxmem - 242 - 243 t200vals = 0.0 - 244 t200mean = 0.0 - 245 t200spr = 0.0 - 246 t500vals = 0.0 - 247 t500mean = 0.0 - 248 t500spr = 0.0 - 249 t700vals = 0.0 - 250 t700mean = 0.0 - 251 t700spr = 0.0 - 252 t850vals = 0.0 - 253 t850mean = 0.0 - 254 t850spr = 0.0 - 255 t2mvals = 0.0 - 256 t2mmean = 0.0 - 257 t2mspr = 0.0 - 258 t2maxvals = 0.0 - 259 t2maxmean = 0.0 - 260 t2maxspr = 0.0 - 261 t2minvals = 0.0 - 262 t2minmean = 0.0 - 263 t2minspr = 0.0 - 264 td2mvals = 0.0 - 265 td2mmean = 0.0 - 266 td2mspr = 0.0 - 267 - 268 z200vals = 0.0 - 269 z200mean = 0.0 - 270 z200spr = 0.0 - 271 z500vals = 0.0 - 272 z500mean = 0.0 - 273 z500spr = 0.0 - 274 z700vals = 0.0 - 275 z700mean = 0.0 - 276 z700spr = 0.0 - 277 z850vals = 0.0 - 278 z850mean = 0.0 - 279 z850spr = 0.0 - 280 z1000vals = 0.0 - 281 z1000mean = 0.0 - 282 z1000spr = 0.0 - 283 - 284 rh500vals = 0.0 - 285 rh500mean = 0.0 - -Page 7 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 286 rh500spr = 0.0 - 287 rh700vals = 0.0 - 288 rh700mean = 0.0 - 289 rh700spr = 0.0 - 290 rh850vals = 0.0 - 291 rh850mean = 0.0 - 292 rh850spr = 0.0 - 293 - 294 mslpvals = 0.0 - 295 mslpmean = 0.0 - 296 mslpspr = 0.0 - 297 psfcvals = 0.0 - 298 psfcmean = 0.0 - 299 psfcspr = 0.0 - 300 prcpvals = 0.0 - 301 prcpmean = 0.0 - 302 prcpspr = 0.0 - 303 tcdcvals = 0.0 - 304 tcdcmean = 0.0 - 305 tcdcspr = 0.0 - 306 c - 307 gott200pds = 'n' - 308 gott500pds = 'n' - 309 gott700pds = 'n' - 310 gott850pds = 'n' - 311 gott2mpds = 'n' - 312 gott2maxpds = 'n' - 313 gott2minpds = 'n' - 314 gottd2mpds = 'n' - 315 - 316 gotz200pds = 'n' - 317 gotz500pds = 'n' - 318 gotz700pds = 'n' - 319 gotz850pds = 'n' - 320 gotz1000pds = 'n' - 321 - 322 gotrh500pds = 'n' - 323 gotrh700pds = 'n' - 324 gotrh850pds = 'n' - 325 - 326 gotmslppds = 'n' - 327 gotpsfcpds = 'n' - 328 gotprcppds = 'n' - 329 gottcdcpds = 'n' - 330 c - 331 t200ct = 0 - 332 t500ct = 0 - 333 t700ct = 0 - 334 t850ct = 0 - 335 t2mct = 0 - 336 t2maxct = 0 - 337 t2minct = 0 - 338 td2mct = 0 - 339 - 340 z200ct = 0 - 341 z500ct = 0 - 342 z700ct = 0 - -Page 8 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 343 z850ct = 0 - 344 z1000ct = 0 - 345 - 346 rh500ct = 0 - 347 rh700ct = 0 - 348 rh850ct = 0 - 349 - 350 mslpct = 0 - 351 psfcct = 0 - 352 prcpct = 0 - 353 tcdcct = 0 - 354 - 355 c maxloop = number of perturbations + the LRC record. Remember, - 356 c the HRC record is in a file by itself, and is handled by the - 357 c case of kres=1. - 358 - 359 maxloop = kmaxmem + 1 - 360 jpds = -1 - 361 jgds = -1 - 362 jens = -1 - 363 jpds(23) = 0 - 364 kgds = 0 - 365 memberct = 0 - 366 - 367 kpdsread = 0 - 368 - 369 c jpds(23) = 0 - 370 j = 0 - 371 iret = 0 - 372 - 373 print *,' ' - 374 do while (iret.eq.0) - 375 - 376 kpdsnull = -kpdsread - 377 kpds = kpdsnull - 378 kens = kpdsnull - 379 - 380 c Use my modified version of getgbens in this program to - 381 c read the recs. This version of getgbens reads different - 382 c bytes from the ECMWF PDS extension than are read from the - 383 c NCEP PDS extension. *** NOTE: THE VERSION OF GETGBENS - 384 c THAT IS CALLED HAS BEEN MODIFIED FROM THE W3LIB VERSION. - 385 c Modified getgbens has been renamed getgbece - 386 - 387 call getgbece(lugb,lugi,jf,j,jpds,jgds,jens, - 388 & kf,k,kpds,kgds,kens,lb,f,iret, - 389 & ktype,kfnum,ktot) - 390 c check for kpds written out of bounds - 391 if (kpdsread.lt.10) then - 392 print *,' ' - 393 print *,'check kpds before processing' - 394 write(*,71) (kpds(mm),mm=1,5) - 395 write(*,72) (kpds(mm),mm=6,10) - 396 write(*,73) (kpds(mm),mm=11,15) - 397 write(*,74) (kpds(mm),mm=16,20) - 398 write(*,75) (kpds(mm),mm=21,25) - 399 write(*,76) (kgds(mm),mm=1,5) - -Page 9 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 400 write(*,77) (kgds(mm),mm=6,10) - 401 write(*,78) (kgds(mm),mm=11,15) - 402 write(*,79) (kgds(mm),mm=16,20) - 403 write(*,80) (kgds(mm),mm=21,22) - 404 write(*,81) f(1),f(kf/4),f(kf/2) - 405 & ,f(3*kf/4),f(kf) - 406 call srange(kf,lb,f) - 407 c for overflow test - 408 do ichk=26,200 - 409 if ( jpds(ichk) .ne. -1 ) then - 410 write (*,'(''kpdsread, ichk, jpds, jpds, rjpds ='', - .................................................................. -(1) The extra characters in the format specification will be ignored. ['('kpdsread, ichk, jpds, jpds, rjpds =', ] - - 411 & i3,i4,i23,z17,1pe32.23))') - 412 & kpdsread,ichk,jpds(ichk),jpds(ichk),rjpds(ichk) - 413 endif - 414 enddo - 415 do ichk=23,200 - 416 if ( jgds(ichk) .ne. -1 ) then - 417 write (*,'(''kpdsread, ichk, jgds, jgds, rjgds ='', - .................................................................. -(1) The extra characters in the format specification will be ignored. ['('kpdsread, ichk, jgds, jgds, rjgds =', ] - - 418 & i3,i4,i23,z17,1pe32.23))') - 419 & kpdsread,ichk,jgds(ichk),jgds(ichk),rjgds(ichk) - 420 endif - 421 enddo - 422 do ichk=6,200 - 423 if ( jens(ichk) .ne. -1 ) then - 424 write (*,'(''kpdsread, ichk, jens, jens, rjens ='', - .................................................................. -(1) The extra characters in the format specification will be ignored. ['('kpdsread, ichk, jens, jens, rjens =', ] - - 425 & i3,i4,i23,z17,1pe32.23))') - 426 & kpdsread,ichk,jens(ichk),jens(ichk),rjens(ichk) - 427 endif - 428 enddo - 429 do ichk=26,200 - 430 if ( kpds(ichk) .ne. kpdsnull ) then - 431 write (*,'(''kpdsread, ichk, kpds, kpds, rkpds ='', - .................................................................. -(1) The extra characters in the format specification will be ignored. ['('kpdsread, ichk, kpds, kpds, rkpds =', ] - - 432 & i3,i4,i23,z17,1pe32.23))') - 433 & kpdsread,ichk,kpds(ichk),kpds(ichk),rkpds(ichk) - 434 endif - 435 enddo - 436 do ichk=23,200 - 437 if ( kgds(ichk) .ne. 0 ) then - 438 write (*,'(''kpdsread, ichk, kgds, kgds, rkgds ='', - .................................................................. -(1) The extra characters in the format specification will be ignored. ['('kpdsread, ichk, kgds, kgds, rkgds =', ] - - 439 & i3,i4,i23,z17,1pe32.23))') - 440 & kpdsread,ichk,kgds(ichk),kgds(ichk),rkgds(ichk) - 441 endif - -Page 10 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 442 enddo - 443 do ichk=6,200 - 444 if ( kens(ichk) .ne. kpdsnull ) then - 445 write (*,'(''kpdsread, ichk, kens, kens, rkens ='', - .................................................................. -(1) The extra characters in the format specification will be ignored. ['('kpdsread, ichk, kens, kens, rkens =', ] - - 446 & i3,i4,i23,z17,1pe32.23))') - 447 & kpdsread,ichk,kens(ichk),kens(ichk),rkens(ichk) - 448 endif - 449 enddo - 450 endif - 451 kpdsread=kpdsread+1 - 452 c - 453 j=k - 454 c print *,' ' - 455 if (iret.eq.0) then - 456 print *,'immediately after call to getgb, j=',j - 457 & ,' k=',k,' kpds(5)=',kpds(5) - 458 & ,' kpds(14)=',kpds(14),' kfnum=',kfnum - 459 & ,' iret=',iret - 460 if ((kfnum.le.2).or.(kfnum.ge.49)) then - 461 if ((kpds(14).le.24).or.(kpds(14).ge.228)) then - 462 c print *,' ' - 463 call srange(kf,lb,f) - 464 call grange(kf,lb,f,dmin,dmax) - 465 print '(4i5,4i3,2x,a1,i3,3i5,2x,i7,2g12.4)' - 466 & ,k,(kpds(i),i=5,11),'f',kpds(14),ktype,kfnum - 467 & ,ktot,kf,dmin,dmax - 468 endif - 469 endif - 470 else - 471 print *,'!!! getgb IRET= ',iret,' j= ',j - 472 & ,' .... Continuing to next loop iteration ....' - 473 goto 600 - 474 endif - 475 - 476 call adjpds (kpds,contflag,lugout,memberct) - 477 - 478 if (contflag.eq.'n') goto 600 - 479 - 480 call grib_open_wa (lugout,ireto) - 481 if (ireto.gt.0) then - 482 print *,'ireto,lu from grib_open_wa in ecmwfens = ',ireto,lugout - 483 endif - 484 - 485 call adjext (kens,ktype,kfnum,kres) - 486 - 487 c if ((kfnum.le.2).or.(kfnum.ge.49)) then - 488 c if ((kpds(14).le.24).or.(kpds(14).ge.228)) then - 489 c print *,' ' - 490 c write(*,71) (kpds(mm),mm=1,5) - 491 c write(*,72) (kpds(mm),mm=6,10) - 492 c write(*,73) (kpds(mm),mm=11,15) - 493 c write(*,74) (kpds(mm),mm=16,20) - 494 c write(*,75) (kpds(mm),mm=21,25) - 495 c write(*,76) (kgds(mm),mm=1,5) - -Page 11 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 496 c write(*,77) (kgds(mm),mm=6,10) - 497 c write(*,78) (kgds(mm),mm=11,15) - 498 c write(*,79) (kgds(mm),mm=16,20) - 499 c write(*,80) (kgds(mm),mm=21,22) - 500 c write(*,81) f(1),f(kf/4),f(kf/2) - 501 c & ,f(3*kf/4),f(kf) - 502 c call srange(kf,lb,f) - 503 c endif - 504 c endif - 505 - 506 kpds(23)=2 - 507 - 508 newvar = 'none' - 509 - 510 if (kpds(5) .eq. 61) then - 511 if (gotprcppds .eq. 'n') then - 512 gotprcppds = 'y' - 513 newvar = 'prcp ' - 514 do i = 1,25 - 515 kholdprcppds(i) = kpds(i) - 516 enddo - 517 endif - 518 prcpct = prcpct + 1 - 519 do ip = 1,kf - 520 f(ip) = f(ip) * 1000.0 - 521 prcpvals(prcpct,ip) = prcpvals(prcpct,ip) + f(ip) - 522 enddo - 523 else if (kpds(5) .eq. 2) then - 524 if (gotmslppds .eq. 'n') then - 525 gotmslppds = 'y' - 526 newvar = 'mslp ' - 527 do i = 1,25 - 528 kholdmslppds(i) = kpds(i) - 529 enddo - 530 endif - 531 call srange(kf,lb,f) - 532 mslpct = mslpct + 1 - 533 do ip = 1,kf - 534 mslpvals(mslpct,ip) = mslpvals(mslpct,ip) + f(ip) - 535 enddo - 536 else if (kpds(5) .eq. 11) then - 537 if (kpds(7) .eq. 850) then - 538 if (gott850pds .eq. 'n') then - 539 gott850pds = 'y' - 540 newvar = 't850 ' - 541 do i = 1,25 - 542 kholdt850pds(i) = kpds(i) - 543 enddo - 544 endif - 545 t850ct = t850ct + 1 - 546 do ip = 1,kf - 547 t850vals(t850ct,ip) = t850vals(t850ct,ip) + f(ip) - 548 enddo - 549 else if (kpds(7) .eq. 700) then - 550 if (gott700pds .eq. 'n') then - 551 gott700pds = 'y' - 552 newvar = 't700 ' - -Page 12 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 553 do i = 1,25 - 554 kholdt700pds(i) = kpds(i) - 555 enddo - 556 endif - 557 t700ct = t700ct + 1 - 558 do ip = 1,kf - 559 t700vals(t700ct,ip) = t700vals(t700ct,ip) + f(ip) - 560 enddo - 561 else if (kpds(7) .eq. 500) then - 562 if (gott500pds .eq. 'n') then - 563 gott500pds = 'y' - 564 newvar = 't500 ' - 565 do i = 1,25 - 566 kholdt500pds(i) = kpds(i) - 567 enddo - 568 endif - 569 t500ct = t500ct + 1 - 570 do ip = 1,kf - 571 t500vals(t500ct,ip) = t500vals(t500ct,ip) + f(ip) - 572 enddo - 573 else if (kpds(7) .eq. 200) then - 574 if (gott200pds .eq. 'n') then - 575 gott200pds = 'y' - 576 newvar = 't200 ' - 577 do i = 1,25 - 578 kholdt200pds(i) = kpds(i) - 579 enddo - 580 endif - 581 t200ct = t200ct + 1 - 582 do ip = 1,kf - 583 t200vals(t200ct,ip) = t200vals(t200ct,ip) + f(ip) - 584 enddo - 585 else if (kpds(7) .eq. 2) then - 586 if (gott2mpds .eq. 'n') then - 587 gott2mpds = 'y' - 588 newvar = 't2m ' - 589 do i = 1,25 - 590 kholdt2mpds(i) = kpds(i) - 591 enddo - 592 endif - 593 t2mct = t2mct + 1 - 594 do ip = 1,kf - 595 t2mvals(t2mct,ip) = t2mvals(t2mct,ip) + f(ip) - 596 enddo - 597 endif - 598 c new batch 01/04 - 599 else if (kpds(5) .eq. 15) then - 600 if (kpds(7) .eq. 2) then - 601 if (gott2maxpds .eq. 'n') then - 602 gott2maxpds = 'y' - 603 newvar = 't2max ' - 604 do i = 1,25 - 605 kholdt2maxpds(i) = kpds(i) - 606 enddo - 607 endif - 608 t2maxct = t2maxct + 1 - 609 do ip = 1,kf - -Page 13 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 610 t2maxvals(t2maxct,ip) = t2maxvals(t2maxct,ip) + f(ip) - 611 enddo - 612 endif - 613 else if (kpds(5) .eq. 16) then - 614 if (kpds(7) .eq. 2) then - 615 if (gott2minpds .eq. 'n') then - 616 gott2minpds = 'y' - 617 newvar = 't2min ' - 618 do i = 1,25 - 619 kholdt2minpds(i) = kpds(i) - 620 enddo - 621 endif - 622 t2minct = t2minct + 1 - 623 do ip = 1,kf - 624 t2minvals(t2minct,ip) = t2minvals(t2minct,ip) + f(ip) - 625 enddo - 626 endif - 627 else if (kpds(5) .eq. 17) then - 628 if (kpds(7) .eq. 2) then - 629 if (gottd2mpds .eq. 'n') then - 630 gottd2mpds = 'y' - 631 newvar = 'td2m ' - 632 do i = 1,25 - 633 kholdtd2mpds(i) = kpds(i) - 634 enddo - 635 endif - 636 td2mct = td2mct + 1 - 637 do ip = 1,kf - 638 td2mvals(td2mct,ip) = td2mvals(td2mct,ip) + f(ip) - 639 enddo - 640 endif - 641 else if (kpds(5) .eq. 1) then - 642 if (kpds(7) .eq. 0) then - 643 if (gotpsfcpds .eq. 'n') then - 644 gotpsfcpds = 'y' - 645 newvar = 'psfc ' - 646 do i = 1,25 - 647 kholdpsfcpds(i) = kpds(i) - 648 enddo - 649 endif - 650 psfcct = psfcct + 1 - 651 do ip = 1,kf - 652 cJ.Peng---2011-05-17------------NCO change Surface Pressure---- - 653 c f(ip) = exp(f(ip)) - 654 - 655 psfcvals(psfcct,ip) = psfcvals(psfcct,ip) + f(ip) - 656 enddo - 657 endif - 658 else if (kpds(5) .eq. 71) then - 659 if (kpds(7) .eq. 0) then - 660 if (gottcdcpds .eq. 'n') then - 661 gottcdcpds = 'y' - 662 newvar = 'tcdc ' - 663 do i = 1,25 - 664 kholdtcdcpds(i) = kpds(i) - 665 enddo - 666 endif - -Page 14 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 667 tcdcct = tcdcct + 1 - 668 do ip = 1,kf - 669 f(ip) = f(ip) * 100.0 - 670 tcdcvals(tcdcct,ip) = tcdcvals(tcdcct,ip) + f(ip) - 671 enddo - 672 endif - 673 else if (kpds(5) .eq. 7) then - 674 if (kpds(7) .eq. 1000) then - 675 if (gotz1000pds .eq. 'n') then - 676 gotz1000pds = 'y' - 677 newvar = 'z1000 ' - 678 do i = 1,25 - 679 kholdz1000pds(i) = kpds(i) - 680 enddo - 681 endif - 682 z1000ct = z1000ct + 1 - 683 do ip = 1,kf - 684 z1000vals(z1000ct,ip) = z1000vals(z1000ct,ip) + f(ip) - 685 enddo - 686 else if (kpds(7).eq.850) then - 687 if (gotz850pds .eq. 'n') then - 688 gotz850pds = 'y' - 689 newvar = 'z850 ' - 690 do i = 1,25 - 691 kholdz850pds(i) = kpds(i) - 692 enddo - 693 endif - 694 z850ct = z850ct + 1 - 695 do ip = 1,kf - 696 z850vals(z850ct,ip) = z850vals(z850ct,ip) + f(ip) - 697 enddo - 698 else if (kpds(7).eq.700) then - 699 if (gotz700pds .eq. 'n') then - 700 gotz700pds = 'y' - 701 newvar = 'z700 ' - 702 do i = 1,25 - 703 kholdz700pds(i) = kpds(i) - 704 enddo - 705 endif - 706 z700ct = z700ct + 1 - 707 do ip = 1,kf - 708 z700vals(z700ct,ip) = z700vals(z700ct,ip) + f(ip) - 709 enddo - 710 else if (kpds(7).eq.500) then - 711 if (gotz500pds .eq. 'n') then - 712 gotz500pds = 'y' - 713 newvar = 'z500 ' - 714 do i = 1,25 - 715 kholdz500pds(i) = kpds(i) - 716 enddo - 717 endif - 718 z500ct = z500ct + 1 - 719 do ip = 1,kf - 720 z500vals(z500ct,ip) = z500vals(z500ct,ip) + f(ip) - 721 enddo - 722 else if (kpds(7).eq.200) then - 723 if (gotz200pds .eq. 'n') then - -Page 15 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 724 gotz200pds = 'y' - 725 newvar = 'z200 ' - 726 do i = 1,25 - 727 kholdz200pds(i) = kpds(i) - 728 enddo - 729 endif - 730 z200ct = z200ct + 1 - 731 do ip = 1,kf - 732 z200vals(z200ct,ip) = z200vals(z200ct,ip) + f(ip) - 733 enddo - 734 endif - 735 else if (kpds(5) .eq. 52) then - 736 if (kpds(7) .eq. 850) then - 737 if (gotrh850pds .eq. 'n') then - 738 gotrh850pds = 'y' - 739 newvar = 'rh850 ' - 740 do i = 1,25 - 741 kholdrh850pds(i) = kpds(i) - 742 enddo - 743 endif - 744 rh850ct = rh850ct + 1 - 745 do ip = 1,kf - 746 rh850vals(rh850ct,ip) = rh850vals(rh850ct,ip) + f(ip) - 747 enddo - 748 else if (kpds(7) .eq. 700) then - 749 if (gotrh700pds .eq. 'n') then - 750 gotrh700pds = 'y' - 751 newvar = 'rh700 ' - 752 do i = 1,25 - 753 kholdrh700pds(i) = kpds(i) - 754 enddo - 755 endif - 756 rh700ct = rh700ct + 1 - 757 do ip = 1,kf - 758 rh700vals(rh700ct,ip) = rh700vals(rh700ct,ip) + f(ip) - 759 enddo - 760 else if (kpds(7) .eq. 500) then - 761 if (gotrh500pds .eq. 'n') then - 762 gotrh500pds = 'y' - 763 newvar = 'rh500 ' - 764 do i = 1,25 - 765 kholdrh500pds(i) = kpds(i) - 766 enddo - 767 endif - 768 rh500ct = rh500ct + 1 - 769 do ip = 1,kf - 770 rh500vals(rh500ct,ip) = rh500vals(rh500ct,ip) + f(ip) - 771 enddo - 772 endif - 773 else if (kpds(5) .eq. 33) then - 774 if (kpds(7) .eq. 200) then - 775 if (gotu200 .eq. 'n') then - 776 gotu200 = 'y' - 777 newvar = 'u200' - 778 endif - 779 else if (kpds(7) .eq. 500) then - 780 if (gotu500 .eq. 'n') then - -Page 16 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 781 gotu500 = 'y' - 782 newvar = 'u500' - 783 endif - 784 else if (kpds(7) .eq. 700) then - 785 if (gotu700 .eq. 'n') then - 786 gotu700 = 'y' - 787 newvar = 'u700' - 788 endif - 789 else if (kpds(7) .eq. 850) then - 790 if (gotu850 .eq. 'n') then - 791 gotu850 = 'y' - 792 newvar = 'u850' - 793 endif - 794 else if (kpds(7) .eq. 10) then - 795 if (gotu10m .eq. 'n') then - 796 gotu10m = 'y' - 797 newvar = 'u10m' - 798 endif - 799 endif - 800 else if (kpds(5) .eq. 34) then - 801 if (kpds(7) .eq. 200) then - 802 if (gotv200 .eq. 'n') then - 803 gotv200 = 'y' - 804 newvar = 'v200' - 805 endif - 806 else if (kpds(7) .eq. 500) then - 807 if (gotv500 .eq. 'n') then - 808 gotv500 = 'y' - 809 newvar = 'v500' - 810 endif - 811 else if (kpds(7) .eq. 700) then - 812 if (gotv700 .eq. 'n') then - 813 gotv700 = 'y' - 814 newvar = 'v700' - 815 endif - 816 else if (kpds(7) .eq. 850) then - 817 if (gotv850 .eq. 'n') then - 818 gotv850 = 'y' - 819 newvar = 'v850' - 820 endif - 821 else if (kpds(7) .eq. 10) then - 822 if (gotv10m .eq. 'n') then - 823 gotv10m = 'y' - 824 newvar = 'v10m' - 825 endif - 826 endif - 827 endif - 828 - 829 if ( newvar .ne. 'none') then - 830 print *,'new variable ',newvar - 831 call srange(kf,lb,f) - 832 call grange(kf,lb,f,dmin,dmax) - 833 print '(4i5,4i3,2x,a1,i3,3i5,2x,i7,2g12.4)' - 834 & ,k,(kpds(i),i=5,11),'f',kpds(14),ktype,kfnum - 835 & ,ktot,kf,dmin,dmax - 836 write(*,71) (kpds(mm),mm=1,5) - 837 write(*,72) (kpds(mm),mm=6,10) - -Page 17 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 838 write(*,73) (kpds(mm),mm=11,15) - 839 write(*,74) (kpds(mm),mm=16,20) - 840 write(*,75) (kpds(mm),mm=21,25) - 841 write(*,76) (kgds(mm),mm=1,5) - 842 write(*,77) (kgds(mm),mm=6,10) - 843 write(*,78) (kgds(mm),mm=11,15) - 844 write(*,79) (kgds(mm),mm=16,20) - 845 write(*,80) (kgds(mm),mm=21,22) - 846 write(*,81) f(1),f(kf/4),f(kf/2) - 847 & ,f(3*kf/4),f(kf) - 848 endif - 849 - 850 call output (lugout,kf,kpds,kgds,lb,f,kens) - 851 - 852 call grib_close (lugout,ireto) - 853 if (ireto.gt.0) then - 854 print *,'ireto,lu from grib_close in ecmwfens = ',ireto,lugout - 855 endif - 856 - 857 - 858 if ( newvar .ne. 'none') then - 859 print *,' ' - 860 endif - 861 - 862 600 continue - 863 - 864 enddo - 865 call grib_close (lugb,ireto) - 866 if (ireto.gt.0) then - 867 print *,'ireto,lu from grib_close in ecmwfens = ',ireto,lugb - 868 endif - 869 call grib_close (lugi,ireto) - 870 if (ireto.gt.0) then - 871 print *,'ireto,lu from grib_close in ecmwfens = ',ireto,lugi - 872 endif - 873 - 874 if (gott200pds .eq. 'y') then - 875 call create_stats (numec,nlat,nlon - 876 & ,t200ct,t200vals,t200mean,t200spr) - 877 call output_stats ('t200',151,nlat*nlon,kholdt200pds,kgds - 878 & ,lb,t200mean,t200spr) - 879 else - 880 print *,'no statistics for t200' - 881 endif - 882 if (gott500pds .eq. 'y') then - 883 call create_stats (numec,nlat,nlon - 884 & ,t500ct,t500vals,t500mean,t500spr) - 885 call output_stats ('t500',152,nlat*nlon,kholdt500pds,kgds - 886 & ,lb,t500mean,t500spr) - 887 else - 888 print *,'no statistics for t500' - 889 endif - 890 if (gott700pds .eq. 'y') then - 891 call create_stats (numec,nlat,nlon - 892 & ,t700ct,t700vals,t700mean,t700spr) - 893 call output_stats ('t700',153,nlat*nlon,kholdt700pds,kgds - 894 & ,lb,t700mean,t700spr) - -Page 18 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 895 else - 896 print *,'no statistics for t700' - 897 endif - 898 if (gott850pds .eq. 'y') then - 899 call create_stats (numec,nlat,nlon - 900 & ,t850ct,t850vals,t850mean,t850spr) - 901 call output_stats ('t850',154,nlat*nlon,kholdt850pds,kgds - 902 & ,lb,t850mean,t850spr) - 903 else - 904 print *,'no statistics for t850' - 905 endif - 906 if (gott2mpds .eq. 'y') then - 907 call create_stats (numec,nlat,nlon - 908 & ,t2mct,t2mvals,t2mmean,t2mspr) - 909 call output_stats ('t2m ',155,nlat*nlon,kholdt2mpds,kgds - 910 & ,lb,t2mmean,t2mspr) - 911 else - 912 print *,'no statistics for t2m' - 913 endif - 914 if (gott2maxpds .eq. 'y') then - 915 call create_stats (numec,nlat,nlon - 916 & ,t2maxct,t2maxvals,t2maxmean,t2maxspr) - 917 call output_stats ('t2mx',156,nlat*nlon,kholdt2maxpds,kgds - 918 & ,lb,t2maxmean,t2maxspr) - 919 else - 920 print *,'no statistics for t2max' - 921 endif - 922 if (gott2minpds .eq. 'y') then - 923 call create_stats (numec,nlat,nlon - 924 & ,t2minct,t2minvals,t2minmean,t2minspr) - 925 call output_stats ('t2mn',157,nlat*nlon,kholdt2minpds,kgds - 926 & ,lb,t2minmean,t2minspr) - 927 else - 928 print *,'no statistics for t2min' - 929 endif - 930 if (gottd2mpds .eq. 'y') then - 931 call create_stats (numec,nlat,nlon - 932 & ,td2mct,td2mvals,td2mmean,td2mspr) - 933 call output_stats ('td2m',158,nlat*nlon,kholdtd2mpds,kgds - 934 & ,lb,td2mmean,td2mspr) - 935 else - 936 print *,'no statistics for td2m' - 937 endif - 938 - 939 if (gotz200pds .eq. 'y') then - 940 call create_stats (numec,nlat,nlon - 941 & ,z200ct,z200vals,z200mean,z200spr) - 942 call output_stats ('z200',161,nlat*nlon,kholdz200pds,kgds - 943 & ,lb,z200mean,z200spr) - 944 else - 945 print *,'no statistics for z200' - 946 endif - 947 if (gotz500pds .eq. 'y') then - 948 call create_stats (numec,nlat,nlon - 949 & ,z500ct,z500vals,z500mean,z500spr) - 950 call output_stats ('z500',162,nlat*nlon,kholdz500pds,kgds - 951 & ,lb,z500mean,z500spr) - -Page 19 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 952 else - 953 print *,'no statistics for z500' - 954 endif - 955 if (gotz700pds .eq. 'y') then - 956 call create_stats (numec,nlat,nlon - 957 & ,z700ct,z700vals,z700mean,z700spr) - 958 call output_stats ('z700',163,nlat*nlon,kholdz700pds,kgds - 959 & ,lb,z700mean,z700spr) - 960 else - 961 print *,'no statistics for z700' - 962 endif - 963 if (gotz850pds .eq. 'y') then - 964 call create_stats (numec,nlat,nlon - 965 & ,z850ct,z850vals,z850mean,z850spr) - 966 call output_stats ('z850',164,nlat*nlon,kholdz850pds,kgds - 967 & ,lb,z850mean,z850spr) - 968 else - 969 print *,'no statistics for z850' - 970 endif - 971 if (gotz1000pds .eq. 'y') then - 972 call create_stats (numec,nlat,nlon - 973 & ,z1000ct,z1000vals,z1000mean,z1000spr) - 974 call output_stats ('z1k ',165,nlat*nlon,kholdz1000pds,kgds - 975 & ,lb,z1000mean,z1000spr) - 976 else - 977 print *,'no statistics for z1000' - 978 endif - 979 - 980 if (gotrh500pds .eq. 'y') then - 981 call create_stats (numec,nlat,nlon - 982 & ,rh500ct,rh500vals,rh500mean,rh500spr) - 983 call output_stats ('r500',171,nlat*nlon,kholdrh500pds,kgds - 984 & ,lb,rh500mean,rh500spr) - 985 else - 986 print *,'no statistics for rh500' - 987 endif - 988 if (gotrh700pds .eq. 'y') then - 989 call create_stats (numec,nlat,nlon - 990 & ,rh700ct,rh700vals,rh700mean,rh700spr) - 991 call output_stats ('r700',172,nlat*nlon,kholdrh700pds,kgds - 992 & ,lb,rh700mean,rh700spr) - 993 else - 994 print *,'no statistics for rh700' - 995 endif - 996 if (gotrh850pds .eq. 'y') then - 997 call create_stats (numec,nlat,nlon - 998 & ,rh850ct,rh850vals,rh850mean,rh850spr) - 999 call output_stats ('r850',173,nlat*nlon,kholdrh850pds,kgds - 1000 & ,lb,rh850mean,rh850spr) - 1001 else - 1002 print *,'no statistics for rh850' - 1003 endif - 1004 - 1005 if (gotmslppds .eq. 'y') then - 1006 call create_stats (numec,nlat,nlon - 1007 & ,mslpct,mslpvals,mslpmean,mslpspr) - 1008 call output_stats ('mslp',181,nlat*nlon,kholdmslppds,kgds - -Page 20 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 1009 & ,lb,mslpmean,mslpspr) - 1010 else - 1011 print *,'no statistics for mslp' - 1012 endif - 1013 if (gotpsfcpds .eq. 'y') then - 1014 call create_stats (numec,nlat,nlon - 1015 & ,psfcct,psfcvals,psfcmean,psfcspr) - 1016 call output_stats ('psfc',182,nlat*nlon,kholdpsfcpds,kgds - 1017 & ,lb,psfcmean,psfcspr) - 1018 else - 1019 print *,'no statistics for psfc' - 1020 endif - 1021 if (gotprcppds .eq. 'y') then - 1022 call create_stats (numec,nlat,nlon - 1023 & ,prcpct,prcpvals,prcpmean,prcpspr) - 1024 call output_stats ('prcp',183,nlat*nlon,kholdprcppds,kgds - 1025 & ,lb,prcpmean,prcpspr) - 1026 else - 1027 print *,'no statistics for prcp' - 1028 endif - 1029 if (gottcdcpds .eq. 'y') then - 1030 call create_stats (numec,nlat,nlon - 1031 & ,tcdcct,tcdcvals,tcdcmean,tcdcspr) - 1032 call output_stats ('tcdc',184,nlat*nlon,kholdtcdcpds,kgds - 1033 & ,lb,tcdcmean,tcdcspr) - 1034 else - 1035 print *,'no statistics for tcdc' - 1036 endif - 1037 - 1038 71 format('p1= ',i7,' p2= ',i7,' p3= ',i7,' p4= ',i7,' p5= ',i7) - 1039 72 format('p6= ',i7,' p7= ',i7,' p8= ',i7,' p9= ',i7,' p10= ',i7) - 1040 73 format('p11= ',i7,' p12= ',i7,' p13= ',i7,' p14= ',i7,' p15= ',i7) - 1041 74 format('p16= ',i7,' p17= ',i7,' p18= ',i7,' p19= ',i7,' p20= ',i7) - 1042 75 format('p21= ',i7,' p22= ',i7,' p23= ',i7,' p24= ',i7,' p25= ',i7) - 1043 76 format('g1= ',i7,' g2= ',i7,' g3= ',i7,' g4= ',i7,' g5= ',i7) - 1044 77 format('g6= ',i7,' g7= ',i7,' g8= ',i7,' g9= ',i7,' g10= ',i7) - 1045 78 format('g11= ',i7,' g12= ',i7,' g13= ',i7,' g14= ',i7,' g15= ',i7) - 1046 79 format('g16= ',i7,' g17= ',i7,' g18= ',i7,' g19= ',i7,' g20= ',i7) - 1047 80 format('g21= ',i7,' g22= ',i7) - 1048 81 format('f(1)= ',g12.4,' f(kf/4)= ',g12.4,' f(kf/2)= ',g12.4 - 1049 & ,' f(3*kf/4)= ',g12.4,' f(kf)= ',g12.4) - 1050 c - 1051 700 continue - 1052 call w3tage('ECMWFENS') - 1053 stop - 1054 end - -Page 21 Source Listing ECMWFENSH -2020-06-22 00:51 Entry Points ecmwfensh.f - - - -ENTRY POINTS - - Name - - MAIN__ - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - 1000 Label 236 235 - 600 Label 862 473,478 - 700 Label 1051 - 71 Label 1038 394,836 - 72 Label 1039 395,837 - 73 Label 1040 396,838 - 74 Label 1041 397,839 - 75 Label 1042 398,840 - 76 Label 1043 399,841 - 77 Label 1044 400,842 - 78 Label 1045 401,843 - 79 Label 1046 402,844 - 80 Label 1047 403,845 - 81 Label 1048 404,846 - ADJEXT Subr 485 485 - ADJPDS Subr 476 476 - CONTFLAG Local 139 CHAR 1 scalar 476,478 - CREATE_STATS Subr 875 875,883,891,899,907,915,923,931,94 - 0,948,956,964,972,981,989,997,1006 - ,1014,1022,1030 - DMAX Local 464 R(8) 8 scalar 464,467,832,835 - DMIN Local 464 R(8) 8 scalar 464,467,832,835 - ECMWFENSH Prog 1 - F Local 190 R(8) 8 1 131072 388,404,405,406,463,464,520,521,53 - 1,534,547,559,571,583,595,610,624, - 638,655,669,670,684,696,708,720,73 - 2,746,758,770,831,832,846,847,850 - GETGBECE Subr 387 387 - GOTMSLPPDS Local 145 CHAR 1 scalar 326,524,525,1005 - GOTPRCPPDS Local 145 CHAR 1 scalar 328,511,512,1021 - GOTPSFCPDS Local 145 CHAR 1 scalar 327,643,644,1013 - GOTRH500PDS Local 144 CHAR 1 scalar 322,761,762,980 - GOTRH700PDS Local 144 CHAR 1 scalar 323,749,750,988 - GOTRH850PDS Local 144 CHAR 1 scalar 324,737,738,996 - GOTT200PDS Local 140 CHAR 1 scalar 307,574,575,874 - GOTT2MAXPDS Local 141 CHAR 1 scalar 312,601,602,914 - GOTT2MINPDS Local 141 CHAR 1 scalar 313,615,616,922 - GOTT2MPDS Local 141 CHAR 1 scalar 311,586,587,906 - GOTT500PDS Local 140 CHAR 1 scalar 308,562,563,882 - GOTT700PDS Local 140 CHAR 1 scalar 309,550,551,890 - GOTT850PDS Local 140 CHAR 1 scalar 310,538,539,898 - GOTTCDCPDS Local 145 CHAR 1 scalar 329,660,661,1029 - GOTTD2MPDS Local 141 CHAR 1 scalar 314,629,630,930 - GOTU10M Local 150 CHAR 1 scalar 795,796 - -Page 22 Source Listing ECMWFENSH -2020-06-22 00:51 Symbol Table ecmwfensh.f - - Name Object Declared Type Bytes Dimen Elements Attributes References - - GOTU200 Local 146 CHAR 1 scalar 775,776 - GOTU500 Local 147 CHAR 1 scalar 780,781 - GOTU700 Local 148 CHAR 1 scalar 785,786 - GOTU850 Local 149 CHAR 1 scalar 790,791 - GOTV10M Local 150 CHAR 1 scalar 822,823 - GOTV200 Local 146 CHAR 1 scalar 802,803 - GOTV500 Local 147 CHAR 1 scalar 807,808 - GOTV700 Local 148 CHAR 1 scalar 812,813 - GOTV850 Local 149 CHAR 1 scalar 817,818 - GOTZ1000PDS Local 143 CHAR 1 scalar 320,675,676,971 - GOTZ200PDS Local 142 CHAR 1 scalar 316,723,724,939 - GOTZ500PDS Local 142 CHAR 1 scalar 317,711,712,947 - GOTZ700PDS Local 142 CHAR 1 scalar 318,699,700,955 - GOTZ850PDS Local 142 CHAR 1 scalar 319,687,688,963 - GRANGE Subr 464 464,832 - GRIB_CLOSE Subr 852 852,865,869 - GRIB_OPEN_WA Subr 480 480 - I Local 466 I(4) 4 scalar 466,514,515,527,528,541,542,553,55 - 4,565,566,577,578,589,590,604,605, - 618,619,632,633,646,647,663,664,67 - 8,679,690,691,702,703,714,715,726, - 727,740,741,752,753,764,765,834 - ICHK Local 408 I(4) 4 scalar 408,409,412,415,416,419,422,423,42 - 6,429,430,433,436,437,440,443,444, - 447 - IP Local 519 I(4) 4 scalar 519,520,521,533,534,546,547,558,55 - 9,570,571,582,583,594,595,609,610, - 623,624,637,638,651,655,668,669,67 - 0,683,684,695,696,707,708,719,720, - 731,732,745,746,757,758,769,770 - IRET Local 371 I(4) 4 scalar 371,374,388,455,459,471 - IRETO Local 480 I(4) 4 scalar 480,481,482,852,853,854,865,866,86 - 7,869,870,871 - J Local 370 I(4) 4 scalar 370,387,453,456,471 - JENS Local 152 I(4) 4 1 200 362,387,423,426 - JF Param 137 I(4) 4 scalar 189,190,387 - JGDS Local 152 I(4) 4 1 200 361,387,416,419 - JPDS Local 152 I(4) 4 1 200 360,363,387,409,412 - K Local 388 I(4) 4 scalar 388,453,457,466,834 - KENS Local 153 I(4) 4 1 200 378,388,444,447,485,850 - KF Local 388 I(4) 4 scalar 388,404,405,406,463,464,467,519,53 - 1,533,546,558,570,582,594,609,623, - 637,651,668,683,695,707,719,731,74 - 5,757,769,831,832,835,846,847,850 - KFNUM Local 389 I(4) 4 scalar 389,458,460,466,485,834 - KGDS Local 153 I(4) 4 1 200 364,388,399,400,401,402,403,437,44 - 0,841,842,843,844,845,850,877,885, - 893,901,909,917,925,933,942,950,95 - 8,966,974,983,991,999,1008,1016,10 - 24,1032 - KHOLDMSLPPDS Local 179 I(4) 4 1 200 528,1008 - KHOLDPRCPPDS Local 181 I(4) 4 1 200 515,1024 - KHOLDPSFCPDS Local 180 I(4) 4 1 200 647,1016 - KHOLDRH500PDS Local 176 I(4) 4 1 200 765,983 - KHOLDRH700PDS Local 177 I(4) 4 1 200 753,991 - -Page 23 Source Listing ECMWFENSH -2020-06-22 00:51 Symbol Table ecmwfensh.f - - Name Object Declared Type Bytes Dimen Elements Attributes References - - KHOLDRH850PDS Local 178 I(4) 4 1 200 741,999 - KHOLDT200PDS Local 163 I(4) 4 1 200 578,877 - KHOLDT2MAXPDS Local 168 I(4) 4 1 200 605,917 - KHOLDT2MINPDS Local 169 I(4) 4 1 200 619,925 - KHOLDT2MPDS Local 167 I(4) 4 1 200 590,909 - KHOLDT500PDS Local 164 I(4) 4 1 200 566,885 - KHOLDT700PDS Local 165 I(4) 4 1 200 554,893 - KHOLDT850PDS Local 166 I(4) 4 1 200 542,901 - KHOLDTCDCPDS Local 182 I(4) 4 1 200 664,1032 - KHOLDTD2MPDS Local 170 I(4) 4 1 200 633,933 - KHOLDZ1000PDS Local 175 I(4) 4 1 200 679,974 - KHOLDZ200PDS Local 171 I(4) 4 1 200 727,942 - KHOLDZ500PDS Local 172 I(4) 4 1 200 715,950 - KHOLDZ700PDS Local 173 I(4) 4 1 200 703,958 - KHOLDZ850PDS Local 174 I(4) 4 1 200 691,966 - KMAXMEM Local 231 I(4) 4 scalar 231,241,359 - KPDS Local 153 I(4) 4 1 200 377,388,394,395,396,397,398,430,43 - 3,457,458,461,466,476,506,510,515, - 523,528,536,537,542,549,554,561,56 - 6,573,578,585,590,599,600,605,613, - 614,619,627,628,633,641,642,647,65 - 8,659,664,673,674,679,686,691,698, - 703,710,715,722,727,735,736,741,74 - 8,753,760,765,773,774,779,784,789, - 794,800,801,806,811,816,821,834,83 - 6,837,838,839,840,850 - KPDSNULL Local 376 I(4) 4 scalar 376,377,378,430,444 - KPDSREAD Local 367 I(4) 4 scalar 367,376,391,412,419,426,433,440,44 - 7,451 - KRES Local 231 I(4) 4 scalar 231,240,485 - KTOT Local 389 I(4) 4 scalar 389,467,835 - KTYPE Local 389 I(4) 4 scalar 389,466,485,834 - LB Local 189 L(4) 4 1 131072 388,406,463,464,531,831,832,850,87 - 8,886,894,902,910,918,926,934,943, - 951,959,967,975,984,992,1000,1009, - 1017,1025,1033 - LUGB Param 137 I(4) 4 scalar 387,865,867 - LUGI Param 137 I(4) 4 scalar 387,869,871 - LUGOUT Local 476 I(4) 4 scalar 476,480,482,850,852,854 - MAXLOOP Local 359 I(4) 4 scalar 359 - MEMBERCT Local 183 I(4) 4 1 2 365,476 - MM Local 394 I(4) 4 scalar 394,395,396,397,398,399,400,401,40 - 2,403,836,837,838,839,840,841,842, - 843,844,845 - MSLPCT Local 188 I(4) 4 scalar 350,532,534,1007 - MSLPMEAN Local 224 R(8) 8 1 65160 295,1007,1009 - MSLPSPR Local 224 R(8) 8 1 65160 296,1007,1009 - MSLPVALS Local 223 R(8) 8 2 3388320 294,534,1007 - NAMIN Local 231 scalar 235 - NEWVAR Local 151 CHAR 8 scalar 508,513,526,540,552,564,576,588,60 - 3,617,631,645,662,677,689,701,713, - 725,739,751,763,777,782,787,792,79 - 7,804,809,814,819,824,829,830,858 - NLAT Param 137 I(4) 4 scalar 191,192,193,194,195,196,197,198,19 - 9,200,201,202,203,204,205,206,207, - -Page 24 Source Listing ECMWFENSH -2020-06-22 00:51 Symbol Table ecmwfensh.f - - Name Object Declared Type Bytes Dimen Elements Attributes References - - 208,209,210,211,212,213,214,215,21 - 6,217,218,219,220,221,222,223,224, - 225,226,227,228,229,230,875,877,88 - 3,885,891,893,899,901,907,909,915, - 917,923,925,931,933,940,942,948,95 - 0,956,958,964,966,972,974,981,983, - 989,991,997,999,1006,1008,1014,101 - 6,1022,1024,1030,1032 - NLON Param 137 I(4) 4 scalar 191,192,193,194,195,196,197,198,19 - 9,200,201,202,203,204,205,206,207, - 208,209,210,211,212,213,214,215,21 - 6,217,218,219,220,221,222,223,224, - 225,226,227,228,229,230,875,877,88 - 3,885,891,893,899,901,907,909,915, - 917,923,925,931,933,940,942,948,95 - 0,956,958,964,966,972,974,981,983, - 989,991,997,999,1006,1008,1014,101 - 6,1022,1024,1030,1032 - NUMEC Param 138 I(4) 4 scalar 191,193,195,197,199,201,203,205,20 - 7,209,211,213,215,217,219,221,223, - 225,227,229,875,883,891,899,907,91 - 5,923,931,940,948,956,964,972,981, - 989,997,1006,1014,1022,1030 - OUTPUT Subr 850 850 - OUTPUT_STATS Subr 877 877,885,893,901,909,917,925,933,94 - 2,950,958,966,974,983,991,999,1008 - ,1016,1024,1032 - PRCPCT Local 188 I(4) 4 scalar 352,518,521,1023 - PRCPMEAN Local 228 R(8) 8 1 65160 301,1023,1025 - PRCPSPR Local 228 R(8) 8 1 65160 302,1023,1025 - PRCPVALS Local 227 R(8) 8 2 3388320 300,521,1023 - PSFCCT Local 188 I(4) 4 scalar 351,650,655,1015 - PSFCMEAN Local 226 R(8) 8 1 65160 298,1015,1017 - PSFCSPR Local 226 R(8) 8 1 65160 299,1015,1017 - PSFCVALS Local 225 R(8) 8 2 3388320 297,655,1015 - RH500CT Local 187 I(4) 4 scalar 346,768,770,982 - RH500MEAN Local 218 R(8) 8 1 65160 285,982,984 - RH500SPR Local 218 R(8) 8 1 65160 286,982,984 - RH500VALS Local 217 R(8) 8 2 3388320 284,770,982 - RH700CT Local 187 I(4) 4 scalar 347,756,758,990 - RH700MEAN Local 220 R(8) 8 1 65160 288,990,992 - RH700SPR Local 220 R(8) 8 1 65160 289,990,992 - RH700VALS Local 219 R(8) 8 2 3388320 287,758,990 - RH850CT Local 187 I(4) 4 scalar 348,744,746,998 - RH850MEAN Local 222 R(8) 8 1 65160 291,998,1000 - RH850SPR Local 222 R(8) 8 1 65160 292,998,1000 - RH850VALS Local 221 R(8) 8 2 3388320 290,746,998 - RJENS Local 155 R(8) 8 1 204 426 - RJGDS Local 155 R(8) 8 1 204 419 - RJPDS Local 155 R(8) 8 1 204 412 - RKENS Local 159 R(8) 8 1 204 447 - RKGDS Local 159 R(8) 8 1 204 440 - RKPDS Local 159 R(8) 8 1 204 433 - SRANGE Subr 406 406,463,531,831 - T200CT Local 184 I(4) 4 scalar 331,581,583,876 - -Page 25 Source Listing ECMWFENSH -2020-06-22 00:51 Symbol Table ecmwfensh.f - - Name Object Declared Type Bytes Dimen Elements Attributes References - - T200MEAN Local 192 R(8) 8 1 65160 244,876,878 - T200SPR Local 192 R(8) 8 1 65160 245,876,878 - T200VALS Local 191 R(8) 8 2 3388320 243,583,876 - T2MAXCT Local 185 I(4) 4 scalar 336,608,610,916 - T2MAXMEAN Local 202 R(8) 8 1 65160 259,916,918 - T2MAXSPR Local 202 R(8) 8 1 65160 260,916,918 - T2MAXVALS Local 201 R(8) 8 2 3388320 258,610,916 - T2MCT Local 185 I(4) 4 scalar 335,593,595,908 - T2MINCT Local 185 I(4) 4 scalar 337,622,624,924 - T2MINMEAN Local 204 R(8) 8 1 65160 262,924,926 - T2MINSPR Local 204 R(8) 8 1 65160 263,924,926 - T2MINVALS Local 203 R(8) 8 2 3388320 261,624,924 - T2MMEAN Local 200 R(8) 8 1 65160 256,908,910 - T2MSPR Local 200 R(8) 8 1 65160 257,908,910 - T2MVALS Local 199 R(8) 8 2 3388320 255,595,908 - T500CT Local 184 I(4) 4 scalar 332,569,571,884 - T500MEAN Local 194 R(8) 8 1 65160 247,884,886 - T500SPR Local 194 R(8) 8 1 65160 248,884,886 - T500VALS Local 193 R(8) 8 2 3388320 246,571,884 - T700CT Local 184 I(4) 4 scalar 333,557,559,892 - T700MEAN Local 196 R(8) 8 1 65160 250,892,894 - T700SPR Local 196 R(8) 8 1 65160 251,892,894 - T700VALS Local 195 R(8) 8 2 3388320 249,559,892 - T850CT Local 184 I(4) 4 scalar 334,545,547,900 - T850MEAN Local 198 R(8) 8 1 65160 253,900,902 - T850SPR Local 198 R(8) 8 1 65160 254,900,902 - T850VALS Local 197 R(8) 8 2 3388320 252,547,900 - TCDCCT Local 188 I(4) 4 scalar 353,667,670,1031 - TCDCMEAN Local 230 R(8) 8 1 65160 304,1031,1033 - TCDCSPR Local 230 R(8) 8 1 65160 305,1031,1033 - TCDCVALS Local 229 R(8) 8 2 3388320 303,670,1031 - TD2MCT Local 185 I(4) 4 scalar 338,636,638,932 - TD2MMEAN Local 206 R(8) 8 1 65160 265,932,934 - TD2MSPR Local 206 R(8) 8 1 65160 266,932,934 - TD2MVALS Local 205 R(8) 8 2 3388320 264,638,932 - W3TAGB Subr 233 233 - W3TAGE Subr 1052 1052 - Z1000CT Local 186 I(4) 4 scalar 344,682,684,973 - Z1000MEAN Local 216 R(8) 8 1 65160 281,973,975 - Z1000SPR Local 216 R(8) 8 1 65160 282,973,975 - Z1000VALS Local 215 R(8) 8 2 3388320 280,684,973 - Z200CT Local 186 I(4) 4 scalar 340,730,732,941 - Z200MEAN Local 208 R(8) 8 1 65160 269,941,943 - Z200SPR Local 208 R(8) 8 1 65160 270,941,943 - Z200VALS Local 207 R(8) 8 2 3388320 268,732,941 - Z500CT Local 186 I(4) 4 scalar 341,718,720,949 - Z500MEAN Local 210 R(8) 8 1 65160 272,949,951 - Z500SPR Local 210 R(8) 8 1 65160 273,949,951 - Z500VALS Local 209 R(8) 8 2 3388320 271,720,949 - Z700CT Local 186 I(4) 4 scalar 342,706,708,957 - Z700MEAN Local 212 R(8) 8 1 65160 275,957,959 - Z700SPR Local 212 R(8) 8 1 65160 276,957,959 - Z700VALS Local 211 R(8) 8 2 3388320 274,708,957 - Z850CT Local 186 I(4) 4 scalar 343,694,696,965 - Z850MEAN Local 214 R(8) 8 1 65160 278,965,967 - -Page 26 Source Listing ECMWFENSH -2020-06-22 00:51 Symbol Table ecmwfensh.f - - Name Object Declared Type Bytes Dimen Elements Attributes References - - Z850SPR Local 214 R(8) 8 1 65160 279,965,967 - Z850VALS Local 213 R(8) 8 2 3388320 277,696,965 - -Page 27 Source Listing ECMWFENSH -2020-06-22 00:51 ecmwfensh.f - - 1055 c - 1056 c----------------------------------------------------------------------c - 1057 c----------------------------------------------------------------------c - 1058 c - 1059 subroutine create_stats (numec,nlat,nlon,nct,vals,vmean,vspr) - 1060 c$$$ SUBPROGRAM DOCUMENTATION BLOCK - 1061 C . . . . - 1062 C SUBPROGRAM: create_stats - 1063 C PRGMMR: WOBUS ORG: NP20 DATE: 2004-01-26 - 1064 C - 1065 C ABSTRACT: START ABSTRACT HERE AND INDENT TO COLUMN 5 ON THE - 1066 C FOLLOWING LINES. PLEASE PROVIDE A BRIEF DESCRIPTION OF - 1067 C WHAT THE SUBPROGRAM DOES. - 1068 C - 1069 C PROGRAM HISTORY LOG: - 1070 C 97-01-17 MARCHOK original program - 1071 C 01-01-16 WOBUS added DOCBLOCK - 1072 C 04-01-26 WOBUS rearranged to process one variable - 1073 C - 1074 C USAGE: CALL PROGRAM-NAME(INARG1, INARG2, WRKARG, OUTARG1, ... ) - 1075 C INPUT ARGUMENT LIST: - 1076 C INARG1 - GENERIC DESCRIPTION, INCLUDING CONTENT, UNITS, - 1077 C INARG2 - TYPE. EXPLAIN FUNCTION IF CONTROL VARIABLE. - 1078 C - 1079 C OUTPUT ARGUMENT LIST: (INCLUDING WORK ARRAYS) - 1080 C WRKARG - GENERIC DESCRIPTION, ETC., AS ABOVE. - 1081 C OUTARG1 - EXPLAIN COMPLETELY IF ERROR RETURN - 1082 C ERRFLAG - EVEN IF MANY LINES ARE NEEDED - 1083 C - 1084 C INPUT FILES: (DELETE IF NO INPUT FILES IN SUBPROGRAM) - 1085 C - 1086 C OUTPUT FILES: (DELETE IF NO OUTPUT FILES IN SUBPROGRAM) - 1087 C - 1088 C REMARKS: LIST CAVEATS, OTHER HELPFUL HINTS OR INFORMATION - 1089 C - 1090 C ATTRIBUTES: - 1091 C LANGUAGE: INDICATE EXTENSIONS, COMPILER OPTIONS - 1092 C MACHINE: IBM SP - 1093 C - 1094 C$$$ - 1095 c - 1096 real vals(numec,nlat*nlon) - 1097 real vmean(nlat*nlon) - 1098 real vspr(nlat*nlon) - 1099 integer nct - 1100 c - 1101 npts = nlon * nlat - 1102 vmean = 0.0 - 1103 - 1104 c ----------------------------------------------------- - 1105 c MEAN & SPREAD - 1106 c ----------------------------------------------------- - 1107 - 1108 if (nct .gt. 0) then - 1109 - 1110 c sum up all values from all members at all points - 1111 - -Page 28 Source Listing CREATE_STATS -2020-06-22 00:51 ecmwfensh.f - - 1112 do imem = 1,nct - 1113 do n = 1,npts - 1114 vmean(n) = vmean(n) + vals(imem,n) - 1115 enddo - 1116 enddo - 1117 - 1118 c calculate mean - 1119 - 1120 do n = 1,npts - 1121 vmean(n) = vmean(n) / float(nct) - 1122 enddo - 1123 - 1124 c calculate standard deviation - 1125 - 1126 if (nct .gt. 1) then - 1127 - 1128 do n = 1,npts - 1129 varnce = 0.0 - 1130 do imem = 1,nct - 1131 xdiff = vals(imem,n) - vmean(n) - 1132 xdiffsqr = xdiff * xdiff - 1133 varnce = varnce + xdiffsqr - 1134 enddo - 1135 vspr(n) = sqrt(varnce/float(nct)) - 1136 enddo - 1137 print *,' in create_stats imem=',imem,' nct=',nct - 1138 - 1139 else - 1140 - 1141 do n = 1,npts - 1142 vspr(n) = 0.00 - 1143 enddo - 1144 - 1145 endif - 1146 - 1147 else - 1148 - 1149 do n = 1,npts - 1150 vmean(n) = -99.0 - 1151 vspr(n) = -99.0 - 1152 enddo - 1153 - 1154 endif - 1155 - 1156 return - 1157 end - -Page 29 Source Listing CREATE_STATS -2020-06-22 00:51 Entry Points ecmwfensh.f - - - -ENTRY POINTS - - Name - - create_stats_ - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - CREATE_STATS Subr 1059 - FLOAT Func 1121 scalar 1121,1135 - IMEM Local 1112 I(4) 4 scalar 1112,1114,1130,1131,1137 - N Local 1113 I(4) 4 scalar 1113,1114,1120,1121,1128,1131,1135 - ,1141,1142,1149,1150,1151 - NCT Dummy 1059 I(4) 4 scalar ARG,INOUT 1108,1112,1121,1126,1130,1135,1137 - NLAT Dummy 1059 I(4) 4 scalar ARG,INOUT 1096,1097,1098,1101 - NLON Dummy 1059 I(4) 4 scalar ARG,INOUT 1096,1097,1098,1101 - NPTS Local 1101 I(4) 4 scalar 1101,1113,1120,1128,1141,1149 - NUMEC Dummy 1059 I(4) 4 scalar ARG,INOUT 1096 - SQRT Func 1135 scalar 1135 - VALS Dummy 1059 R(8) 8 2 0 ARG,INOUT 1114,1131 - VARNCE Local 1129 R(8) 8 scalar 1129,1133,1135 - VMEAN Dummy 1059 R(8) 8 1 0 ARG,INOUT 1102,1114,1121,1131,1150 - VSPR Dummy 1059 R(8) 8 1 0 ARG,INOUT 1135,1142,1151 - XDIFF Local 1131 R(8) 8 scalar 1131,1132 - XDIFFSQR Local 1132 R(8) 8 scalar 1132,1133 - -Page 30 Source Listing CREATE_STATS -2020-06-22 00:51 ecmwfensh.f - - 1158 c - 1159 c----------------------------------------------------------------------c - 1160 c----------------------------------------------------------------------c - 1161 c - 1162 subroutine adjpds (kpds,contflag,lugout,memberct) - 1163 c$$$ SUBPROGRAM DOCUMENTATION BLOCK - 1164 C . . . . - 1165 C SUBPROGRAM: adjpds - 1166 C PRGMMR: WOBUS ORG: NP20 DATE: 2001-01-16 - 1167 C - 1168 C ABSTRACT: START ABSTRACT HERE AND INDENT TO COLUMN 5 ON THE - 1169 C FOLLOWING LINES. PLEASE PROVIDE A BRIEF DESCRIPTION OF - 1170 C WHAT THE SUBPROGRAM DOES. - 1171 C - 1172 C PROGRAM HISTORY LOG: - 1173 C 97-01-17 MARCHOK original program - 1174 C 01-01-16 WOBUS added DOCBLOCK - 1175 C - 1176 C USAGE: CALL PROGRAM-NAME(INARG1, INARG2, WRKARG, OUTARG1, ... ) - 1177 C INPUT ARGUMENT LIST: - 1178 C INARG1 - GENERIC DESCRIPTION, INCLUDING CONTENT, UNITS, - 1179 C INARG2 - TYPE. EXPLAIN FUNCTION IF CONTROL VARIABLE. - 1180 C - 1181 C OUTPUT ARGUMENT LIST: (INCLUDING WORK ARRAYS) - 1182 C WRKARG - GENERIC DESCRIPTION, ETC., AS ABOVE. - 1183 C OUTARG1 - EXPLAIN COMPLETELY IF ERROR RETURN - 1184 C ERRFLAG - EVEN IF MANY LINES ARE NEEDED - 1185 C - 1186 C INPUT FILES: (DELETE IF NO INPUT FILES IN SUBPROGRAM) - 1187 C - 1188 C OUTPUT FILES: (DELETE IF NO OUTPUT FILES IN SUBPROGRAM) - 1189 C - 1190 C REMARKS: LIST CAVEATS, OTHER HELPFUL HINTS OR INFORMATION - 1191 C - 1192 C ATTRIBUTES: - 1193 C LANGUAGE: INDICATE EXTENSIONS, COMPILER OPTIONS - 1194 C MACHINE: IBM SP - 1195 C - 1196 C$$$ - 1197 c - 1198 c **************************************************** - 1199 c ADJUST GRIB PARAMETER FROM THE ECMWF TABLE VALUES - 1200 c TO NCEP TABLE VALUES AS FOLLOWS: - 1201 c - 1202 c Parameter ECMWF Grib Parm NCEP Grib Parm - 1203 c ------------- --------------- -------------- - 1204 c u-comp (std lev) 131 33 - 1205 c v-comp (std lev) 132 34 - 1206 c gp height 156 7 - 1207 c relative humidity 157 52 - 1208 c temperature 130 11 - 1209 c mslp 151 2 - 1210 c total precip 228 61 - 1211 c - 1212 c - new batch 1/04 - 1213 c 2 meter temp 167 1 0 11 105 2 - 1214 c 2 meter tmax 201 1 0 15 105 2 - -Page 31 Source Listing ADJPDS -2020-06-22 00:51 ecmwfensh.f - - 1215 c 2 meter tmin 202 1 0 16 105 2 - 1216 c 2 meter td 168 1 0 17 105 2 - 1217 cJ.Peng---2011-05-17------------NCO change Surface Pressure---- - 1218 c psfc 152 109 1 (log) 1 1 0 - 1219 c psfc 134 1 0 new 1 1 0 - 1220 - 1221 c total cloud cover 164 1 0 (0-1) 71 200 0 (%) - 1222 c - 1223 c - 1224 c - Others, which currently (1/97) are in the ECMWF - 1225 c data sets, but are not needed for this output data - 1226 c set (they're needed by the Ocean Modeling Branch - 1227 c and are processed in a different program): - 1228 c (now processed here 1/04) - 1229 c - 1230 c u-comp (10m) 165 1 0 33 105 10 - 1231 c v-comp (10m) 166 1 0 34 105 10 - 1232 c - 1233 c ------------------------------------------------- - 1234 c - 1235 c ALSO, get the output GRIB file number, which is - 1236 c based on the parameter number and vertical level. - 1237 c - 1238 c contflag is needed because ECMWF sends a couple of - 1239 c additional records in their package which our Ocean - 1240 c Modeling Branch uses, but which we do not archive, - 1241 c so we don't want to output these to our GRIB files. - 1242 c - 1243 c **************************************************** - 1244 c - 1245 character*1 contflag - 1246 integer kpds(25),memberct(2) - 1247 c - 1248 contflag='n' - 1249 lugout=0 - 1250 - 1251 cJ.Peng---2011-05-17------------NCO change Surface Pressure---- - 1252 c 152----to ----134 - 1253 if (kpds(5).ne.130 .and. - 1254 & kpds(5).ne.131 .and. kpds(5).ne.132 .and. - 1255 & kpds(5).ne.151 .and. kpds(5).ne.134 .and. - 1256 & kpds(5).ne.156 .and. kpds(5).ne.157 .and. - 1257 & kpds(5).ne.164 .and. - 1258 & kpds(5).ne.165 .and. kpds(5).ne.166 .and. - 1259 & kpds(5).ne.167 .and. kpds(5).ne.168 .and. - 1260 & kpds(5).ne.201 .and. kpds(5).ne.202 .and. - 1261 & kpds(5).ne.228) then - 1262 goto 900 - 1263 endif - 1264 - 1265 c - 1266 if (kpds(5).eq.130) then - 1267 kpds(5) = 11 - 1268 if (kpds(7).eq.200) lugout = 51 - 1269 if (kpds(7).eq.500) lugout = 52 - 1270 if (kpds(7).eq.700) lugout = 53 - 1271 if (kpds(7).eq.850) lugout = 54 - -Page 32 Source Listing ADJPDS -2020-06-22 00:51 ecmwfensh.f - - 1272 else if (kpds(5).eq.131) then - 1273 kpds(5) = 33 - 1274 if (kpds(7).eq.200) lugout = 101 - 1275 if (kpds(7).eq.500) lugout = 103 - 1276 if (kpds(7).eq.700) lugout = 105 - 1277 if (kpds(7).eq.850) lugout = 107 - 1278 else if (kpds(5).eq.132) then - 1279 kpds(5) = 34 - 1280 if (kpds(7).eq.200) lugout = 102 - 1281 if (kpds(7).eq.500) lugout = 104 - 1282 if (kpds(7).eq.700) lugout = 106 - 1283 if (kpds(7).eq.850) lugout = 108 - 1284 else if (kpds(5).eq.151) then - 1285 lugout = 81 - 1286 kpds(5) = 2 - 1287 c kpds(6) = 100 - 1288 - 1289 cJ.Peng---2011-05-17------------NCO change Surface Pressure---- - 1290 c else if (kpds(5).eq.152) then - 1291 else if (kpds(5).eq.134) then - 1292 - 1293 kpds(5) = 1 - 1294 c if (kpds(7).eq.1) then - 1295 if (kpds(7).eq.0) then - 1296 - 1297 lugout = 82 - 1298 kpds(6) = 1 - 1299 kpds(7) = 0 - 1300 end if - 1301 else if (kpds(5).eq.156) then - 1302 kpds(5) = 7 - 1303 if (kpds(7).eq.1000) lugout = 65 - 1304 if (kpds(7).eq.850) lugout = 64 - 1305 if (kpds(7).eq.700) lugout = 63 - 1306 if (kpds(7).eq.500) lugout = 62 - 1307 if (kpds(7).eq.200) lugout = 61 - 1308 else if (kpds(5).eq.157) then - 1309 kpds(5) = 52 - 1310 if (kpds(7).eq.500) lugout = 71 - 1311 if (kpds(7).eq.700) lugout = 72 - 1312 if (kpds(7).eq.850) lugout = 73 - 1313 else if (kpds(5).eq.164) then - 1314 kpds(5) = 71 - 1315 if (kpds(7).eq.0) then - 1316 lugout = 84 - 1317 kpds(6) = 200 - 1318 kpds(7) = 0 - 1319 end if - 1320 else if (kpds(5).eq.165) then - 1321 kpds(5) = 33 - 1322 if (kpds(7).eq.0) then - 1323 lugout = 109 - 1324 kpds(6) = 105 - 1325 kpds(7) = 10 - 1326 end if - 1327 else if (kpds(5).eq.166) then - 1328 kpds(5) = 34 - -Page 33 Source Listing ADJPDS -2020-06-22 00:51 ecmwfensh.f - - 1329 if (kpds(7).eq.0) then - 1330 lugout = 110 - 1331 kpds(6) = 105 - 1332 kpds(7) = 10 - 1333 end if - 1334 else if (kpds(5).eq.167) then - 1335 kpds(5) = 11 - 1336 if (kpds(7).eq.0) then - 1337 lugout = 55 - 1338 kpds(6) = 105 - 1339 kpds(7) = 2 - 1340 end if - 1341 else if (kpds(5).eq.168) then - 1342 kpds(5) = 17 - 1343 if (kpds(7).eq.0) then - 1344 lugout = 58 - 1345 kpds(6) = 105 - 1346 kpds(7) = 2 - 1347 end if - 1348 else if (kpds(5).eq.201) then - 1349 kpds(5) = 15 - 1350 if (kpds(7).eq.0) then - 1351 lugout = 56 - 1352 kpds(6) = 105 - 1353 kpds(7) = 2 - 1354 end if - 1355 else if (kpds(5).eq.202) then - 1356 kpds(5) = 16 - 1357 if (kpds(7).eq.0) then - 1358 lugout = 57 - 1359 kpds(6) = 105 - 1360 kpds(7) = 2 - 1361 end if - 1362 - 1363 c Now make adjustments for the precip GRIB PDS parms, - 1364 c which ECMWF did not code correctly for accumulations. - 1365 - 1366 else if (kpds(5).eq.228) then - 1367 kpds(5) = 61 - 1368 lugout = 83 - 1369 kpds(13) = 1 - 1370 if (kpds(14).eq.0) then - 1371 kpds(14) = 0 - 1372 kpds(15) = 0 - 1373 else - 1374 kpds(14) = kpds(14) - 12 - 1375 kpds(15) = kpds(14) + 12 - 1376 endif - 1377 kpds(16) = 4 - 1378 kpds(22) = 1 - 1379 endif - 1380 c - 1381 kpds(19) = 2 - 1382 c - 1383 if (lugout .ne. 0) then - 1384 contflag='y' - 1385 endif - -Page 34 Source Listing ADJPDS -2020-06-22 00:51 ecmwfensh.f - - 1386 c - 1387 900 continue - 1388 return - 1389 end - - -ENTRY POINTS - - Name - - adjpds_ - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - 900 Label 1387 1262 - ADJPDS Subr 1162 - CONTFLAG Dummy 1162 CHAR 1 scalar ARG,INOUT 1248,1384 - KPDS Dummy 1162 I(4) 4 1 25 ARG,INOUT 1253,1254,1255,1256,1257,1258,1259 - ,1260,1261,1266,1267,1268,1269,127 - 0,1271,1272,1273,1274,1275,1276,12 - 77,1278,1279,1280,1281,1282,1283,1 - 284,1286,1291,1293,1295,1298,1299, - 1301,1302,1303,1304,1305,1306,1307 - ,1308,1309,1310,1311,1312,1313,131 - 4,1315,1317,1318,1320,1321,1322,13 - 24,1325,1327,1328,1329,1331,1332,1 - 334,1335,1336,1338,1339,1341,1342, - 1343,1345,1346,1348,1349,1350,1352 - ,1353,1355,1356,1357,1359,1360,136 - 6,1367,1369,1370,1371,1372,1374,13 - 75,1377,1378,1381 - LUGOUT Dummy 1162 I(4) 4 scalar ARG,INOUT 1249,1268,1269,1270,1271,1274,1275 - ,1276,1277,1280,1281,1282,1283,128 - 5,1297,1303,1304,1305,1306,1307,13 - 10,1311,1312,1316,1323,1330,1337,1 - 344,1351,1358,1368,1383 - MEMBERCT Dummy 1162 I(4) 4 1 2 ARG,INOUT - -Page 35 Source Listing ADJPDS -2020-06-22 00:51 ecmwfensh.f - - 1390 c - 1391 c----------------------------------------------------------------------c - 1392 c----------------------------------------------------------------------c - 1393 c - 1394 subroutine adjext (kens,ktype,kfnum,kres) - 1395 c$$$ SUBPROGRAM DOCUMENTATION BLOCK - 1396 C . . . . - 1397 C SUBPROGRAM: adjext - 1398 C PRGMMR: WOBUS ORG: NP20 DATE: 2001-01-16 - 1399 C - 1400 C ABSTRACT: START ABSTRACT HERE AND INDENT TO COLUMN 5 ON THE - 1401 C FOLLOWING LINES. PLEASE PROVIDE A BRIEF DESCRIPTION OF - 1402 C WHAT THE SUBPROGRAM DOES. - 1403 C - 1404 C PROGRAM HISTORY LOG: - 1405 C 97-01-17 MARCHOK original program - 1406 C 01-01-16 WOBUS added DOCBLOCK - 1407 C - 1408 C USAGE: CALL PROGRAM-NAME(INARG1, INARG2, WRKARG, OUTARG1, ... ) - 1409 C INPUT ARGUMENT LIST: - 1410 C INARG1 - GENERIC DESCRIPTION, INCLUDING CONTENT, UNITS, - 1411 C INARG2 - TYPE. EXPLAIN FUNCTION IF CONTROL VARIABLE. - 1412 C - 1413 C OUTPUT ARGUMENT LIST: (INCLUDING WORK ARRAYS) - 1414 C WRKARG - GENERIC DESCRIPTION, ETC., AS ABOVE. - 1415 C OUTARG1 - EXPLAIN COMPLETELY IF ERROR RETURN - 1416 C ERRFLAG - EVEN IF MANY LINES ARE NEEDED - 1417 C - 1418 C INPUT FILES: (DELETE IF NO INPUT FILES IN SUBPROGRAM) - 1419 C - 1420 C OUTPUT FILES: (DELETE IF NO OUTPUT FILES IN SUBPROGRAM) - 1421 C - 1422 C REMARKS: LIST CAVEATS, OTHER HELPFUL HINTS OR INFORMATION - 1423 C - 1424 C ATTRIBUTES: - 1425 C LANGUAGE: INDICATE EXTENSIONS, COMPILER OPTIONS - 1426 C MACHINE: IBM SP - 1427 C - 1428 C$$$ - 1429 c - 1430 c This subroutine takes the ECMWF ensemble PDS header - 1431 c extension parameters and creates the corresponding - 1432 c NCEP header extension. - 1433 c - 1434 c INPUT - 1435 c ----- - 1436 c ktype ECMWF flag; 10 = Control, 11 = Perturbed Fcst - 1437 c kfnum 0 = CONTROL FORECAST, 1-nn = Perturbed Fcst, - 1438 c odd number is positive pert, - 1439 C even number is for negative pert. - 1440 c kres 1 - input file contains only high res control - 1441 c 2 - input file contains low res control and - 1442 c perturbations - 1443 c (Important to know since high res control - 1444 c does NOT have a PDS extension) - 1445 c - 1446 c OUTPUT - -Page 36 Source Listing ADJEXT -2020-06-22 00:51 ecmwfensh.f - - 1447 c ------ - 1448 c kens NCEP ensemble PDS extension (Bytes 41-45) - 1449 c - 1450 c - 1451 c - 1452 integer kens(5) - 1453 c - 1454 kens=0 - 1455 kens(1)=1 - 1456 c - 1457 if (kres.eq.1) then - 1458 - 1459 c If kres=1 (this is information that is passed into this program - 1460 c via a namelist), then you know that you are reading a file that - 1461 c contains only HRC records, so give the current record an - 1462 c NCEP ensemble extension to indicate such. - 1463 - 1464 kens(2) = 1 - 1465 kens(3) = 1 - 1466 - 1467 else - 1468 - 1469 c If kres=2 and the ktype=10, then you know that you're reading - 1470 c a LRC record from one of the "USE" files. As such, give it an - 1471 c NCEP LRC designation. If ktype=11, then you're reading one of - 1472 c the perturbation records. - 1473 - 1474 if (ktype.eq.10) then - 1475 kens(2) = 1 - 1476 kens(3) = 2 - 1477 else - 1478 if (mod(kfnum,2).gt.0) then - 1479 kens(2) = 3 - 1480 else - 1481 kens(2) = 2 - 1482 end if - 1483 end if - 1484 - 1485 end if - 1486 c - 1487 c CONSECUTIVELY NUMBERED ECMWF FORECASTS MAKE UP A NEGATIVELY - 1488 c AND POSITIVELY PERTURBED PAIR. THIS NEXT BIT OF CODE - 1489 c ASSOCIATES AN ID NUMBER TO A MEMBER FROM EACH PAIR. - 1490 c - 1491 if (kres.eq.2 .and. ktype.eq.11) then - 1492 kens(3) = (kfnum + 1) / 2 - 1493 endif - 1494 c - 1495 c SET NMCEXT ARRAY MEMBERS 4 AND 5 EQUAL TO 1 AND 255. - 1496 c - 1497 400 continue - 1498 c - 1499 kens(4) = 1 - 1500 kens(5) = 255 - 1501 c - 1502 return - 1503 end - -Page 37 Source Listing ADJEXT -2020-06-22 00:51 Entry Points ecmwfensh.f - - - -ENTRY POINTS - - Name - - adjext_ - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - 400 Label 1497 - ADJEXT Subr 1394 - KENS Dummy 1394 I(4) 4 1 5 ARG,INOUT 1454,1455,1464,1465,1475,1476,1479 - ,1481,1492,1499,1500 - KFNUM Dummy 1394 I(4) 4 scalar ARG,INOUT 1478,1492 - KRES Dummy 1394 I(4) 4 scalar ARG,INOUT 1457,1491 - KTYPE Dummy 1394 I(4) 4 scalar ARG,INOUT 1474,1491 - MOD Func 1478 scalar 1478 - -Page 38 Source Listing ADJEXT -2020-06-22 00:51 ecmwfensh.f - - 1504 c - 1505 c----------------------------------------------------------------------c - 1506 c----------------------------------------------------------------------c - 1507 c - 1508 subroutine output (lugout,kf,kpds,kgds,ld,data,kens) - 1509 c$$$ SUBPROGRAM DOCUMENTATION BLOCK - 1510 C . . . . - 1511 C SUBPROGRAM: output - 1512 C PRGMMR: WOBUS ORG: NP20 DATE: 2001-01-16 - 1513 C - 1514 C ABSTRACT: START ABSTRACT HERE AND INDENT TO COLUMN 5 ON THE - 1515 C FOLLOWING LINES. PLEASE PROVIDE A BRIEF DESCRIPTION OF - 1516 C WHAT THE SUBPROGRAM DOES. - 1517 C - 1518 C PROGRAM HISTORY LOG: - 1519 C 97-01-17 MARCHOK original program - 1520 C 01-01-16 WOBUS added DOCBLOCK - 1521 C - 1522 C USAGE: CALL PROGRAM-NAME(INARG1, INARG2, WRKARG, OUTARG1, ... ) - 1523 C INPUT ARGUMENT LIST: - 1524 C INARG1 - GENERIC DESCRIPTION, INCLUDING CONTENT, UNITS, - 1525 C INARG2 - TYPE. EXPLAIN FUNCTION IF CONTROL VARIABLE. - 1526 C - 1527 C OUTPUT ARGUMENT LIST: (INCLUDING WORK ARRAYS) - 1528 C WRKARG - GENERIC DESCRIPTION, ETC., AS ABOVE. - 1529 C OUTARG1 - EXPLAIN COMPLETELY IF ERROR RETURN - 1530 C ERRFLAG - EVEN IF MANY LINES ARE NEEDED - 1531 C - 1532 C INPUT FILES: (DELETE IF NO INPUT FILES IN SUBPROGRAM) - 1533 C - 1534 C OUTPUT FILES: (DELETE IF NO OUTPUT FILES IN SUBPROGRAM) - 1535 C - 1536 C REMARKS: LIST CAVEATS, OTHER HELPFUL HINTS OR INFORMATION - 1537 C - 1538 C ATTRIBUTES: - 1539 C LANGUAGE: INDICATE EXTENSIONS, COMPILER OPTIONS - 1540 C MACHINE: IBM SP - 1541 C - 1542 C$$$ - 1543 c - 1544 integer kpds(200),kgds(200),kens(200) - 1545 logical ld(kf) - 1546 real data(kf) - 1547 c - 1548 c **************************** - 1549 c WRITE GRIB FILE - 1550 c **************************** - 1551 c - 1552 if ((kens(3).le.1).or.(kens(3).ge.25)) then - 1553 if ((kpds(14).le.24).or.(kpds(14).ge.228)) then - 1554 print *,'at beginning of output, lugout=',lugout - 1555 & ,' kpds(5)=',kpds(5) - 1556 & ,' kens(2)=',kens(2),' kens(3)=',kens(3),' kf=',kf - 1557 c print *,' ' - 1558 endif - 1559 endif - 1560 call putgbe (lugout,kf,kpds,kgds,kens,ld,data,iret) - -Page 39 Source Listing OUTPUT -2020-06-22 00:51 ecmwfensh.f - - 1561 - 1562 if (iret.eq.0) then - 1563 c print *,' ' - 1564 if ((kens(3).le.1).or.(kens(3).ge.25)) then - 1565 if ((kpds(14).le.24).or.(kpds(14).ge.228)) then - 1566 print *,'IRET = 0 after call to putgbe' - 1567 c print *,' ' - 1568 endif - 1569 endif - 1570 else - 1571 print *,' ' - 1572 print *,'!!! ERROR: IRET NE 0 AFTER CALL TO PUTGBE !!!' - 1573 & ,lugout,'=lugout ',iret,'=iret ' - 1574 print *,' ' - 1575 endif - 1576 c - 1577 return - 1578 end - - -ENTRY POINTS - - Name - - output_ - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - DATA Dummy 1508 R(8) 8 1 0 ARG,INOUT 1560 - IRET Local 1560 I(4) 4 scalar 1560,1562,1573 - KENS Dummy 1508 I(4) 4 1 200 ARG,INOUT 1552,1556,1560,1564 - KF Dummy 1508 I(4) 4 scalar ARG,INOUT 1545,1546,1556,1560 - KGDS Dummy 1508 I(4) 4 1 200 ARG,INOUT 1560 - KPDS Dummy 1508 I(4) 4 1 200 ARG,INOUT 1553,1555,1560,1565 - LD Dummy 1508 L(4) 4 1 0 ARG,INOUT 1560 - LUGOUT Dummy 1508 I(4) 4 scalar ARG,INOUT 1554,1560,1573 - OUTPUT Subr 1508 - PUTGBE Subr 1560 1560 - -Page 40 Source Listing OUTPUT -2020-06-22 00:51 ecmwfensh.f - - 1579 c - 1580 c----------------------------------------------------------------------c - 1581 c----------------------------------------------------------------------c - 1582 c - 1583 c subroutine output_stats (cparm,ctype,kf,kpds,kgds,ld,data) - 1584 subroutine output_stats (cparm,lugout,kf,kpds,kgds,ld,datam - 1585 &,datas) - 1586 c$$$ SUBPROGRAM DOCUMENTATION BLOCK - 1587 C . . . . - 1588 C SUBPROGRAM: output_stats - 1589 C PRGMMR: WOBUS ORG: NP20 DATE: 2001-01-16 - 1590 C - 1591 C ABSTRACT: START ABSTRACT HERE AND INDENT TO COLUMN 5 ON THE - 1592 C FOLLOWING LINES. PLEASE PROVIDE A BRIEF DESCRIPTION OF - 1593 C WHAT THE SUBPROGRAM DOES. - 1594 C - 1595 C PROGRAM HISTORY LOG: - 1596 C 97-01-17 MARCHOK original program - 1597 C 01-01-16 WOBUS added DOCBLOCK - 1598 C - 1599 C USAGE: CALL PROGRAM-NAME(INARG1, INARG2, WRKARG, OUTARG1, ... ) - 1600 C INPUT ARGUMENT LIST: - 1601 C INARG1 - GENERIC DESCRIPTION, INCLUDING CONTENT, UNITS, - 1602 C INARG2 - TYPE. EXPLAIN FUNCTION IF CONTROL VARIABLE. - 1603 C - 1604 C OUTPUT ARGUMENT LIST: (INCLUDING WORK ARRAYS) - 1605 C WRKARG - GENERIC DESCRIPTION, ETC., AS ABOVE. - 1606 C OUTARG1 - EXPLAIN COMPLETELY IF ERROR RETURN - 1607 C ERRFLAG - EVEN IF MANY LINES ARE NEEDED - 1608 C - 1609 C INPUT FILES: (DELETE IF NO INPUT FILES IN SUBPROGRAM) - 1610 C - 1611 C OUTPUT FILES: (DELETE IF NO OUTPUT FILES IN SUBPROGRAM) - 1612 C - 1613 C REMARKS: LIST CAVEATS, OTHER HELPFUL HINTS OR INFORMATION - 1614 C - 1615 C ATTRIBUTES: - 1616 C LANGUAGE: INDICATE EXTENSIONS, COMPILER OPTIONS - 1617 C MACHINE: IBM SP - 1618 C - 1619 C$$$ - 1620 c - 1621 integer kpds(200),kgds(200),kens(200) - 1622 logical ld(kf) - 1623 real datam(kf) - 1624 real datas(kf) - 1625 character cparm*4,ctype*4 - 1626 - 1627 call grib_open_wa (lugout,ireto) - 1628 if (ireto.gt.0) then - 1629 print *,'ireto,lu from grib_open_wa in output_stats = ', - 1630 , ireto,lugout - 1631 endif - 1632 - 1633 do istat=1,2 - 1634 - 1635 if (istat .eq. 1) ctype='mean' - -Page 41 Source Listing OUTPUT_STATS -2020-06-22 00:51 ecmwfensh.f - - 1636 if (istat .eq. 2) ctype='spr ' - 1637 - 1638 ld = .TRUE. - 1639 - 1640 iready=1 - 1641 if (cparm .eq. 't200') then - 1642 kpds(5) = 11 - 1643 kpds(6) = 100 - 1644 kpds(7) = 200 - 1645 kpds(22) = 1 - 1646 else if (cparm .eq. 't500') then - 1647 kpds(5) = 11 - 1648 kpds(6) = 100 - 1649 kpds(7) = 500 - 1650 kpds(22) = 1 - 1651 else if (cparm .eq. 't700') then - 1652 kpds(5) = 11 - 1653 kpds(6) = 100 - 1654 kpds(7) = 500 - 1655 kpds(22) = 1 - 1656 else if (cparm .eq. 't850') then - 1657 kpds(5) = 11 - 1658 kpds(6) = 100 - 1659 kpds(7) = 850 - 1660 kpds(22) = 1 - 1661 else if (cparm .eq. 't2m') then - 1662 kpds(5) = 11 - 1663 kpds(6) = 105 - 1664 kpds(7) = 2 - 1665 kpds(22) = 1 - 1666 else if (cparm .eq. 't2mx') then - 1667 kpds(5) = 15 - 1668 kpds(6) = 105 - 1669 kpds(7) = 2 - 1670 kpds(22) = 1 - 1671 else if (cparm .eq. 't2mn') then - 1672 kpds(5) = 16 - 1673 kpds(6) = 105 - 1674 kpds(7) = 2 - 1675 kpds(22) = 1 - 1676 else if (cparm .eq. 'td2m') then - 1677 kpds(5) = 17 - 1678 kpds(6) = 105 - 1679 kpds(7) = 2 - 1680 kpds(22) = 1 - 1681 else if (cparm .eq. 'z200') then - 1682 kpds(5) = 7 - 1683 kpds(6) = 100 - 1684 kpds(7) = 200 - 1685 if (ctype .eq. 'mean') then - 1686 kpds(22) = 0 - 1687 else if (ctype .eq. 'spr ') then - 1688 kpds(22) = 1 - 1689 endif - 1690 else if (cparm .eq. 'z500') then - 1691 kpds(5) = 7 - 1692 kpds(6) = 100 - -Page 42 Source Listing OUTPUT_STATS -2020-06-22 00:51 ecmwfensh.f - - 1693 kpds(7) = 500 - 1694 if (ctype .eq. 'mean') then - 1695 kpds(22) = 0 - 1696 else if (ctype .eq. 'spr ') then - 1697 kpds(22) = 1 - 1698 endif - 1699 else if (cparm .eq. 'z700') then - 1700 kpds(5) = 7 - 1701 kpds(6) = 100 - 1702 kpds(7) = 700 - 1703 if (ctype .eq. 'mean') then - 1704 kpds(22) = 0 - 1705 else if (ctype .eq. 'spr ') then - 1706 kpds(22) = 1 - 1707 endif - 1708 else if (cparm .eq. 'z850') then - 1709 kpds(5) = 7 - 1710 kpds(6) = 850 - 1711 kpds(7) = 700 - 1712 if (ctype .eq. 'mean') then - 1713 kpds(22) = 0 - 1714 else if (ctype .eq. 'spr ') then - 1715 kpds(22) = 1 - 1716 endif - 1717 c new batch 01/04 - 1718 else if (cparm .eq. 'z1k ') then - 1719 kpds(5) = 7 - 1720 kpds(6) = 100 - 1721 kpds(7) = 1000 - 1722 if (ctype .eq. 'mean') then - 1723 kpds(22) = 0 - 1724 else if (ctype .eq. 'spr ') then - 1725 kpds(22) = 1 - 1726 endif - 1727 else if (cparm .eq. 'r500') then - 1728 kpds(5) = 52 - 1729 kpds(6) = 100 - 1730 kpds(7) = 500 - 1731 kpds(22) = 1 - 1732 else if (cparm .eq. 'r700') then - 1733 kpds(5) = 52 - 1734 kpds(6) = 100 - 1735 kpds(7) = 700 - 1736 kpds(22) = 1 - 1737 else if (cparm .eq. 'r850') then - 1738 kpds(5) = 52 - 1739 kpds(6) = 100 - 1740 kpds(7) = 850 - 1741 kpds(22) = 1 - 1742 else if (cparm .eq. 'mslp') then - 1743 kpds(5) = 2 - 1744 kpds(6) = 102 - 1745 kpds(7) = 0 - 1746 if (ctype .eq. 'mean') then - 1747 kpds(22) = 0 - 1748 else if (ctype .eq. 'spr ') then - 1749 kpds(22) = 1 - -Page 43 Source Listing OUTPUT_STATS -2020-06-22 00:51 ecmwfensh.f - - 1750 endif - 1751 else if (cparm .eq. 'psfc') then - 1752 kpds(5) = 1 - 1753 kpds(6) = 102 - 1754 kpds(7) = 0 - 1755 if (ctype .eq. 'mean') then - 1756 kpds(22) = 0 - 1757 else if (ctype .eq. 'spr ') then - 1758 kpds(22) = 1 - 1759 endif - 1760 else if (cparm .eq. 'prcp') then - 1761 kpds(5) = 61 - 1762 kpds(6) = 1 - 1763 kpds(7) = 0 - 1764 - 1765 C THIS NEXT STUFF IS ALREADY DONE IN ADJPDS, - 1766 C SO COMMENT IT OUT HERE.... - 1767 C if (kpds(14).eq.0) then - 1768 C kpds(14) = 0 - 1769 C kpds(15) = 0 - 1770 C else - 1771 C kpds(14) = kpds(14) - 12 - 1772 C kpds(15) = kpds(14) + 12 - 1773 C endif - 1774 - 1775 kpds(16) = 4 - 1776 kpds(22) = 1 - 1777 else if (cparm .eq. 'tcdc') then - 1778 kpds(5) = 61 - 1779 kpds(6) = 200 - 1780 kpds(7) = 0 - 1781 kpds(22) = 1 - 1782 else - 1783 iready=no - 1784 endif - 1785 kens(1)=1 - 1786 kens(2)=5 - 1787 kens(3)=0 - 1788 kens(4)=0 - 1789 kens(5)=255 - 1790 if (ctype .eq. 'mean') then - 1791 kens(4)=1 - 1792 else if (ctype .eq. 'spr ') then - 1793 kens(4)=11 - 1794 endif - 1795 - 1796 c **************************** - 1797 c WRITE GRIB FILE - 1798 c **************************** - 1799 - 1800 print *,'In output_stats, lugout= ',lugout,' kf= ',kf - 1801 print *,'In output_stats, cparm= ',cparm,' ctype= ',ctype - 1802 c print *,' ' - 1803 - 1804 c if ( kpds(14) .eq. 72 ) then - 1805 c print *,' ' - 1806 write(*,71) (kpds(mm),mm=1,5) - -Page 44 Source Listing OUTPUT_STATS -2020-06-22 00:51 ecmwfensh.f - - 1807 write(*,72) (kpds(mm),mm=6,10) - 1808 write(*,73) (kpds(mm),mm=11,15) - 1809 write(*,74) (kpds(mm),mm=16,20) - 1810 write(*,75) (kpds(mm),mm=21,25) - 1811 write(*,76) (kgds(mm),mm=1,5) - 1812 write(*,77) (kgds(mm),mm=6,10) - 1813 write(*,78) (kgds(mm),mm=11,15) - 1814 write(*,79) (kgds(mm),mm=16,20) - 1815 write(*,80) (kgds(mm),mm=21,22) - 1816 if ( ctype .eq. 'mean' ) then - 1817 write(*,81) datam(1),datam(kf/4),datam(kf/2) - 1818 & ,datam(3*kf/4),datam(kf) - 1819 call srange(kf,ld,datam) - 1820 endif - 1821 if ( ctype .eq. 'spr ' ) then - 1822 write(*,81) datas(1),datas(kf/4),datas(kf/2) - 1823 & ,datas(3*kf/4),datas(kf) - 1824 call srange(kf,ld,datas) - 1825 endif - 1826 c print *,' ' - 1827 c endif - 1828 - 1829 71 format('p1= ',i7,' p2= ',i7,' p3= ',i7,' p4= ',i7,' p5= ' - 1830 & ,i7) - 1831 72 format('p6= ',i7,' p7= ',i7,' p8= ',i7,' p9= ',i7,' p10= ' - 1832 & ,i7) - 1833 73 format('p11= ',i7,' p12= ',i7,' p13= ',i7,' p14= ',i7,' p15= ' - 1834 & ,i7) - 1835 74 format('p16= ',i7,' p17= ',i7,' p18= ',i7,' p19= ',i7,' p20= ' - 1836 & ,i7) - 1837 75 format('p21= ',i7,' p22= ',i7,' p23= ',i7,' p24= ',i7,' p25= ' - 1838 & ,i7) - 1839 76 format('g1= ',i7,' g2= ',i7,' g3= ',i7,' g4= ',i7,' g5= ' - 1840 & ,i7) - 1841 77 format('g6= ',i7,' g7= ',i7,' g8= ',i7,' g9= ',i7,' g10= ' - 1842 & ,i7) - 1843 78 format('g11= ',i7,' g12= ',i7,' g13= ',i7,' g14= ',i7,' g15= ' - 1844 & ,i7) - 1845 79 format('g16= ',i7,' g17= ',i7,' g18= ',i7,' g19= ',i7,' g20= ' - 1846 & ,i7) - 1847 80 format('g21= ',i7,' g22= ',i7) - 1848 81 format('f(1)= ',g12.4,' f(kf/4)= ',g12.4,' f(kf/2)= ',g12.4 - 1849 & ,' f(3*kf/4)= ',g12.4,' f(kf)= ',g12.4) - 1850 - 1851 iret=99 - 1852 - 1853 if (iready .eq. 1) then - 1854 - 1855 if ( ctype .eq. 'mean' ) then - 1856 call putgbe (lugout,kf,kpds,kgds,kens,ld,datam,iret) - 1857 endif - 1858 if ( ctype .eq. 'spr ' ) then - 1859 call putgbe (lugout,kf,kpds,kgds,kens,ld,datas,iret) - 1860 endif - 1861 if (iret.eq.0) then - 1862 c c print *,' ' - 1863 print *,'IRET = 0 after call to putgbe in sub output_stats' - -Page 45 Source Listing OUTPUT_STATS -2020-06-22 00:51 ecmwfensh.f - - 1864 c c print *,' ' - 1865 else - 1866 print *,' ' - 1867 print *,'!!! ERROR: IRET NE 0 AFTER ' - 1868 & ,'PUTGBE IN OUTPUT_STATS!!!' - 1869 & ,cparm,'= cparm',ctype,'=ctype ' - 1870 & ,lugout,'=lugout ',iret,'=iret ' - 1871 print *,' ' - 1872 endif - 1873 else - 1874 print *,'This variable not ready' - 1875 & ,cparm,'= cparm',ctype,'=ctype ' - 1876 & ,lugout,'=lugout ',iret,'=iret ' - 1877 endif - 1878 - 1879 enddo - 1880 - 1881 call grib_close (lugout,ireto) - 1882 if (ireto.gt.0) then - 1883 print *,'ireto,lu from grib_close in output_stats = ', - 1884 , ireto,lugout - 1885 endif - 1886 - 1887 c - 1888 return - 1889 end - - -ENTRY POINTS - - Name - - output_stats_ - -Page 46 Source Listing OUTPUT_STATS -2020-06-22 00:51 Symbol Table ecmwfensh.f - - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - 71 Label 1829 1806 - 72 Label 1831 1807 - 73 Label 1833 1808 - 74 Label 1835 1809 - 75 Label 1837 1810 - 76 Label 1839 1811 - 77 Label 1841 1812 - 78 Label 1843 1813 - 79 Label 1845 1814 - 80 Label 1847 1815 - 81 Label 1848 1817,1822 - CPARM Dummy 1584 CHAR 4 scalar ARG,INOUT 1641,1646,1651,1656,1661,1666,1671 - ,1676,1681,1690,1699,1708,1718,172 - 7,1732,1737,1742,1751,1760,1777,18 - 01,1869,1875 - CTYPE Local 1625 CHAR 4 scalar 1635,1636,1685,1687,1694,1696,1703 - ,1705,1712,1714,1722,1724,1746,174 - 8,1755,1757,1790,1792,1801,1816,18 - 21,1855,1858,1869,1875 - DATAM Dummy 1584 R(8) 8 1 0 ARG,INOUT 1817,1818,1819,1856 - DATAS Dummy 1585 R(8) 8 1 0 ARG,INOUT 1822,1823,1824,1859 - GRIB_CLOSE Subr 1881 1881 - GRIB_OPEN_WA Subr 1627 1627 - IREADY Local 1640 I(4) 4 scalar 1640,1783,1853 - IRET Local 1851 I(4) 4 scalar 1851,1856,1859,1861,1870,1876 - IRETO Local 1627 I(4) 4 scalar 1627,1628,1630,1881,1882,1884 - ISTAT Local 1633 I(4) 4 scalar 1633,1635,1636 - KENS Local 1621 I(4) 4 1 200 1785,1786,1787,1788,1789,1791,1793 - ,1856,1859 - KF Dummy 1584 I(4) 4 scalar ARG,INOUT 1622,1623,1624,1800,1817,1818,1819 - ,1822,1823,1824,1856,1859 - KGDS Dummy 1584 I(4) 4 1 200 ARG,INOUT 1811,1812,1813,1814,1815,1856,1859 - KPDS Dummy 1584 I(4) 4 1 200 ARG,INOUT 1642,1643,1644,1645,1647,1648,1649 - ,1650,1652,1653,1654,1655,1657,165 - 8,1659,1660,1662,1663,1664,1665,16 - 67,1668,1669,1670,1672,1673,1674,1 - 675,1677,1678,1679,1680,1682,1683, - 1684,1686,1688,1691,1692,1693,1695 - ,1697,1700,1701,1702,1704,1706,170 - 9,1710,1711,1713,1715,1719,1720,17 - 21,1723,1725,1728,1729,1730,1731,1 - 733,1734,1735,1736,1738,1739,1740, - 1741,1743,1744,1745,1747,1749,1752 - ,1753,1754,1756,1758,1761,1762,176 - 3,1775,1776,1778,1779,1780,1781,18 - 06,1807,1808,1809,1810,1856,1859 - LD Dummy 1584 L(4) 4 1 0 ARG,INOUT 1638,1819,1824,1856,1859 - LUGOUT Dummy 1584 I(4) 4 scalar ARG,INOUT 1627,1630,1800,1856,1859,1870,1876 - ,1881,1884 - MM Local 1806 I(4) 4 scalar 1806,1807,1808,1809,1810,1811,1812 - ,1813,1814,1815 - -Page 47 Source Listing OUTPUT_STATS -2020-06-22 00:51 Symbol Table ecmwfensh.f - - Name Object Declared Type Bytes Dimen Elements Attributes References - - NO Local 1783 I(4) 4 scalar 1783 - OUTPUT_STATS Subr 1584 - PUTGBE Subr 1856 1856,1859 - SRANGE Subr 1819 1819,1824 - -Page 48 Source Listing OUTPUT_STATS -2020-06-22 00:51 ecmwfensh.f - - 1890 c - 1891 c----------------------------------------------------------------------c - 1892 c----------------------------------------------------------------------c - 1893 c - 1894 subroutine srange(nlat,ld,var) - 1895 dimension var(nlat) - 1896 logical ld(nlat) - 1897 c produces range, mean, avg dev, std dev, skew - 1898 ptsn=nlat - 1899 sa=0.0 - 1900 dmin=1.e40 - 1901 dmax=-1.e40 - 1902 do j=1,nlat - 1903 if (ld(j)) then - 1904 sa=sa+var(j) - 1905 dmin=min(dmin,var(j)) - 1906 dmax=max(dmax,var(j)) - 1907 endif - 1908 enddo - 1909 avg=sa/ptsn - 1910 sl=0.0 - 1911 sv=0.0 - 1912 do j=1,nlat - 1913 if (ld(j)) then - 1914 sl=sl+abs(var(j)-avg) - 1915 sv=sv+(var(j)-avg)**2 - 1916 endif - 1917 enddo - 1918 adev=sl/ptsn - 1919 sdev=sqrt(sv/(ptsn-1)) - 1920 if (sdev.gt.0.0) then - 1921 ss=0.0 - 1922 do j=1,nlat - 1923 if (ld(j)) then - 1924 devn=(var(j)-avg)/sdev - 1925 ss=ss+devn**3 - 1926 endif - 1927 enddo - 1928 skew=ss/ptsn - 1929 else - 1930 skew=0.0 - 1931 endif - 1932 c scale for cleaner output - 1933 outmin=1.0e10 - 1934 outmax=1.0e-10 - 1935 if ( dmin.ne.0.0 ) then - 1936 if (outmin.gt.abs(dmin)) then - 1937 outmin = abs(dmin) - 1938 endif - 1939 if (outmax.lt.abs(dmin)) then - 1940 outmax = abs(dmin) - 1941 endif - 1942 else - 1943 if (outmin.gt.1.0) then - 1944 outmin = 1.0 - 1945 endif - 1946 if (outmax.lt.1.0) then - -Page 49 Source Listing SRANGE -2020-06-22 00:51 ecmwfensh.f - - 1947 outmax = 1.0 - 1948 endif - 1949 endif - 1950 if ( dmax.ne.0.0 ) then - 1951 if (outmin.gt.abs(dmax)) then - 1952 outmin = abs(dmax) - 1953 endif - 1954 if (outmax.lt.abs(dmax)) then - 1955 outmax = abs(dmax) - 1956 endif - 1957 else - 1958 if (outmin.gt.1.0) then - 1959 outmin = 1.0 - 1960 endif - 1961 if (outmax.lt.1.0) then - 1962 outmax = 1.0 - 1963 endif - 1964 endif - 1965 if ( avg.ne.0.0 ) then - 1966 if (outmin.gt.abs(avg)) then - 1967 outmin = abs(avg) - 1968 endif - 1969 if (outmax.lt.abs(avg)) then - 1970 outmax = abs(avg) - 1971 endif - 1972 else - 1973 if (outmin.gt.1.0) then - 1974 outmin = 1.0 - 1975 endif - 1976 if (outmax.lt.1.0) then - 1977 outmax = 1.0 - 1978 endif - 1979 endif - 1980 if ( adev.ne.0.0 ) then - 1981 if (outmin.gt.abs(adev)) then - 1982 outmin = abs(adev) - 1983 endif - 1984 if (outmax.lt.abs(adev)) then - 1985 outmax = abs(adev) - 1986 endif - 1987 else - 1988 if (outmin.gt.1.0) then - 1989 outmin = 1.0 - 1990 endif - 1991 if (outmax.lt.1.0) then - 1992 outmax = 1.0 - 1993 endif - 1994 endif - 1995 if ( sdev.ne.0.0 ) then - 1996 if (outmin.gt.abs(sdev)) then - 1997 outmin = abs(sdev) - 1998 endif - 1999 if (outmax.lt.abs(sdev)) then - 2000 outmax = abs(sdev) - 2001 endif - 2002 else - 2003 if (outmin.gt.1.0) then - -Page 50 Source Listing SRANGE -2020-06-22 00:51 ecmwfensh.f - - 2004 outmin = 1.0 - 2005 endif - 2006 if (outmax.lt.1.0) then - 2007 outmax = 1.0 - 2008 endif - 2009 endif - 2010 if ( skew.ne.0.0 ) then - 2011 if (outmin.gt.abs(skew)) then - 2012 outmin = abs(skew) - 2013 endif - 2014 if (outmax.lt.abs(skew)) then - 2015 outmax = abs(skew) - 2016 endif - 2017 else - 2018 if (outmin.gt.1.0) then - 2019 outmin = 1.0 - 2020 endif - 2021 if (outmax.lt.1.0) then - 2022 outmax = 1.0 - 2023 endif - 2024 endif - 2025 if ( (outmax.lt.9.9e4) .and. (outmin.gt.1.0e-5) ) then - 2026 write(6,'('' '',6f19.13)') dmin,dmax,avg,adev,sdev,skew - 2027 else - 2028 write(6,'('' '',1p,6e19.10)') dmin,dmax,avg,adev,sdev,skew - 2029 endif - 2030 c print *,dmin,dmax,avg,adev,sdev,skew - 2031 return - 2032 end - -Page 51 Source Listing SRANGE -2020-06-22 00:51 Entry Points ecmwfensh.f - - - -ENTRY POINTS - - Name - - srange_ - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - ABS Func 1914 scalar 1914,1936,1937,1939,1940,1951,1952 - ,1954,1955,1966,1967,1969,1970,198 - 1,1982,1984,1985,1996,1997,1999,20 - 00,2011,2012,2014,2015 - ADEV Local 1918 R(8) 8 scalar 1918,1980,1981,1982,1984,1985,2026 - ,2028 - AVG Local 1909 R(8) 8 scalar 1909,1914,1915,1924,1965,1966,1967 - ,1969,1970,2026,2028 - DEVN Local 1924 R(8) 8 scalar 1924,1925 - DMAX Local 1901 R(8) 8 scalar 1901,1906,1950,1951,1952,1954,1955 - ,2026,2028 - DMIN Local 1900 R(8) 8 scalar 1900,1905,1935,1936,1937,1939,1940 - ,2026,2028 - J Local 1902 I(4) 4 scalar 1902,1903,1904,1905,1906,1912,1913 - ,1914,1915,1922,1923,1924 - LD Dummy 1894 L(4) 4 1 0 ARG,INOUT 1903,1913,1923 - MAX Func 1906 scalar 1906 - MIN Func 1905 scalar 1905 - NLAT Dummy 1894 I(4) 4 scalar ARG,INOUT 1895,1896,1898,1902,1912,1922 - OUTMAX Local 1934 R(8) 8 scalar 1934,1939,1940,1946,1947,1954,1955 - ,1961,1962,1969,1970,1976,1977,198 - 4,1985,1991,1992,1999,2000,2006,20 - 07,2014,2015,2021,2022,2025 - OUTMIN Local 1933 R(8) 8 scalar 1933,1936,1937,1943,1944,1951,1952 - ,1958,1959,1966,1967,1973,1974,198 - 1,1982,1988,1989,1996,1997,2003,20 - 04,2011,2012,2018,2019,2025 - PTSN Local 1898 R(8) 8 scalar 1898,1909,1918,1919,1928 - SA Local 1899 R(8) 8 scalar 1899,1904,1909 - SDEV Local 1919 R(8) 8 scalar 1919,1920,1924,1995,1996,1997,1999 - ,2000,2026,2028 - SKEW Local 1928 R(8) 8 scalar 1928,1930,2010,2011,2012,2014,2015 - ,2026,2028 - SL Local 1910 R(8) 8 scalar 1910,1914,1918 - SQRT Func 1919 scalar 1919 - SRANGE Subr 1894 - SS Local 1921 R(8) 8 scalar 1921,1925,1928 - SV Local 1911 R(8) 8 scalar 1911,1915,1919 - VAR Dummy 1894 R(8) 8 1 0 ARG,INOUT 1904,1905,1906,1914,1915,1924 - -Page 52 Source Listing SRANGE -2020-06-22 00:51 ecmwfensh.f - - 2033 c - 2034 c----------------------------------------------------------------------c - 2035 c----------------------------------------------------------------------c - 2036 c - 2037 subroutine grange(n,ld,d,dmin,dmax) - 2038 c$$$ SUBPROGRAM DOCUMENTATION BLOCK - 2039 C . . . . - 2040 C SUBPROGRAM: GRANGE - 2041 C PRGMMR: WOBUS ORG: NP20 DATE: 2001-01-16 - 2042 C - 2043 C ABSTRACT: Calculate the maximum and minimum values in an array - 2044 C - 2045 C PROGRAM HISTORY LOG: - 2046 C 97-01-17 MARCHOK original program - 2047 C 01-01-16 WOBUS added DOCBLOCK - 2048 C - 2049 C USAGE: CALL PROGRAM-NAME(INARG1, INARG2, WRKARG, OUTARG1, ... ) - 2050 C INPUT ARGUMENT LIST: - 2051 c n - dimension of the array - 2052 c ld - logical array (bit map) - 2053 c d - array - 2054 C - 2055 C OUTPUT ARGUMENT LIST: (INCLUDING WORK ARRAYS) - 2056 c dmin - minimum value in array d - 2057 c dmin - maximum value in array d - 2058 C - 2059 C ATTRIBUTES: - 2060 C MACHINE: IBM SP - 2061 C - 2062 C$$$ - 2063 logical ld - 2064 dimension ld(n),d(n) - 2065 c - 2066 dmin=1.e40 - 2067 dmax=-1.e40 - 2068 c - 2069 do i=1,n - 2070 if(ld(i)) then - 2071 dmin=min(dmin,d(i)) - 2072 dmax=max(dmax,d(i)) - 2073 endif - 2074 enddo - 2075 c - 2076 return - 2077 end - -Page 53 Source Listing GRANGE -2020-06-22 00:51 Entry Points ecmwfensh.f - - - -ENTRY POINTS - - Name - - grange_ - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - D Dummy 2037 R(8) 8 1 0 ARG,INOUT 2071,2072 - DMAX Dummy 2037 R(8) 8 scalar ARG,INOUT 2067,2072 - DMIN Dummy 2037 R(8) 8 scalar ARG,INOUT 2066,2071 - GRANGE Subr 2037 - I Local 2069 I(4) 4 scalar 2069,2070,2071,2072 - LD Dummy 2037 L(4) 4 1 0 ARG,INOUT 2070 - MAX Func 2072 scalar 2072 - MIN Func 2071 scalar 2071 - N Dummy 2037 I(4) 4 scalar ARG,INOUT 2064,2069 - -Page 54 Source Listing GRANGE -2020-06-22 00:51 ecmwfensh.f - - 2078 c - 2079 c----------------------------------------------------------------------c - 2080 c----------------------------------------------------------------------c - 2081 c - 2082 SUBROUTINE GETGBECE(LUGB,LUGI,JF,J,JPDS,JGDS,JENS, - 2083 & KF,K,KPDS,KGDS,KENS,LB,F,IRET, - 2084 & ktype,kfnum,ktot) - 2085 C$$$ SUBPROGRAM DOCUMENTATION BLOCK - 2086 C - 2087 C SUBPROGRAM: GETGBECE FINDS AND UNPACKS A GRIB MESSAGE - 2088 C PRGMMR: WOBUS ORG: NP20 DATE: 2001-01-16 - 2089 C - 2090 C ABSTRACT: FIND AND UNPACK A GRIB MESSAGE, ECMWF ENSEMBLE VERSION. - 2091 C READ AN ASSOCIATED GRIB INDEX FILE (UNLESS IT ALREADY WAS READ). - 2092 C FIND IN THE INDEX FILE A REFERENCE TO THE GRIB MESSAGE REQUESTED. - 2093 C THE GRIB MESSAGE REQUEST SPECIFIES THE NUMBER OF MESSAGES TO SKIP - 2094 C AND THE UNPACKED PDS AND GDS PARAMETERS. (A REQUESTED PARAMETER - 2095 C OF -1 MEANS TO ALLOW ANY VALUE OF THIS PARAMETER TO BE FOUND.) - 2096 C IF THE REQUESTED GRIB MESSAGE IS FOUND, THEN IT IS READ FROM THE - 2097 C GRIB FILE AND UNPACKED. ITS MESSAGE NUMBER IS RETURNED ALONG WITH - 2098 C THE UNPACKED PDS AND GDS PARAMETERS, THE UNPACKED BITMAP (IF ANY), - 2099 C AND THE UNPACKED DATA. IF THE GRIB MESSAGE IS NOT FOUND, THEN THE - 2100 C RETURN CODE WILL BE NONZERO. - 2101 C - 2102 C PROGRAM HISTORY LOG: - 2103 C 94-04-01 IREDELL - 2104 C 97-01-17 MARCHOK - modified version for ECMWF ensemble GRIB ext. - 2105 C 01-01-16 WOBUS - renamed and updated DOCBLOCK - 2106 C - 2107 C USAGE: CALL GETGBECE(LUGB,LUGI,JF,J,JPDS,JGDS,JENS, - 2108 C & KF,K,KPDS,KGDS,KENS,LB,F,IRET) - 2109 C INPUT ARGUMENTS: - 2110 C LUGB LOGICAL UNIT OF THE UNBLOCKED GRIB DATA FILE - 2111 C LUGI LOGICAL UNIT OF THE UNBLOCKED GRIB INDEX FILE - 2112 C JF INTEGER MAXIMUM NUMBER OF DATA POINTS TO UNPACK - 2113 C J INTEGER NUMBER OF MESSAGES TO SKIP - 2114 C (=0 TO SEARCH FROM BEGINNING) - 2115 C (<0 TO REOPEN INDEX FILE AND SEARCH FROM BEGINNING) - 2116 C JPDS INTEGER (25) PDS PARAMETERS FOR WHICH TO SEARCH - 2117 C (=-1 FOR WILDCARD) - 2118 C JGDS INTEGER (22) GDS PARAMETERS FOR WHICH TO SEARCH - 2119 C (ONLY SEARCHED IF JPDS(3)=255) - 2120 C (=-1 FOR WILDCARD) - 2121 C JENS INTEGER (5) ENSEMBLE PDS PARMS FOR WHICH TO SEARCH - 2122 C (ONLY SEARCHED IF JPDS(23)=3) - 2123 C (=-1 FOR WILDCARD) - 2124 C OUTPUT ARGUMENTS: - 2125 C KF INTEGER NUMBER OF DATA POINTS UNPACKED - 2126 C K INTEGER MESSAGE NUMBER UNPACKED - 2127 C (CAN BE SAME AS J IN CALLING PROGRAM - 2128 C IN ORDER TO FACILITATE MULTIPLE SEARCHES) - 2129 C KPDS INTEGER (25) UNPACKED PDS PARAMETERS - 2130 C KGDS INTEGER (22) UNPACKED GDS PARAMETERS - 2131 c - 2132 c - 2133 C KENS INTEGER (5) UNPACKED ENSEMBLE PDS PARMS - 2134 c - -Page 55 Source Listing GETGBECE -2020-06-22 00:51 ecmwfensh.f - - 2135 c *********** CODE ADDED FOR ECMWF ORIGINAL ENSEMBLE FILES **** - 2136 c - 2137 c ktype 10 = ECMWF control forecast - 2138 c 11 = ECMWF perturbed forecast - 2139 c kfnum Ensemble Forecast Number; - 2140 c Control Forecast is number 0, - 2141 c perturbed forecast are 1-nn, where - 2142 c positive perturbation is an odd number, - 2143 c negative perturbation is an even number. - 2144 c ktot Total number of forecast in ensemble. - 2145 c This number includes the control forecast. - 2146 c - 2147 C LB LOGICAL (KF) UNPACKED BITMAP IF PRESENT - 2148 C F REAL (KF) UNPACKED DATA - 2149 C IRET INTEGER RETURN CODE - 2150 C 0 ALL OK - 2151 C 96 ERROR READING INDEX FILE - 2152 C 97 ERROR READING GRIB FILE - 2153 C 98 NUMBER OF DATA POINTS GREATER THAN JF - 2154 C 99 REQUEST NOT FOUND - 2155 C OTHER W3FI63 GRIB UNPACKER RETURN CODE - 2156 C - 2157 C SUBPROGRAMS CALLED: - 2158 C BAopenr open for BYTE-ADDRESSABLE READ, read-only - 2159 C BAopen open for BYTE-ADDRESSABLE READ - 2160 C BAclose close for BYTE-ADDRESSABLE READ - 2161 C BAREAD BYTE-ADDRESSABLE READ - 2162 C GBYTEC UNPACK BYTES - 2163 C FI632 UNPACK PDS - 2164 C FI633 UNPACK GDS - 2165 C PDSEUP UNPACK PDS EXTENSION - 2166 C W3FI63 UNPACK GRIB - 2167 C - 2168 C REMARKS: LIST CAVEATS, OTHER HELPFUL HINTS OR INFORMATION - 2169 c IMPORTANT NOTE: THIS GETGBENS SUBROUTINE HAS BEEN MODIFIED!!! - 2170 C IT IS *NOT* THE SAME GETGBENS AS IS FOUND IN - 2171 C /NWPROD/W3LIB. MODIFICATIONS WERE MADE TO IT - 2172 C TO BE ABLE TO READ THE ECMWF PDS EXTENSION - 2173 c Modified getgbens has been renamed getgbece - 2174 c - 2175 C ATTRIBUTES: - 2176 C LANGUAGE: CRAY FORTRAN - 2177 C LANGUAGE: ibm FORTRAN - 2178 C - 2179 C$$$ - 2180 INTEGER JPDS(25),JGDS(22),KPDS(25),KGDS(22) - 2181 PARAMETER(LPDS=23,LGDS=22) - 2182 INTEGER JENS(5),KENS(5) - 2183 LOGICAL LB(JF) - 2184 REAL F(JF) - 2185 PARAMETER(MBUF=8192*128) - 2186 CHARACTER CBUF(MBUF) - 2187 SAVE LUX,NSKP,NLEN,NNUM,CBUF - 2188 DATA LUX/0/ - 2189 CHARACTER CHEAD(2)*81 - 2190 CHARACTER CPDS(80)*1,CGDS(42)*1 - 2191 C INTEGER KPTR(16) - -Page 56 Source Listing GETGBECE -2020-06-22 00:51 ecmwfensh.f - - 2192 INTEGER KPTR(20) - 2193 INTEGER IPDSP(LPDS),JPDSP(LPDS),IGDSP(LGDS),JGDSP(LGDS) - 2194 INTEGER IENSP(5),JENSP(5) - 2195 CHARACTER GRIB(200+17*JF/8)*1 - 2196 C - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2197 C READ INDEX FILE - 2198 IF(J.LT.0.OR.LUGI.NE.LUX) THEN - 2199 call grib_open_r (lugb,ireto) - 2200 if (ireto.gt.0) then - 2201 print *,'ireto,lu from grib_open_r in getgbece = ',ireto,lugb - 2202 endif - 2203 call grib_open_r (lugi,ireto) - 2204 if (ireto.gt.0) then - 2205 print *,'ireto,lu from grib_open_r in getgbece = ',ireto,lugi - 2206 endif - 2207 print *,' in getgbece: units b,i: ',lugb,lugi - 2208 c REWIND LUGI - 2209 c READ(LUGI,IOSTAT=IOS) CHEAD - 2210 c CALL BAREAD(LUGB,LSKIP,LGRIB,LREAD,GRIB) - 2211 ios=-1 - 2212 nskp=0 - 2213 lgrib=81 - 2214 CALL BAREAD(LUGI,NSKP,LGRIB,LREAD,CHEAD(1)) - 2215 if ( lgrib.eq.lread ) ios=0 - 2216 c print *,'ios after chead read = ',ios - 2217 print *,'chead 42-47 = ',CHEAD(1)(42:47) - 2218 print *,'chead 38-43 = ',CHEAD(1)(38:43) - 2219 print *,' nskp, lgrib, lread = ',nskp,lgrib,lread - 2220 nskp=lread - 2221 CALL BAREAD(LUGI,NSKP,LGRIB,LREAD,CHEAD(2)) - 2222 print *,' nskp, lgrib, lread = ',nskp,lgrib,lread - 2223 IF(IOS.EQ.0.AND.CHEAD(1)(42:47).EQ.'GB1IX1') THEN - 2224 c IF(IOS.EQ.0.AND.CHEAD(1)(38:43).EQ.'GB1IX1') THEN - 2225 LUX=0 - 2226 READ(CHEAD(2),'(8X,3I10,2X,A40)',IOSTAT=IOS) NSKP,NLEN,NNUM - 2227 print *,'nlen= ',nlen,' ios= ',ios,' nskp= ',nskp,' nnum= ' - 2228 & ,nnum - 2229 IF(IOS.EQ.0) THEN - 2230 NBUF=NNUM*NLEN - 2231 IF(NBUF.GT.MBUF) THEN - 2232 PRINT *,'GETGB: INCREASE BUFFER FROM ',MBUF,' TO ',NBUF - 2233 NNUM=MBUF/NLEN - 2234 NBUF=NNUM*NLEN - 2235 ENDIF - 2236 CALL BAREAD(LUGI,NSKP,NBUF,LBUF,CBUF) - 2237 IF(LBUF.EQ.NBUF) THEN - 2238 c print *,'************** lux being set equal to lugi' - 2239 LUX=LUGI - 2240 J=MAX(J,0) - 2241 ENDIF - 2242 ENDIF - 2243 ENDIF - 2244 ENDIF - 2245 C - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2246 C SEARCH FOR REQUEST - 2247 KENS=0 - 2248 LGRIB=0 - -Page 57 Source Listing GETGBECE -2020-06-22 00:51 ecmwfensh.f - - 2249 KJ=J - 2250 K=J - 2251 KF=0 - 2252 IF(J.GE.0.AND.LUGI.EQ.LUX) THEN - 2253 LPDSP=0 - 2254 DO I=1,LPDS - 2255 IF(JPDS(I).NE.-1) THEN - 2256 LPDSP=LPDSP+1 - 2257 IPDSP(LPDSP)=I - 2258 JPDSP(LPDSP)=JPDS(I) - 2259 ENDIF - 2260 ENDDO - 2261 LGDSP=0 - 2262 IF(JPDS(3).EQ.255) THEN - 2263 DO I=1,LGDS - 2264 IF(JGDS(I).NE.-1) THEN - 2265 LGDSP=LGDSP+1 - 2266 IGDSP(LGDSP)=I - 2267 JGDSP(LGDSP)=JGDS(I) - 2268 ENDIF - 2269 ENDDO - 2270 ENDIF - 2271 LENSP=0 - 2272 IF(JPDS(23).EQ.3 .or. jpds(23).eq.0.or.jpds(23).eq.-1) THEN - 2273 DO I=1,5 - 2274 IF(JENS(I).NE.-1) THEN - 2275 LENSP=LENSP+1 - 2276 IENSP(LENSP)=I - 2277 JENSP(LENSP)=JENS(I) - 2278 ENDIF - 2279 ENDDO - 2280 else - 2281 print *,'!!! jpds(23) != 0 or 3, jpds(23)= ',jpds(23) - 2282 ENDIF - 2283 IRET=99 - 2284 DOWHILE(LGRIB.EQ.0.AND.KJ.LT.NNUM) - 2285 KJ=KJ+1 - 2286 LT=0 - 2287 IF(LPDSP.GT.0) THEN - 2288 CPDS=CBUF((KJ-1)*NLEN+26:(KJ-1)*NLEN+53) - 2289 KPTR=0 - 2290 CALL GBYTEC(CBUF,KPTR(3),(KJ-1)*NLEN*8+25*8,3*8) - 2291 CALL FI632(CPDS,KPTR,KPDS,IRET) - 2292 c print *, 'after fi632, iret=',iret - 2293 DO I=1,LPDSP - 2294 IP=IPDSP(I) - 2295 LT=LT+ABS(JPDS(IP)-KPDS(IP)) - 2296 ENDDO - 2297 ENDIF - 2298 IF(LT.EQ.0.AND.LGDSP.GT.0) THEN - 2299 CGDS=CBUF((KJ-1)*NLEN+54:(KJ-1)*NLEN+95) - 2300 KPTR=0 - 2301 CALL FI633(CGDS,KPTR,KGDS,IRET) - 2302 c print *, 'after fi633, iret=',iret - 2303 DO I=1,LGDSP - 2304 IP=IGDSP(I) - 2305 LT=LT+ABS(JGDS(IP)-KGDS(IP)) - -Page 58 Source Listing GETGBECE -2020-06-22 00:51 ecmwfensh.f - - 2306 ENDDO - 2307 ENDIF - 2308 c print *, 'lt=',lt,'lensp=',lensp - 2309 IF(LT.EQ.0.AND.LENSP.GT.0) THEN - 2310 CPDS(41:80)=CBUF((KJ-1)*NLEN+113:(KJ-1)*NLEN+152) - 2311 c CALL PDSEUP(KENS,KPROB,XPROB,KCLUST,KMEMBR,45,CPDS) - 2312 CALL ecmext(ktype,kfnum,ktot,45,CPDS) - 2313 DO I=1,LENSP - 2314 IP=IENSP(I) - 2315 LT=LT+ABS(JENS(IP)-KENS(IP)) - 2316 ENDDO - 2317 ENDIF - 2318 C - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2319 C READ AND UNPACK GRIB DATA - 2320 IF(LT.EQ.0) THEN - 2321 CALL GBYTEC(CBUF,LSKIP,(KJ-1)*NLEN*8,4*8) - 2322 CALL GBYTEC(CBUF,LGRIB,(KJ-1)*NLEN*8+20*8,4*8) - 2323 CGDS=CBUF((KJ-1)*NLEN+54:(KJ-1)*NLEN+95) - 2324 KPTR=0 - 2325 CALL FI633(CGDS,KPTR,KGDS,IRET) - 2326 c print *, 'after FI633, iret=',iret - 2327 IF(KPDS(23).EQ.3 .or. kpds(23).eq.0.or.kpds(23).eq.-1) THEN - 2328 CPDS(41:80)=CBUF((KJ-1)*NLEN+113:(KJ-1)*NLEN+152) - 2329 c CALL PDSEUP(KENS,KPROB,XPROB,KCLUST,KMEMBR,45,CPDS) - 2330 CALL ecmext(ktype,kfnum,ktot,45,CPDS) - 2331 else - 2332 print *,'!!! kpds(23) != 0 or 3, kpds(23)= ',kpds(23) - 2333 ENDIF - 2334 IF(LGRIB.LE.200+17*JF/8.AND.KGDS(2)*KGDS(3).LE.JF) THEN - 2335 CALL BAREAD(LUGB,LSKIP,LGRIB,LREAD,GRIB) - 2336 IF(LREAD.EQ.LGRIB) THEN - 2337 CALL W3FI63(GRIB,KPDS,KGDS,LB,F,KPTR,IRET) - 2338 c print *, 'after W3FI63, iret=',iret - 2339 IF(IRET.EQ.0) THEN - 2340 K=KJ - 2341 KF=KPTR(10) - 2342 ENDIF - 2343 ELSE - 2344 IRET=97 - 2345 ENDIF - 2346 ELSE - 2347 IRET=98 - 2348 ENDIF - 2349 ENDIF - 2350 ENDDO - 2351 ELSE - 2352 IRET=96 - 2353 ENDIF - 2354 C - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2355 RETURN - 2356 END - -Page 59 Source Listing GETGBECE -2020-06-22 00:51 Entry Points ecmwfensh.f - - - -ENTRY POINTS - - Name - - getgbece_ - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - ABS Func 2295 scalar 2295,2305,2315 - BAREAD Subr 2214 2214,2221,2236,2335 - CBUF Local 2186 CHAR 1 1 1048576 SAVE 2236,2288,2290,2299,2310,2321,2322 - ,2323,2328 - CGDS Local 2190 CHAR 1 1 42 2299,2301,2323,2325 - CHEAD Local 2189 CHAR 81 1 2 2214,2217,2218,2221,2223,2226 - CPDS Local 2190 CHAR 1 1 80 2288,2291,2310,2312,2328,2330 - ECMEXT Subr 2312 2312,2330 - F Dummy 2083 R(8) 8 1 0 ARG,INOUT 2337 - FI632 Subr 2291 2291 - FI633 Subr 2301 2301,2325 - GBYTEC Subr 2290 2290,2321,2322 - GETGBECE Subr 2082 - GRIB Local 2195 CHAR 1 1 0 2335,2337 - GRIB_OPEN_R Subr 2199 2199,2203 - I Local 2254 I(4) 4 scalar 2254,2255,2257,2258,2263,2264,2266 - ,2267,2273,2274,2276,2277,2293,229 - 4,2303,2304,2313,2314 - IENSP Local 2194 I(4) 4 1 5 2276,2314 - IGDSP Local 2193 I(4) 4 1 22 2266,2304 - IOS Local 2211 I(4) 4 scalar 2211,2215,2223,2226,2227,2229 - IP Local 2294 I(4) 4 scalar 2294,2295,2304,2305,2314,2315 - IPDSP Local 2193 I(4) 4 1 23 2257,2294 - IRET Dummy 2083 I(4) 4 scalar ARG,INOUT 2283,2291,2301,2325,2337,2339,2344 - ,2347,2352 - IRETO Local 2199 I(4) 4 scalar 2199,2200,2201,2203,2204,2205 - J Dummy 2082 I(4) 4 scalar ARG,INOUT 2198,2240,2249,2250,2252 - JENS Dummy 2082 I(4) 4 1 5 ARG,INOUT 2274,2277,2315 - JENSP Local 2194 I(4) 4 1 5 2277 - JF Dummy 2082 I(4) 4 scalar ARG,INOUT 2183,2184,2195,2334 - JGDS Dummy 2082 I(4) 4 1 22 ARG,INOUT 2264,2267,2305 - JGDSP Local 2193 I(4) 4 1 22 2267 - JPDS Dummy 2082 I(4) 4 1 25 ARG,INOUT 2255,2258,2262,2272,2281,2295 - JPDSP Local 2193 I(4) 4 1 23 2258 - K Dummy 2083 I(4) 4 scalar ARG,INOUT 2250,2340 - KENS Dummy 2083 I(4) 4 1 5 ARG,INOUT 2247,2315 - KF Dummy 2083 I(4) 4 scalar ARG,INOUT 2251,2341 - KFNUM Dummy 2084 I(4) 4 scalar ARG,INOUT 2312,2330 - KGDS Dummy 2083 I(4) 4 1 22 ARG,INOUT 2301,2305,2325,2334,2337 - KJ Local 2249 I(4) 4 scalar 2249,2284,2285,2288,2290,2299,2310 - ,2321,2322,2323,2328,2340 - KPDS Dummy 2083 I(4) 4 1 25 ARG,INOUT 2291,2295,2327,2332,2337 - KPTR Local 2192 I(4) 4 1 20 2289,2290,2291,2300,2301,2324,2325 - ,2337,2341 - -Page 60 Source Listing GETGBECE -2020-06-22 00:51 Symbol Table ecmwfensh.f - - Name Object Declared Type Bytes Dimen Elements Attributes References - - KTOT Dummy 2084 I(4) 4 scalar ARG,INOUT 2312,2330 - KTYPE Dummy 2084 I(4) 4 scalar ARG,INOUT 2312,2330 - LB Dummy 2083 L(4) 4 1 0 ARG,INOUT 2337 - LBUF Local 2236 I(4) 4 scalar 2236,2237 - LENSP Local 2271 I(4) 4 scalar 2271,2275,2276,2277,2309,2313 - LGDS Param 2181 I(4) 4 scalar 2193,2263 - LGDSP Local 2261 I(4) 4 scalar 2261,2265,2266,2267,2298,2303 - LGRIB Local 2213 I(4) 4 scalar 2213,2214,2215,2219,2221,2222,2248 - ,2284,2322,2334,2335,2336 - LPDS Param 2181 I(4) 4 scalar 2193,2254 - LPDSP Local 2253 I(4) 4 scalar 2253,2256,2257,2258,2287,2293 - LREAD Local 2214 I(4) 4 scalar 2214,2215,2219,2220,2221,2222,2335 - ,2336 - LSKIP Local 2321 I(4) 4 scalar 2321,2335 - LT Local 2286 I(4) 4 scalar 2286,2295,2298,2305,2309,2315,2320 - LUGB Dummy 2082 I(4) 4 scalar ARG,INOUT 2199,2201,2207,2335 - LUGI Dummy 2082 I(4) 4 scalar ARG,INOUT 2198,2203,2205,2207,2214,2221,2236 - ,2239,2252 - LUX Local 2187 I(4) 4 scalar SAVE 2188,2198,2225,2239,2252 - MAX Func 2240 scalar 2240 - MBUF Param 2185 I(4) 4 scalar 2186,2231,2232,2233 - NBUF Local 2230 I(4) 4 scalar 2230,2231,2232,2234,2236,2237 - NLEN Local 2187 I(4) 4 scalar SAVE 2226,2227,2230,2233,2234,2288,2290 - ,2299,2310,2321,2322,2323,2328 - NNUM Local 2187 I(4) 4 scalar SAVE 2226,2228,2230,2233,2234,2284 - NSKP Local 2187 I(4) 4 scalar SAVE 2212,2214,2219,2220,2221,2222,2226 - ,2227,2236 - W3FI63 Subr 2337 2337 - -Page 61 Source Listing GETGBECE -2020-06-22 00:51 ecmwfensh.f - - 2357 c - 2358 C----------------------------------------------------------------------c - 2359 C----------------------------------------------------------------------c - 2360 c - 2361 SUBROUTINE ecmext(ktype,kfnum,ktot,ILAST,MSGA) - 2362 C$$$ SUBPROGRAM DOCUMENTATION BLOCK - 2363 C . . . . - 2364 C SUBPROGRAM: ecmext.f UNPACKS GRIB PDS EXTENSION 41- FOR ENSEMBLE - 2365 C PRGMMR: WOBUS ORG: NP20 DATE: 2001-01-16 - 2366 C - 2367 C ABSTRACT: unpacks GRIB pds extension starting on byte 41 for ECMWF - 2368 c ensemble files. NOTE that this extension format is - 2369 c completely different from NCEP's extension format, and - 2370 c this subroutine will not work if you try to read NCEP - 2371 c ensemble files. This subroutine will unpack bytes - 2372 c 41-52 of the pds header extension. - 2373 C - 2374 C PROGRAM HISTORY LOG: - 2375 c 97-01-17 Tim Marchok (Most of the code, however, is taken from - 2376 c the pdseup.f subroutine, originally written - 2377 c by Mark Iredell and Zoltan Toth). - 2378 C 97-01-17 MARCHOK original program - 2379 C 01-01-16 WOBUS added DOCBLOCK - 2380 c - 2381 C - 2382 C USAGE: call ecmext(ktype,kfnum,ktot,ILAST,MSGA) - 2383 C INPUT ARGUMENT LIST: - 2384 C ILAST - LAST BYTE TO BE UNPACKED (IF GREATER/EQUAL TO FIRST BYT - 2385 C IN ANY OF FOUR SECTIONS BELOW, WHOLE SECTION IS PACKED. - 2386 C MSGA - FULL PDS SECTION, INCLUDING NEW ENSEMBLE EXTENSION - 2387 C - 2388 C OUTPUT ARGUMENT LIST: - 2389 c - 2390 c ********* ECMWF PDS EXTENSION BYTE LIST **************** - 2391 c - 2392 c ludn Byte 41 (Local Use Definition Number; should = 1) - 2393 c kclass Byte 42 (1=Operations; 2=Research) - 2394 c ktype Byte 43 (10=Control Fcst; 11=Perturbed Forecast) - 2395 c kstream Bytes 44-45 (1035=Ensemble Forecasts) - 2396 c kver Bytes 46-49 (Version Number/Experiment Identifier; - 2397 c 4 ascii characters, right justified) - 2398 c kfnum Byte 50 (Ensemble Forecast Number; - 2399 c Control Forecast is number 0, - 2400 c perturbed forecast are 1-nn, where - 2401 c positive perturbation is an odd number, - 2402 c negative perturbation is an even number. - 2403 c ktot Byte 51 (Total number of forecasts in ensemble. - 2404 c This number includeds the control forecast). - 2405 c ----- Byte 52 (Reserved, should be set to 0). - 2406 c - 2407 C - 2408 C REMARKS: USE PDSENS.F FOR PACKING PDS ENSEMBLE EXTENSION. - 2409 C - 2410 C ATTRIBUTES: - 2411 C LANGUAGE: CF77 FORTRAN - 2412 C MACHINE: CRAY, WORKSTATIONS - 2413 C - -Page 62 Source Listing ECMEXT -2020-06-22 00:51 ecmwfensh.f - - 2414 C$$$ - 2415 C - 2416 INTEGER KENS(5),KPROB(2),KCLUST(16),KMEMBR(80) - 2417 integer ludn,kclass,ktype,kstream,kver,kfnum,ktot - 2418 DIMENSION XPROB(2) - 2419 CHARACTER*1 MSGA(100) - 2420 character*1 cver(4) - 2421 c - 2422 C CHECKING TOTAL NUMBER OF BYTES IN PDS (IBYTES) - 2423 c print *,' ' - 2424 CALL GBYTEC(MSGA, IBYTES, 0,24) - 2425 c PRINT *,'IBYTES (length of pds) = ',IBYTES - 2426 IF (ILAST.GT.IBYTES) THEN - 2427 C ILAST=IBYTES - 2428 PRINT *,'ERROR - THERE ARE ONLY ',IBYTES, ' BYTES IN THE PDS.' - 2429 GO TO 333 - 2430 ENDIF - 2431 IF (ILAST.LT.41) THEN - 2432 PRINT *,'WARNING - SUBROUTINE FOR UNPACKING BYTES 41 AND ABOVE' - 2433 GO TO 333 - 2434 ENDIF - 2435 C UNPACKING FIRST SECTION (GENERAL INFORMATION) - 2436 c - 2437 CALL GBYTEC(MSGA,ludn,40*8,8) - 2438 c print *,'ludn= ',ludn - 2439 CALL GBYTEC(MSGA,kclass,41*8,8) - 2440 c print *,'kclass= ',kclass - 2441 CALL GBYTEC(MSGA,ktype,42*8,8) - 2442 c print *,'ktype= ',ktype - 2443 CALL GBYTEC(MSGA,kstream,43*8,16) - 2444 c print *,'kstream= ',kstream - 2445 c CALL GBYTEC(MSGA,kver,45*8,32) - 2446 do ii=1,4 - 2447 cver(ii) = msga(ii+45) - 2448 enddo - 2449 c print '(17a,3x,4a1)','Version Number = ',cver - 2450 CALL GBYTEC(MSGA,kfnum,49*8,8) - 2451 c print *,'kfnum= ',kfnum - 2452 CALL GBYTEC(MSGA,ktot,50*8,8) - 2453 c print *,'ktot= ',ktot - 2454 CALL GBYTEC(MSGA,junk,51*8,8) - 2455 c print *,'Byte 52= ',junk - 2456 c - 2457 c & ,' str=',kstream,' ver=',kver,' mem=',kfnum - 2458 c print '(7(a6,i6))',' lu=',ludn,' cls=',kclass,' typ=',ktype - 2459 c & ,' str=',kstream,' mem=',kfnum - 2460 c & ,' tot=',ktot,' b52=',junk - 2461 goto 333 - 2462 C - 2463 333 CONTINUE - 2464 RETURN - 2465 END - -Page 63 Source Listing ECMEXT -2020-06-22 00:51 Entry Points ecmwfensh.f - - - -ENTRY POINTS - - Name - - ecmext_ - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - 333 Label 2463 2429,2433,2461 - CVER Local 2420 CHAR 1 1 4 2447 - ECMEXT Subr 2361 - GBYTEC Subr 2424 2424,2437,2439,2441,2443,2450,2452 - ,2454 - IBYTES Local 2424 I(4) 4 scalar 2424,2426,2428 - II Local 2446 I(4) 4 scalar 2446,2447 - ILAST Dummy 2361 I(4) 4 scalar ARG,INOUT 2426,2431 - JUNK Local 2454 I(4) 4 scalar 2454 - KCLASS Local 2417 I(4) 4 scalar 2439 - KCLUST Local 2416 I(4) 4 1 16 - KENS Local 2416 I(4) 4 1 5 - KFNUM Dummy 2361 I(4) 4 scalar ARG,INOUT 2450 - KMEMBR Local 2416 I(4) 4 1 80 - KPROB Local 2416 I(4) 4 1 2 - KSTREAM Local 2417 I(4) 4 scalar 2443 - KTOT Dummy 2361 I(4) 4 scalar ARG,INOUT 2452 - KTYPE Dummy 2361 I(4) 4 scalar ARG,INOUT 2441 - KVER Local 2417 I(4) 4 scalar - LUDN Local 2417 I(4) 4 scalar 2437 - MSGA Dummy 2361 CHAR 1 1 100 ARG,INOUT 2424,2437,2439,2441,2443,2447,2450 - ,2452,2454 - XPROB Local 2418 R(8) 8 1 2 - -Page 64 Source Listing ECMEXT -2020-06-22 00:51 ecmwfensh.f - - 2466 c - 2467 c----------------------------------------------------------------------c - 2468 c----------------------------------------------------------------------c - 2469 c - 2470 subroutine grib_close (lug,iret) - 2471 c$$$ SUBPROGRAM DOCUMENTATION BLOCK - 2472 C . . . . - 2473 C SUBPROGRAM: grib_close - 2474 C PRGMMR: WOBUS ORG: NP20 DATE: 2001-01-16 - 2475 C - 2476 C ABSTRACT: START ABSTRACT HERE AND INDENT TO COLUMN 5 ON THE - 2477 C FOLLOWING LINES. PLEASE PROVIDE A BRIEF DESCRIPTION OF - 2478 C WHAT THE SUBPROGRAM DOES. - 2479 C - 2480 C PROGRAM HISTORY LOG: - 2481 C 97-01-17 MARCHOK original program - 2482 C 01-01-16 WOBUS added DOCBLOCK - 2483 C - 2484 C USAGE: CALL PROGRAM-NAME(INARG1, INARG2, WRKARG, OUTARG1, ... ) - 2485 C INPUT ARGUMENT LIST: - 2486 C INARG1 - GENERIC DESCRIPTION, INCLUDING CONTENT, UNITS, - 2487 C INARG2 - TYPE. EXPLAIN FUNCTION IF CONTROL VARIABLE. - 2488 C - 2489 C OUTPUT ARGUMENT LIST: (INCLUDING WORK ARRAYS) - 2490 C WRKARG - GENERIC DESCRIPTION, ETC., AS ABOVE. - 2491 C OUTARG1 - EXPLAIN COMPLETELY IF ERROR RETURN - 2492 C ERRFLAG - EVEN IF MANY LINES ARE NEEDED - 2493 C - 2494 C INPUT FILES: (DELETE IF NO INPUT FILES IN SUBPROGRAM) - 2495 C - 2496 C OUTPUT FILES: (DELETE IF NO OUTPUT FILES IN SUBPROGRAM) - 2497 C - 2498 C REMARKS: LIST CAVEATS, OTHER HELPFUL HINTS OR INFORMATION - 2499 C - 2500 C ATTRIBUTES: - 2501 C LANGUAGE: INDICATE EXTENSIONS, COMPILER OPTIONS - 2502 C MACHINE: IBM SP - 2503 C - 2504 C$$$ - 2505 - 2506 C ABSTRACT: This subroutine must be called before any attempt is - 2507 C made to read from the input GRIB files. The GRIB and index files - 2508 C are opened with a call to baopenr. This call to baopenr was not - 2509 C needed in the cray version of this program (the files could be - 2510 C opened with a simple Cray assign statement), but the GRIB-reading - 2511 C utilities on the SP do require calls to this subroutine (it has - 2512 C something to do with the GRIB I/O being done in C on the SP, and - 2513 C the C I/O package needs an explicit open statement). - 2514 C - 2515 C INPUT: - 2516 C lug The Fortran unit number for the GRIB file - 2517 C OUTPUT: - 2518 C iret The return code from this subroutine - 2519 - 2520 character unitname*11 - 2521 character fname*80 - 2522 - -Page 65 Source Listing GRIB_CLOSE -2020-06-22 00:51 ecmwfensh.f - - 2523 unitname(1:11) = "XLFUNIT_ " - 2524 if (lug.lt.100) then - 2525 write(unitname(9:10),'(I2)') lug - 2526 else - 2527 write(unitname(9:11),'(I3)') lug - 2528 endif - 2529 call getenv(unitname,fname) - 2530 c print *,' ' - 2531 c print *,' in grib_close: unit: ',lug - 2532 c print *,' in grib_close: fname: ',fname - 2533 ioret=0 - 2534 call baclose (lug,fname,ioret) - 2535 - 2536 c print *,' ' - 2537 c print *,'baclose: ioret= ',ioret - 2538 - 2539 iret=0 - 2540 if (ioret /= 0) then - 2541 print *,' ' - 2542 print *,'!!! ERROR in grib_close closing grib file' - 2543 print *,'!!! baclose return code = ioret = ',ioret - 2544 iret = 93 - 2545 return - 2546 endif - 2547 - 2548 return - 2549 end - - -ENTRY POINTS - - Name - - grib_close_ - -Page 66 Source Listing GRIB_CLOSE -2020-06-22 00:51 Symbol Table ecmwfensh.f - - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - BACLOSE Subr 2534 2534 - FNAME Local 2521 CHAR 80 scalar 2529,2534 - GETENV Subr 2529 2529 - GRIB_CLOSE Subr 2470 - IORET Local 2533 I(4) 4 scalar 2533,2534,2540,2543 - IRET Dummy 2470 I(4) 4 scalar ARG,INOUT 2539,2544 - LUG Dummy 2470 I(4) 4 scalar ARG,INOUT 2524,2525,2527,2534 - UNITNAME Local 2520 CHAR 11 scalar 2523,2525,2527,2529 - -Page 67 Source Listing GRIB_CLOSE -2020-06-22 00:51 ecmwfensh.f - - 2550 c - 2551 c----------------------------------------------------------------------c - 2552 c----------------------------------------------------------------------c - 2553 c - 2554 subroutine grib_open (lug,iret) - 2555 c$$$ SUBPROGRAM DOCUMENTATION BLOCK - 2556 C . . . . - 2557 C SUBPROGRAM: grib_open - 2558 C PRGMMR: WOBUS ORG: NP20 DATE: 2001-01-16 - 2559 C - 2560 C ABSTRACT: START ABSTRACT HERE AND INDENT TO COLUMN 5 ON THE - 2561 C FOLLOWING LINES. PLEASE PROVIDE A BRIEF DESCRIPTION OF - 2562 C WHAT THE SUBPROGRAM DOES. - 2563 C - 2564 C PROGRAM HISTORY LOG: - 2565 C 97-01-17 MARCHOK original program - 2566 C 01-01-16 WOBUS added DOCBLOCK - 2567 C - 2568 C USAGE: CALL PROGRAM-NAME(INARG1, INARG2, WRKARG, OUTARG1, ... ) - 2569 C INPUT ARGUMENT LIST: - 2570 C INARG1 - GENERIC DESCRIPTION, INCLUDING CONTENT, UNITS, - 2571 C INARG2 - TYPE. EXPLAIN FUNCTION IF CONTROL VARIABLE. - 2572 C - 2573 C OUTPUT ARGUMENT LIST: (INCLUDING WORK ARRAYS) - 2574 C WRKARG - GENERIC DESCRIPTION, ETC., AS ABOVE. - 2575 C OUTARG1 - EXPLAIN COMPLETELY IF ERROR RETURN - 2576 C ERRFLAG - EVEN IF MANY LINES ARE NEEDED - 2577 C - 2578 C INPUT FILES: (DELETE IF NO INPUT FILES IN SUBPROGRAM) - 2579 C - 2580 C OUTPUT FILES: (DELETE IF NO OUTPUT FILES IN SUBPROGRAM) - 2581 C - 2582 C REMARKS: LIST CAVEATS, OTHER HELPFUL HINTS OR INFORMATION - 2583 C - 2584 C ATTRIBUTES: - 2585 C LANGUAGE: INDICATE EXTENSIONS, COMPILER OPTIONS - 2586 C MACHINE: IBM SP - 2587 C - 2588 C$$$ - 2589 - 2590 C ABSTRACT: This subroutine must be called before any attempt is - 2591 C made to access the GRIB file. The GRIB file - 2592 C is opened with a call to baopen. This call to baopen was not - 2593 C needed in the cray version of this program (the files could be - 2594 C opened with a simple Cray assign statement), but the GRIB I/O - 2595 C utilities on the SP do require calls to this subroutine (it has - 2596 C something to do with the GRIB I/O being done in C on the SP, and - 2597 C the C I/O package needs an explicit open statement). - 2598 C - 2599 C INPUT: - 2600 C lug The Fortran unit number for the GRIB file - 2601 C OUTPUT: - 2602 C iret The return code from this subroutine - 2603 - 2604 character unitname*11 - 2605 character fname*80 - 2606 - -Page 68 Source Listing GRIB_OPEN -2020-06-22 00:51 ecmwfensh.f - - 2607 c unitname(1:8) = "XLFUNIT_" - 2608 unitname(1:11) = "XLFUNIT_ " - 2609 if (lug.lt.100) then - 2610 write(unitname(9:10),'(I2)') lug - 2611 else - 2612 write(unitname(9:11),'(I3)') lug - 2613 endif - 2614 c write(unitname(9:10),'(I2)') lug - 2615 call getenv(unitname,fname) - 2616 c print *,' ' - 2617 c print *,' in grib_open: unit: ',lug - 2618 c print *,' in grib_open: fname: ',fname - 2619 ioret=0 - 2620 call baopen (lug,fname,ioret) - 2621 - 2622 c print *,' ' - 2623 c print *,'baopen: ioret= ',ioret - 2624 - 2625 iret=0 - 2626 if (ioret /= 0) then - 2627 print *,' ' - 2628 print *,'!!! ERROR in grib_open opening grib file' - 2629 print *,'!!! baopen return code = ioret = ',ioret - 2630 iret = 93 - 2631 return - 2632 endif - 2633 - 2634 return - 2635 end - -Page 69 Source Listing GRIB_OPEN -2020-06-22 00:51 Entry Points ecmwfensh.f - - - -ENTRY POINTS - - Name - - grib_open_ - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - BAOPEN Subr 2620 2620 - FNAME Local 2605 CHAR 80 scalar 2615,2620 - GETENV Subr 2615 2615 - GRIB_OPEN Subr 2554 - IORET Local 2619 I(4) 4 scalar 2619,2620,2626,2629 - IRET Dummy 2554 I(4) 4 scalar ARG,INOUT 2625,2630 - LUG Dummy 2554 I(4) 4 scalar ARG,INOUT 2609,2610,2612,2620 - UNITNAME Local 2604 CHAR 11 scalar 2608,2610,2612,2615 - -Page 70 Source Listing GRIB_OPEN -2020-06-22 00:51 ecmwfensh.f - - 2636 c - 2637 c----------------------------------------------------------------------c - 2638 c----------------------------------------------------------------------c - 2639 c - 2640 subroutine grib_open_wa (lug,iret) - 2641 c$$$ SUBPROGRAM DOCUMENTATION BLOCK - 2642 C . . . . - 2643 C SUBPROGRAM: grib_open_wa - 2644 C PRGMMR: WOBUS ORG: NP20 DATE: 2001-01-16 - 2645 C - 2646 C ABSTRACT: START ABSTRACT HERE AND INDENT TO COLUMN 5 ON THE - 2647 C FOLLOWING LINES. PLEASE PROVIDE A BRIEF DESCRIPTION OF - 2648 C WHAT THE SUBPROGRAM DOES. - 2649 C - 2650 C PROGRAM HISTORY LOG: - 2651 C 97-01-17 MARCHOK original program - 2652 C 01-01-16 WOBUS added DOCBLOCK - 2653 C - 2654 C USAGE: CALL PROGRAM-NAME(INARG1, INARG2, WRKARG, OUTARG1, ... ) - 2655 C INPUT ARGUMENT LIST: - 2656 C INARG1 - GENERIC DESCRIPTION, INCLUDING CONTENT, UNITS, - 2657 C INARG2 - TYPE. EXPLAIN FUNCTION IF CONTROL VARIABLE. - 2658 C - 2659 C OUTPUT ARGUMENT LIST: (INCLUDING WORK ARRAYS) - 2660 C WRKARG - GENERIC DESCRIPTION, ETC., AS ABOVE. - 2661 C OUTARG1 - EXPLAIN COMPLETELY IF ERROR RETURN - 2662 C ERRFLAG - EVEN IF MANY LINES ARE NEEDED - 2663 C - 2664 C INPUT FILES: (DELETE IF NO INPUT FILES IN SUBPROGRAM) - 2665 C - 2666 C OUTPUT FILES: (DELETE IF NO OUTPUT FILES IN SUBPROGRAM) - 2667 C - 2668 C REMARKS: LIST CAVEATS, OTHER HELPFUL HINTS OR INFORMATION - 2669 C - 2670 C ATTRIBUTES: - 2671 C LANGUAGE: INDICATE EXTENSIONS, COMPILER OPTIONS - 2672 C MACHINE: IBM SP - 2673 C - 2674 C$$$ - 2675 - 2676 C ABSTRACT: This subroutine must be called before any attempt is - 2677 C made to write to from the output GRIB files. The GRIB file - 2678 C is opened with a call to baopenwa. This call to baopenwa was not - 2679 C needed in the cray version of this program (the files could be - 2680 C opened with a simple Cray assign statement), but the GRIB-writing - 2681 C utilities on the SP do require calls to this subroutine (it has - 2682 C something to do with the GRIB I/O being done in C on the SP, and - 2683 C the C I/O package needs an explicit open statement). - 2684 C - 2685 C INPUT: - 2686 C lug The Fortran unit number for the GRIB file - 2687 C OUTPUT: - 2688 C iret The return code from this subroutine - 2689 - 2690 c character unitname*10 - 2691 character unitname*11 - 2692 character fname*80 - -Page 71 Source Listing GRIB_OPEN_WA -2020-06-22 00:51 ecmwfensh.f - - 2693 - 2694 c unitname(1:8) = "XLFUNIT_" - 2695 unitname(1:11) = "XLFUNIT_ " - 2696 if (lug.lt.100) then - 2697 write(unitname(9:10),'(I2)') lug - 2698 else - 2699 write(unitname(9:11),'(I3)') lug - 2700 endif - 2701 c write(unitname(9:10),'(I2)') lug - 2702 call getenv(unitname,fname) - 2703 c print *,' ' - 2704 c print *,' in grib_open_wa: unit: ',lug - 2705 c print *,' in grib_open_wa: fname: ',fname - 2706 ioret=0 - 2707 call baopenwa (lug,fname,ioret) - 2708 - 2709 c print *,' ' - 2710 c print *,'baopenwa: ioret= ',ioret - 2711 - 2712 iret=0 - 2713 if (ioret /= 0) then - 2714 print *,' ' - 2715 print *,'!!! ERROR in grib_open_wa opening grib file' - 2716 print *,'!!! baopenwa return code = ioret = ',ioret - 2717 iret = 93 - 2718 return - 2719 endif - 2720 - 2721 return - 2722 end - -Page 72 Source Listing GRIB_OPEN_WA -2020-06-22 00:51 Entry Points ecmwfensh.f - - - -ENTRY POINTS - - Name - - grib_open_wa_ - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - BAOPENWA Subr 2707 2707 - FNAME Local 2692 CHAR 80 scalar 2702,2707 - GETENV Subr 2702 2702 - GRIB_OPEN_WA Subr 2640 - IORET Local 2706 I(4) 4 scalar 2706,2707,2713,2716 - IRET Dummy 2640 I(4) 4 scalar ARG,INOUT 2712,2717 - LUG Dummy 2640 I(4) 4 scalar ARG,INOUT 2696,2697,2699,2707 - UNITNAME Local 2691 CHAR 11 scalar 2695,2697,2699,2702 - -Page 73 Source Listing GRIB_OPEN_WA -2020-06-22 00:51 ecmwfensh.f - - 2723 c - 2724 c----------------------------------------------------------------------c - 2725 c----------------------------------------------------------------------c - 2726 c - 2727 subroutine grib_open_r (lug,iret) - 2728 c$$$ SUBPROGRAM DOCUMENTATION BLOCK - 2729 C . . . . - 2730 C SUBPROGRAM: grib_open_r - 2731 C PRGMMR: WOBUS ORG: NP20 DATE: 2001-01-16 - 2732 C - 2733 C ABSTRACT: This subroutine must be called before any attempt is - 2734 C made to read from the input GRIB files. The GRIB and index files - 2735 C are opened with a call to baopenr. This call to baopenr was not - 2736 C needed in the cray version of this program (the files could be - 2737 C opened with a simple Cray assign statement), but the GRIB-reading - 2738 C utilities on the SP do require calls to this subroutine (it has - 2739 C something to do with the GRIB I/O being done in C on the SP, and - 2740 C the C I/O package needs an explicit open statement). - 2741 C - 2742 C PROGRAM HISTORY LOG: - 2743 C 97-01-17 MARCHOK original program - 2744 C 01-01-16 WOBUS added DOCBLOCK - 2745 C - 2746 C USAGE: CALL PROGRAM-NAME(INARG1, INARG2, WRKARG, OUTARG1, ... ) - 2747 C INPUT ARGUMENT LIST: - 2748 C lug The Fortran unit number for the GRIB file - 2749 C - 2750 C OUTPUT ARGUMENT LIST: (INCLUDING WORK ARRAYS) - 2751 C iret The return code from this subroutine - 2752 C - 2753 C INPUT FILES: (DELETE IF NO INPUT FILES IN SUBPROGRAM) - 2754 C lug The Fortran unit number for the GRIB file - 2755 C - 2756 C REMARKS: LIST CAVEATS, OTHER HELPFUL HINTS OR INFORMATION - 2757 C - 2758 C ATTRIBUTES: - 2759 C LANGUAGE: INDICATE EXTENSIONS, COMPILER OPTIONS - 2760 C MACHINE: IBM SP - 2761 C - 2762 C$$$ - 2763 - 2764 C - 2765 C INPUT: - 2766 C OUTPUT: - 2767 - 2768 c character unitname*10 - 2769 character unitname*11 - 2770 character fname*80 - 2771 - 2772 c unitname(1:8) = "XLFUNIT_" - 2773 unitname(1:11) = "XLFUNIT_ " - 2774 if (lug.lt.100) then - 2775 write(unitname(9:10),'(I2)') lug - 2776 else - 2777 write(unitname(9:11),'(I3)') lug - 2778 endif - 2779 c write(unitname(9:10),'(I2)') lug - -Page 74 Source Listing GRIB_OPEN_R -2020-06-22 00:51 ecmwfensh.f - - 2780 call getenv(unitname,fname) - 2781 c print *,' ' - 2782 c print *,' in grib_open_r: unit: ',lug - 2783 c print *,' in grib_open_r: fname: ',fname - 2784 ioret=0 - 2785 call baopenr (lug,fname,ioret) - 2786 - 2787 c print *,' ' - 2788 c print *,'baopenr: ioret= ',ioret - 2789 - 2790 iret=0 - 2791 if (ioret /= 0) then - 2792 print *,' ' - 2793 print *,'!!! ERROR in sub grib_open_r opening grib file' - 2794 print *,'!!! baopenr return code = ioret = ',ioret - 2795 iret = 93 - 2796 return - 2797 endif - 2798 - 2799 return - 2800 end - - -ENTRY POINTS - - Name - - grib_open_r_ - -Page 75 Source Listing GRIB_OPEN_R -2020-06-22 00:51 Symbol Table ecmwfensh.f - - - -SYMBOL CROSS REFERENCE - - Name Object Declared Type Bytes Dimen Elements Attributes References - - BAOPENR Subr 2785 2785 - FNAME Local 2770 CHAR 80 scalar 2780,2785 - GETENV Subr 2780 2780 - GRIB_OPEN_R Subr 2727 - IORET Local 2784 I(4) 4 scalar 2784,2785,2791,2794 - IRET Dummy 2727 I(4) 4 scalar ARG,INOUT 2790,2795 - LUG Dummy 2727 I(4) 4 scalar ARG,INOUT 2774,2775,2777,2785 - UNITNAME Local 2769 CHAR 11 scalar 2773,2775,2777,2780 - -Page 76 Source Listing GRIB_OPEN_R -2020-06-22 00:51 Subprograms/Common Blocks ecmwfensh.f - - - -SUBPROGRAMS/COMMON BLOCKS - - Name Object Declared Type Bytes Dimen Elements Attributes References - - ADJEXT Subr 1394 - ADJPDS Subr 1162 - CREATE_STATS Subr 1059 - ECMEXT Subr 2361 - ECMWFENSH Prog 1 - GETGBECE Subr 2082 - GRANGE Subr 2037 - GRIB_CLOSE Subr 2470 - GRIB_OPEN Subr 2554 - GRIB_OPEN_R Subr 2727 - GRIB_OPEN_WA Subr 2640 - OUTPUT Subr 1508 - OUTPUT_STATS Subr 1584 - SRANGE Subr 1894 - -COMPILER OPTIONS BEING USED - - -align noall -align nonone - -align nocommons -align nodcommons - -align noqcommons -align nozcommons - -align records -align nosequence - -align norec1byte -align norec2byte - -align norec4byte -align norec8byte - -align norec16byte -align norec32byte - -align norec64byte -align noarray8byte - -align noarray16byte -align noarray32byte - -align noarray64byte -align noarray128byte - -align noarray256byte -altparam - -assume accuracy_sensitive -assume nobscc - -assume nobuffered_io -assume nobuffered_stdout - -assume nobyterecl -assume nocontiguous_assumed_shape - -assume nocontiguous_pointer -assume nocc_omp - -assume nocstring -assume nodummy_aliases - -assume nofpe_summary -assume noieee_fpe_flags - -assume nominus0 -assume noold_boz - -assume old_complex_align -assume old_unit_star - -assume old_ldout_format -assume noold_logical_assign - -assume noold_logical_ldio -assume old_maxminloc - -assume old_xor -assume noprotect_allocates - -assume protect_constants -assume noprotect_parens - -assume split_common -assume source_include - -assume nostd_intent_in -assume std_minus0_rounding - -assume nostd_mod_proc_name -assume std_value - -assume realloc_lhs -assume underscore - -assume no2underscores no -auto - -auto_scalar no -bintext - -ccdefault default -check noarg_temp_created - -check noassume -check nobounds - -check nocontiguous -check noformat - -check nooutput_conversion -check nooverflow - -check nopointers -check noshape - -Page 77 Source Listing GRIB_OPEN_R -2020-06-22 00:51 ecmwfensh.f - - -check nostack -check nouninitialized - -coarray-num-procs 0 no -coarray-config-file - -convert native -cross_reference - -D __INTEL_COMPILER=1800 -D __INTEL_COMPILER_UPDATE=5 - -D __unix__ -D __unix - -D __linux__ -D __linux - -D __gnu_linux__ -D unix - -D linux -D __ELF__ - -D __x86_64 -D __x86_64__ - -D __amd64 -D __amd64__ - -D __INTEL_COMPILER_BUILD_DATE=20180823 -D __INTEL_OFFLOAD - -D __i686 -D __i686__ - -D __pentiumpro -D __pentiumpro__ - -D __pentium4 -D __pentium4__ - -D __tune_pentium4__ -D __SSE2__ - -D __SSE2_MATH__ -D __SSE__ - -D __SSE_MATH__ -D __MMX__ - -double_size 64 no -d_lines - no -Qdyncom -error_limit 30 - no -f66 no -f77rtl - no -fast -fpscomp nofilesfromcmd - -fpscomp nogeneral -fpscomp noioformat - -fpscomp noldio_spacing -fpscomp nologicals - -fixed no -fpconstant - -fpe3 -fprm nearest - no -ftz -fp_model noprecise - -fp_model fast -fp_model nostrict - -fp_model nosource -fp_model nodouble - -fp_model noextended -fp_model novery_fast - -fp_model noexcept -fp_model nono_except - -heap_arrays 0 no -threadprivate_compat - -g0 -iface nomixed_str_len_arg - -iface nono_mixed_str_len_arg -init noarrays - -init nohuge -init noinfinity - -init nominus_huge -init nominus_infinity - -init nominus_tiny -init nonan - -init nosnan -init notiny - -init nozero no -intconstant - -integer_size 32 no -mixed_str_len_arg - no -module -names lowercase - no -noinclude -offload-build=host - -O2 no -pad_source - -real_size 64 no -recursive - -reentrancy threaded no -sharable_localsaves - -vec=simd -show nofullpath - -show noinclude -show map - -show options no -syntax_only - no -threadcom no -U - no -vms -w noall - -w nonone -w alignments - -w nodeclarations -w general - -w noignore_bounds -w noignore_loc - -w nointerfaces -w notruncated_source - -w uncalled -w uninitialized - -w nounused -w usage - no -wrap-margins - - -Page 78 Source Listing GRIB_OPEN_R -2020-06-22 00:51 ecmwfensh.f - - -includepath : /apps/intel/parallel_studio_xe_2018.4.057/compilers_and_libraries_2018/linux/tbb/include/,/usr/local/include/, - .f,./.f,/apps/intel/parallel_studio_xe_2018.4.057/compilers_and_libraries_2018/linux/ipp/include/.f,/apps/intel/parallel_studio_xe_2018.4.057/compilers_and_libraries_2018/linux/mkl/include/.f, - /apps/intel/parallel_studio_xe_2018.4.057/compilers_and_libraries_2018/linux/pstl/include/.f,/apps/intel/parallel_studio_xe_2018.4.057/compilers_and_libraries_2018/linux/tbb/include/.f, - /apps/intel/parallel_studio_xe_2018.4.057/compilers_and_libraries_2018/linux/daal/include/.f,/apps/intel/compilers_and_libraries_2018.5.274/linux/compiler/include/intel64/.f, - /apps/intel/compilers_and_libraries_2018.5.274/linux/compiler/include/icc/.f,/apps/intel/compilers_and_libraries_2018.5.274/linux/compiler/include/.f, - /usr/local/include/.f,/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/.f,/usr/include/.f,/usr/include/.f, - /usr/include/.f - -list filename : ecmwfensh.lst - -o filename : none - -COMPILER: Intel(R) Fortran 18.0-1651 diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/enum.py b/sorc/ens_tracker.v1.1.15.2/tclogg/enum.py deleted file mode 100644 index 887f91e131..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/tclogg/enum.py +++ /dev/null @@ -1,870 +0,0 @@ -# Copyright (c) 2013, Ethan Furman. -# All rights reserved. - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: - -# Redistributions of source code must retain the above -# copyright notice, this list of conditions and the -# following disclaimer. - -# Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials -# provided with the distribution. - -# Neither the name Ethan Furman nor the names of any -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -"""Python Enumerations""" - -import sys as _sys - -__all__ = ['Enum', 'IntEnum', 'unique'] - -version = 1, 1, 6 - -pyver = float('%s.%s' % _sys.version_info[:2]) - -try: - any -except NameError: - def any(iterable): - for element in iterable: - if element: - return True - return False - -try: - from collections import OrderedDict -except ImportError: - OrderedDict = None - -try: - basestring -except NameError: - # In Python 2 basestring is the ancestor of both str and unicode - # in Python 3 it's just str, but was missing in 3.1 - basestring = str - -try: - unicode -except NameError: - # In Python 3 unicode no longer exists (it's just str) - unicode = str - -class _RouteClassAttributeToGetattr(object): - """Route attribute access on a class to __getattr__. - - This is a descriptor, used to define attributes that act differently when - accessed through an instance and through a class. Instance access remains - normal, but access to an attribute through a class will be routed to the - class's __getattr__ method; this is done by raising AttributeError. - - """ - def __init__(self, fget=None): - self.fget = fget - - def __get__(self, instance, ownerclass=None): - if instance is None: - raise AttributeError() - return self.fget(instance) - - def __set__(self, instance, value): - raise AttributeError("can't set attribute") - - def __delete__(self, instance): - raise AttributeError("can't delete attribute") - - -def _is_descriptor(obj): - """Returns True if obj is a descriptor, False otherwise.""" - return ( - hasattr(obj, '__get__') or - hasattr(obj, '__set__') or - hasattr(obj, '__delete__')) - - -def _is_dunder(name): - """Returns True if a __dunder__ name, False otherwise.""" - return (name[:2] == name[-2:] == '__' and - name[2:3] != '_' and - name[-3:-2] != '_' and - len(name) > 4) - - -def _is_sunder(name): - """Returns True if a _sunder_ name, False otherwise.""" - return (name[0] == name[-1] == '_' and - name[1:2] != '_' and - name[-2:-1] != '_' and - len(name) > 2) - - -def _make_class_unpicklable(cls): - """Make the given class un-picklable.""" - def _break_on_call_reduce(self, protocol=None): - raise TypeError('%r cannot be pickled' % self) - cls.__reduce_ex__ = _break_on_call_reduce - cls.__module__ = '' - - -class _EnumDict(dict): - """Track enum member order and ensure member names are not reused. - - EnumMeta will use the names found in self._member_names as the - enumeration member names. - - """ - def __init__(self): - super(_EnumDict, self).__init__() - self._member_names = [] - - def __setitem__(self, key, value): - """Changes anything not dundered or not a descriptor. - - If a descriptor is added with the same name as an enum member, the name - is removed from _member_names (this may leave a hole in the numerical - sequence of values). - - If an enum member name is used twice, an error is raised; duplicate - values are not checked for. - - Single underscore (sunder) names are reserved. - - Note: in 3.x __order__ is simply discarded as a not necessary piece - leftover from 2.x - - """ - if pyver >= 3.0 and key in ('_order_', '__order__'): - return - elif key == '__order__': - key = '_order_' - if _is_sunder(key): - if key != '_order_': - raise ValueError('_names_ are reserved for future Enum use') - elif _is_dunder(key): - pass - elif key in self._member_names: - # descriptor overwriting an enum? - raise TypeError('Attempted to reuse key: %r' % key) - elif not _is_descriptor(value): - if key in self: - # enum overwriting a descriptor? - raise TypeError('Key already defined as: %r' % self[key]) - self._member_names.append(key) - super(_EnumDict, self).__setitem__(key, value) - - -# Dummy value for Enum as EnumMeta explicity checks for it, but of course until -# EnumMeta finishes running the first time the Enum class doesn't exist. This -# is also why there are checks in EnumMeta like `if Enum is not None` -Enum = None - - -class EnumMeta(type): - """Metaclass for Enum""" - @classmethod - def __prepare__(metacls, cls, bases): - return _EnumDict() - - def __new__(metacls, cls, bases, classdict): - # an Enum class is final once enumeration items have been defined; it - # cannot be mixed with other types (int, float, etc.) if it has an - # inherited __new__ unless a new __new__ is defined (or the resulting - # class will fail). - if type(classdict) is dict: - original_dict = classdict - classdict = _EnumDict() - for k, v in original_dict.items(): - classdict[k] = v - - member_type, first_enum = metacls._get_mixins_(bases) - __new__, save_new, use_args = metacls._find_new_(classdict, member_type, - first_enum) - # save enum items into separate mapping so they don't get baked into - # the new class - members = dict((k, classdict[k]) for k in classdict._member_names) - for name in classdict._member_names: - del classdict[name] - - # py2 support for definition order - _order_ = classdict.get('_order_') - if _order_ is None: - if pyver < 3.0: - try: - _order_ = [name for (name, value) in sorted(members.items(), key=lambda item: item[1])] - except TypeError: - _order_ = [name for name in sorted(members.keys())] - else: - _order_ = classdict._member_names - else: - del classdict['_order_'] - if pyver < 3.0: - _order_ = _order_.replace(',', ' ').split() - aliases = [name for name in members if name not in _order_] - _order_ += aliases - - # check for illegal enum names (any others?) - invalid_names = set(members) & set(['mro']) - if invalid_names: - raise ValueError('Invalid enum member name(s): %s' % ( - ', '.join(invalid_names), )) - - # save attributes from super classes so we know if we can take - # the shortcut of storing members in the class dict - base_attributes = set([a for b in bases for a in b.__dict__]) - # create our new Enum type - enum_class = super(EnumMeta, metacls).__new__(metacls, cls, bases, classdict) - enum_class._member_names_ = [] # names in random order - if OrderedDict is not None: - enum_class._member_map_ = OrderedDict() - else: - enum_class._member_map_ = {} # name->value map - enum_class._member_type_ = member_type - - # Reverse value->name map for hashable values. - enum_class._value2member_map_ = {} - - # instantiate them, checking for duplicates as we go - # we instantiate first instead of checking for duplicates first in case - # a custom __new__ is doing something funky with the values -- such as - # auto-numbering ;) - if __new__ is None: - __new__ = enum_class.__new__ - for member_name in _order_: - value = members[member_name] - if not isinstance(value, tuple): - args = (value, ) - else: - args = value - if member_type is tuple: # special case for tuple enums - args = (args, ) # wrap it one more time - if not use_args or not args: - enum_member = __new__(enum_class) - if not hasattr(enum_member, '_value_'): - enum_member._value_ = value - else: - enum_member = __new__(enum_class, *args) - if not hasattr(enum_member, '_value_'): - enum_member._value_ = member_type(*args) - value = enum_member._value_ - enum_member._name_ = member_name - enum_member.__objclass__ = enum_class - enum_member.__init__(*args) - # If another member with the same value was already defined, the - # new member becomes an alias to the existing one. - for name, canonical_member in enum_class._member_map_.items(): - if canonical_member.value == enum_member._value_: - enum_member = canonical_member - break - else: - # Aliases don't appear in member names (only in __members__). - enum_class._member_names_.append(member_name) - # performance boost for any member that would not shadow - # a DynamicClassAttribute (aka _RouteClassAttributeToGetattr) - if member_name not in base_attributes: - setattr(enum_class, member_name, enum_member) - # now add to _member_map_ - enum_class._member_map_[member_name] = enum_member - try: - # This may fail if value is not hashable. We can't add the value - # to the map, and by-value lookups for this value will be - # linear. - enum_class._value2member_map_[value] = enum_member - except TypeError: - pass - - - # If a custom type is mixed into the Enum, and it does not know how - # to pickle itself, pickle.dumps will succeed but pickle.loads will - # fail. Rather than have the error show up later and possibly far - # from the source, sabotage the pickle protocol for this class so - # that pickle.dumps also fails. - # - # However, if the new class implements its own __reduce_ex__, do not - # sabotage -- it's on them to make sure it works correctly. We use - # __reduce_ex__ instead of any of the others as it is preferred by - # pickle over __reduce__, and it handles all pickle protocols. - unpicklable = False - if '__reduce_ex__' not in classdict: - if member_type is not object: - methods = ('__getnewargs_ex__', '__getnewargs__', - '__reduce_ex__', '__reduce__') - if not any(m in member_type.__dict__ for m in methods): - _make_class_unpicklable(enum_class) - unpicklable = True - - - # double check that repr and friends are not the mixin's or various - # things break (such as pickle) - for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'): - class_method = getattr(enum_class, name) - obj_method = getattr(member_type, name, None) - enum_method = getattr(first_enum, name, None) - if name not in classdict and class_method is not enum_method: - if name == '__reduce_ex__' and unpicklable: - continue - setattr(enum_class, name, enum_method) - - # method resolution and int's are not playing nice - # Python's less than 2.6 use __cmp__ - - if pyver < 2.6: - - if issubclass(enum_class, int): - setattr(enum_class, '__cmp__', getattr(int, '__cmp__')) - - elif pyver < 3.0: - - if issubclass(enum_class, int): - for method in ( - '__le__', - '__lt__', - '__gt__', - '__ge__', - '__eq__', - '__ne__', - '__hash__', - ): - setattr(enum_class, method, getattr(int, method)) - - # replace any other __new__ with our own (as long as Enum is not None, - # anyway) -- again, this is to support pickle - if Enum is not None: - # if the user defined their own __new__, save it before it gets - # clobbered in case they subclass later - if save_new: - setattr(enum_class, '__member_new__', enum_class.__dict__['__new__']) - setattr(enum_class, '__new__', Enum.__dict__['__new__']) - return enum_class - - def __bool__(cls): - """ - classes/types should always be True. - """ - return True - - def __call__(cls, value, names=None, module=None, type=None, start=1): - """Either returns an existing member, or creates a new enum class. - - This method is used both when an enum class is given a value to match - to an enumeration member (i.e. Color(3)) and for the functional API - (i.e. Color = Enum('Color', names='red green blue')). - - When used for the functional API: `module`, if set, will be stored in - the new class' __module__ attribute; `type`, if set, will be mixed in - as the first base class. - - Note: if `module` is not set this routine will attempt to discover the - calling module by walking the frame stack; if this is unsuccessful - the resulting class will not be pickleable. - - """ - if names is None: # simple value lookup - return cls.__new__(cls, value) - # otherwise, functional API: we're creating a new Enum type - return cls._create_(value, names, module=module, type=type, start=start) - - def __contains__(cls, member): - return isinstance(member, cls) and member.name in cls._member_map_ - - def __delattr__(cls, attr): - # nicer error message when someone tries to delete an attribute - # (see issue19025). - if attr in cls._member_map_: - raise AttributeError( - "%s: cannot delete Enum member." % cls.__name__) - super(EnumMeta, cls).__delattr__(attr) - - def __dir__(self): - return (['__class__', '__doc__', '__members__', '__module__'] + - self._member_names_) - - @property - def __members__(cls): - """Returns a mapping of member name->value. - - This mapping lists all enum members, including aliases. Note that this - is a copy of the internal mapping. - - """ - return cls._member_map_.copy() - - def __getattr__(cls, name): - """Return the enum member matching `name` - - We use __getattr__ instead of descriptors or inserting into the enum - class' __dict__ in order to support `name` and `value` being both - properties for enum members (which live in the class' __dict__) and - enum members themselves. - - """ - if _is_dunder(name): - raise AttributeError(name) - try: - return cls._member_map_[name] - except KeyError: - raise AttributeError(name) - - def __getitem__(cls, name): - return cls._member_map_[name] - - def __iter__(cls): - return (cls._member_map_[name] for name in cls._member_names_) - - def __reversed__(cls): - return (cls._member_map_[name] for name in reversed(cls._member_names_)) - - def __len__(cls): - return len(cls._member_names_) - - __nonzero__ = __bool__ - - def __repr__(cls): - return "" % cls.__name__ - - def __setattr__(cls, name, value): - """Block attempts to reassign Enum members. - - A simple assignment to the class namespace only changes one of the - several possible ways to get an Enum member from the Enum class, - resulting in an inconsistent Enumeration. - - """ - member_map = cls.__dict__.get('_member_map_', {}) - if name in member_map: - raise AttributeError('Cannot reassign members.') - super(EnumMeta, cls).__setattr__(name, value) - - def _create_(cls, class_name, names=None, module=None, type=None, start=1): - """Convenience method to create a new Enum class. - - `names` can be: - - * A string containing member names, separated either with spaces or - commas. Values are auto-numbered from 1. - * An iterable of member names. Values are auto-numbered from 1. - * An iterable of (member name, value) pairs. - * A mapping of member name -> value. - - """ - if pyver < 3.0: - # if class_name is unicode, attempt a conversion to ASCII - if isinstance(class_name, unicode): - try: - class_name = class_name.encode('ascii') - except UnicodeEncodeError: - raise TypeError('%r is not representable in ASCII' % class_name) - metacls = cls.__class__ - if type is None: - bases = (cls, ) - else: - bases = (type, cls) - classdict = metacls.__prepare__(class_name, bases) - _order_ = [] - - # special processing needed for names? - if isinstance(names, basestring): - names = names.replace(',', ' ').split() - if isinstance(names, (tuple, list)) and isinstance(names[0], basestring): - names = [(e, i+start) for (i, e) in enumerate(names)] - - # Here, names is either an iterable of (name, value) or a mapping. - item = None # in case names is empty - for item in names: - if isinstance(item, basestring): - member_name, member_value = item, names[item] - else: - member_name, member_value = item - classdict[member_name] = member_value - _order_.append(member_name) - # only set _order_ in classdict if name/value was not from a mapping - if not isinstance(item, basestring): - classdict['_order_'] = ' '.join(_order_) - enum_class = metacls.__new__(metacls, class_name, bases, classdict) - - # TODO: replace the frame hack if a blessed way to know the calling - # module is ever developed - if module is None: - try: - module = _sys._getframe(2).f_globals['__name__'] - except (AttributeError, ValueError): - pass - if module is None: - _make_class_unpicklable(enum_class) - else: - enum_class.__module__ = module - - return enum_class - - @staticmethod - def _get_mixins_(bases): - """Returns the type for creating enum members, and the first inherited - enum class. - - bases: the tuple of bases that was given to __new__ - - """ - if not bases or Enum is None: - return object, Enum - - - # double check that we are not subclassing a class with existing - # enumeration members; while we're at it, see if any other data - # type has been mixed in so we can use the correct __new__ - member_type = first_enum = None - for base in bases: - if (base is not Enum and - issubclass(base, Enum) and - base._member_names_): - raise TypeError("Cannot extend enumerations") - # base is now the last base in bases - if not issubclass(base, Enum): - raise TypeError("new enumerations must be created as " - "`ClassName([mixin_type,] enum_type)`") - - # get correct mix-in type (either mix-in type of Enum subclass, or - # first base if last base is Enum) - if not issubclass(bases[0], Enum): - member_type = bases[0] # first data type - first_enum = bases[-1] # enum type - else: - for base in bases[0].__mro__: - # most common: (IntEnum, int, Enum, object) - # possible: (, , - # , , - # ) - if issubclass(base, Enum): - if first_enum is None: - first_enum = base - else: - if member_type is None: - member_type = base - - return member_type, first_enum - - if pyver < 3.0: - @staticmethod - def _find_new_(classdict, member_type, first_enum): - """Returns the __new__ to be used for creating the enum members. - - classdict: the class dictionary given to __new__ - member_type: the data type whose __new__ will be used by default - first_enum: enumeration to check for an overriding __new__ - - """ - # now find the correct __new__, checking to see of one was defined - # by the user; also check earlier enum classes in case a __new__ was - # saved as __member_new__ - __new__ = classdict.get('__new__', None) - if __new__: - return None, True, True # __new__, save_new, use_args - - N__new__ = getattr(None, '__new__') - O__new__ = getattr(object, '__new__') - if Enum is None: - E__new__ = N__new__ - else: - E__new__ = Enum.__dict__['__new__'] - # check all possibles for __member_new__ before falling back to - # __new__ - for method in ('__member_new__', '__new__'): - for possible in (member_type, first_enum): - try: - target = possible.__dict__[method] - except (AttributeError, KeyError): - target = getattr(possible, method, None) - if target not in [ - None, - N__new__, - O__new__, - E__new__, - ]: - if method == '__member_new__': - classdict['__new__'] = target - return None, False, True - if isinstance(target, staticmethod): - target = target.__get__(member_type) - __new__ = target - break - if __new__ is not None: - break - else: - __new__ = object.__new__ - - # if a non-object.__new__ is used then whatever value/tuple was - # assigned to the enum member name will be passed to __new__ and to the - # new enum member's __init__ - if __new__ is object.__new__: - use_args = False - else: - use_args = True - - return __new__, False, use_args - else: - @staticmethod - def _find_new_(classdict, member_type, first_enum): - """Returns the __new__ to be used for creating the enum members. - - classdict: the class dictionary given to __new__ - member_type: the data type whose __new__ will be used by default - first_enum: enumeration to check for an overriding __new__ - - """ - # now find the correct __new__, checking to see of one was defined - # by the user; also check earlier enum classes in case a __new__ was - # saved as __member_new__ - __new__ = classdict.get('__new__', None) - - # should __new__ be saved as __member_new__ later? - save_new = __new__ is not None - - if __new__ is None: - # check all possibles for __member_new__ before falling back to - # __new__ - for method in ('__member_new__', '__new__'): - for possible in (member_type, first_enum): - target = getattr(possible, method, None) - if target not in ( - None, - None.__new__, - object.__new__, - Enum.__new__, - ): - __new__ = target - break - if __new__ is not None: - break - else: - __new__ = object.__new__ - - # if a non-object.__new__ is used then whatever value/tuple was - # assigned to the enum member name will be passed to __new__ and to the - # new enum member's __init__ - if __new__ is object.__new__: - use_args = False - else: - use_args = True - - return __new__, save_new, use_args - - -######################################################## -# In order to support Python 2 and 3 with a single -# codebase we have to create the Enum methods separately -# and then use the `type(name, bases, dict)` method to -# create the class. -######################################################## -temp_enum_dict = {} -temp_enum_dict['__doc__'] = "Generic enumeration.\n\n Derive from this class to define new enumerations.\n\n" - -def __new__(cls, value): - # all enum instances are actually created during class construction - # without calling this method; this method is called by the metaclass' - # __call__ (i.e. Color(3) ), and by pickle - if type(value) is cls: - # For lookups like Color(Color.red) - value = value.value - #return value - # by-value search for a matching enum member - # see if it's in the reverse mapping (for hashable values) - try: - if value in cls._value2member_map_: - return cls._value2member_map_[value] - except TypeError: - # not there, now do long search -- O(n) behavior - for member in cls._member_map_.values(): - if member.value == value: - return member - raise ValueError("%s is not a valid %s" % (value, cls.__name__)) -temp_enum_dict['__new__'] = __new__ -del __new__ - -def __repr__(self): - return "<%s.%s: %r>" % ( - self.__class__.__name__, self._name_, self._value_) -temp_enum_dict['__repr__'] = __repr__ -del __repr__ - -def __str__(self): - return "%s.%s" % (self.__class__.__name__, self._name_) -temp_enum_dict['__str__'] = __str__ -del __str__ - -if pyver >= 3.0: - def __dir__(self): - added_behavior = [ - m - for cls in self.__class__.mro() - for m in cls.__dict__ - if m[0] != '_' and m not in self._member_map_ - ] - return (['__class__', '__doc__', '__module__', ] + added_behavior) - temp_enum_dict['__dir__'] = __dir__ - del __dir__ - -def __format__(self, format_spec): - # mixed-in Enums should use the mixed-in type's __format__, otherwise - # we can get strange results with the Enum name showing up instead of - # the value - - # pure Enum branch - if self._member_type_ is object: - cls = str - val = str(self) - # mix-in branch - else: - cls = self._member_type_ - val = self.value - return cls.__format__(val, format_spec) -temp_enum_dict['__format__'] = __format__ -del __format__ - - -#################################### -# Python's less than 2.6 use __cmp__ - -if pyver < 2.6: - - def __cmp__(self, other): - if type(other) is self.__class__: - if self is other: - return 0 - return -1 - return NotImplemented - raise TypeError("unorderable types: %s() and %s()" % (self.__class__.__name__, other.__class__.__name__)) - temp_enum_dict['__cmp__'] = __cmp__ - del __cmp__ - -else: - - def __le__(self, other): - raise TypeError("unorderable types: %s() <= %s()" % (self.__class__.__name__, other.__class__.__name__)) - temp_enum_dict['__le__'] = __le__ - del __le__ - - def __lt__(self, other): - raise TypeError("unorderable types: %s() < %s()" % (self.__class__.__name__, other.__class__.__name__)) - temp_enum_dict['__lt__'] = __lt__ - del __lt__ - - def __ge__(self, other): - raise TypeError("unorderable types: %s() >= %s()" % (self.__class__.__name__, other.__class__.__name__)) - temp_enum_dict['__ge__'] = __ge__ - del __ge__ - - def __gt__(self, other): - raise TypeError("unorderable types: %s() > %s()" % (self.__class__.__name__, other.__class__.__name__)) - temp_enum_dict['__gt__'] = __gt__ - del __gt__ - - -def __eq__(self, other): - if type(other) is self.__class__: - return self is other - return NotImplemented -temp_enum_dict['__eq__'] = __eq__ -del __eq__ - -def __ne__(self, other): - if type(other) is self.__class__: - return self is not other - return NotImplemented -temp_enum_dict['__ne__'] = __ne__ -del __ne__ - -def __hash__(self): - return hash(self._name_) -temp_enum_dict['__hash__'] = __hash__ -del __hash__ - -def __reduce_ex__(self, proto): - return self.__class__, (self._value_, ) -temp_enum_dict['__reduce_ex__'] = __reduce_ex__ -del __reduce_ex__ - -# _RouteClassAttributeToGetattr is used to provide access to the `name` -# and `value` properties of enum members while keeping some measure of -# protection from modification, while still allowing for an enumeration -# to have members named `name` and `value`. This works because enumeration -# members are not set directly on the enum class -- __getattr__ is -# used to look them up. - -@_RouteClassAttributeToGetattr -def name(self): - return self._name_ -temp_enum_dict['name'] = name -del name - -@_RouteClassAttributeToGetattr -def value(self): - return self._value_ -temp_enum_dict['value'] = value -del value - -@classmethod -def _convert(cls, name, module, filter, source=None): - """ - Create a new Enum subclass that replaces a collection of global constants - """ - # convert all constants from source (or module) that pass filter() to - # a new Enum called name, and export the enum and its members back to - # module; - # also, replace the __reduce_ex__ method so unpickling works in - # previous Python versions - module_globals = vars(_sys.modules[module]) - if source: - source = vars(source) - else: - source = module_globals - members = dict((name, value) for name, value in source.items() if filter(name)) - cls = cls(name, members, module=module) - cls.__reduce_ex__ = _reduce_ex_by_name - module_globals.update(cls.__members__) - module_globals[name] = cls - return cls -temp_enum_dict['_convert'] = _convert -del _convert - -Enum = EnumMeta('Enum', (object, ), temp_enum_dict) -del temp_enum_dict - -# Enum has now been created -########################### - -class IntEnum(int, Enum): - """Enum where members are also (and must be) ints""" - -def _reduce_ex_by_name(self, proto): - return self.name - -def unique(enumeration): - """Class decorator that ensures only unique members exist in an enumeration.""" - duplicates = [] - for name, member in enumeration.__members__.items(): - if name != member.name: - duplicates.append((name, member.name)) - if duplicates: - duplicate_names = ', '.join( - ["%s -> %s" % (alias, name) for (alias, name) in duplicates] - ) - raise ValueError('duplicate names found in %r: %s' % - (enumeration, duplicate_names) - ) - return enumeration diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/modulefiles/tclogg b/sorc/ens_tracker.v1.1.15.2/tclogg/modulefiles/tclogg deleted file mode 100644 index a8db86a0a6..0000000000 --- a/sorc/ens_tracker.v1.1.15.2/tclogg/modulefiles/tclogg +++ /dev/null @@ -1,7 +0,0 @@ -#%Module - -module-whatis "Sets environment for tclogg tracker" -module load python/3.6.3 -prepend-path PATH /gpfs/hps3/emc/ensemble/save/Jiayi.Peng/ens_tracker.v1.1.15.4/tclogg/bin -prepend-path PYTHONPATH /gpfs/hps3/emc/ensemble/save/Jiayi.Peng/ens_tracker.v1.1.15.4/tclogg -prepend-path PYTHONPATH /usrx/local/prod/python/3.6.3/lib/python3.6 diff --git a/sorc/ens_tracker.v1.1.15.2/README b/sorc/ens_tracker.v1.1.15.4/README similarity index 61% rename from sorc/ens_tracker.v1.1.15.2/README rename to sorc/ens_tracker.v1.1.15.4/README index ae7846d2d3..81085e21ef 100644 --- a/sorc/ens_tracker.v1.1.15.2/README +++ b/sorc/ens_tracker.v1.1.15.4/README @@ -1,10 +1,98 @@ -#--- 06/11/2020 ------- +#--- 03/11/2021 ------- +It is copied from /gpfs/dell2/emc/modeling/save/Kate.Friedman/git/ens_tracker/ens_tracker.v1.1.15.4_4_Kate +Venus: /gpfs/dell2/emc/modeling/noscrub/Jiayi.Peng/ens_tracker.v1.1.15.4/sorc +The update includes: + +(1) All of icc in makefile/makefile_hera/makefile_orion will be updated as "mpiicc" +All of ifort in makefile/makefile_hera/makefile_orion will be updated as "mpiifort" + +*.fd/makefile +*.fd/makefile_hera +*.fd/makefile_orion + +(2)./sorc/Module_ens_tracker.v1.1.15_for_Dell +./sorc/Module_ens_tracker.v1.1.15_for_Hera +./sorc/Module_ens_tracker.v1.1.15_for_Orion + +(3)./sorc/build.sh + +#--- 03/02/2021 ------- +Venus: /gpfs/dell2/emc/modeling/noscrub/Jiayi.Peng/ens_tracker.v1.1.15.4_Kate +Update for hpc-stack module. The changes include: +(1)./sorc/Module_ens_tracker.v1.1.15_for_Dell + +(2)./sorc/build.sh + + + +#--- 08/06/2020 ------- +Mars: /gpfs/dell2/emc/modeling/noscrub/Jiayi.Peng/ens_tracker.v1.1.15.3_4_Kate + +The new FSU-genesis package is added in this package. +The GFS and TC-vital data path has been updated: +export COMPONENT=${COMPONENT:-atmos} +gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}/${COMPONENT}} ; +synvitdir=${COMINgfs:?}/${cyc}/${COMPONENT} + +(1) FSU-genesis +The source python scripts: ./ush/FSUgenesisPY +Please read: README_4_tclogg for compile and run +./control/dell/jfsu_tc_genesis_00.ecf_dell +./jobs/JFSU_TC_GENESIS_JP +./scripts/exgfs_fsu_genesis.sh + export gfsdir=/gpfs/dell1/nco/ops/com/gfs/prod +./ush/data_check_gfs_180hr.sh + export COMPONENT=${COMPONENT:-atmos} .... missing + datdir=${gfsdir}/gfs.${PDY}/${cyc}/${COMPONENT} +./ush/extrkr_fsu.sh + export COMPONENT=${COMPONENT:-atmos} + export file_name=${gfsdir}/gfs.{date:%Y%m%d}/{date:%H}/${COMPONENT}/gfs.t{date:%H}z.pgrb2.0p25.f{fhr:03} + +(2) GFS-track +./control/dell/jfv3_cyclone_track_00.ecf_dell +./jobs/JFV3_CYCLONE_TRACK_JP +./scripts/exgfs_tc_track.sh + export COMPONENT=${COMPONENT:-atmos} + export gfsdir=${COMINgfs}/${cyc}/${COMPONENT} +./ush/data_check_gfs.sh +./ush/extrkr_gfs.sh + gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}/${COMPONENT}} ; + synvitdir=${COMINgfs:?}/${cyc}/${COMPONENT} + synvitold_dir=${synvitdir%.*}.${old_4ymd}/${old_hh}/${COMPONENT} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd}/${future_hh}/${COMPONENT} + +(3) GDAS-track +./control/dell/jfv3gdas_cyclone_track_00.ecf_dell +./jobs/JFV3GDAS_CYCLONE_TRACK_JP +./scripts/exgdas_tc_track.sh + export COMPONENT=${COMPONENT:-atmos} + export gdasdir=${COMINgdas}/${cyc}/${COMPONENT} +./ush/data_check_gdas.sh +./ush/extrkr_gfs.sh + gdasdir=${gdasdir:-${COMINgdas:?}/${cyc}/${COMPONENT}} ; + +(4) GFS-genesis +./control/dell/jfv3_cyclone_genesis_00.ecf_dell +./jobs/JFV3_CYCLONE_GENESIS_JP +./scripts/exgfs_tc_genesis.sh + export COMPONENT=${COMPONENT:-atmos} + export gfsdir=${COMINgfs}/${cyc}/${COMPONENT} +./ush/data_check_gfs.sh +./ush/extrkr_tcv_gfs.sh + gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}/${COMPONENT}} ; + synvitdir=${COMINgfs:?}/${cyc}/${COMPONENT} + synvitold_dir=${synvitdir%.*}.${old_4ymd}/${old_hh}/${COMPONENT} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd}/${future_hh}/${COMPONENT} +./ush/extrkr_gen_gfs.sh + gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}/${COMPONENT}} ; + synvitdir=${COMINgfs:?}/${cyc}/${COMPONENT} + synvitold_dir=${synvitdir%.*}.${old_4ymd}/${old_hh}/${COMPONENT} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd}/${future_hh}/${COMPONENT} +# +#--- 06/11/2020 -------# This version is copied from HERA: /scratch2/NCEPDEV/ensemble/Jiayi.Peng/noscrub/ens_tracker.v1.1.15.2 It is tested for JET. - - - #--- 06/08/2020 ------- The same as: https://github.com/NOAA-EMC/TC_tracker/tree/TC_tracker.v1.1.15.2 diff --git a/sorc/ens_tracker.v1.1.15.2/RELEASE_NOTE_ens_tracker_v1.1 b/sorc/ens_tracker.v1.1.15.4/RELEASE_NOTE_ens_tracker_v1.1 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/RELEASE_NOTE_ens_tracker_v1.1 rename to sorc/ens_tracker.v1.1.15.4/RELEASE_NOTE_ens_tracker_v1.1 diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jcens_genesis_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jcens_genesis_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jcens_genesis_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jcens_genesis_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jcens_track_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jcens_track_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jcens_track_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jcens_track_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jcmc_genesis_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jcmc_genesis_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jcmc_genesis_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jcmc_genesis_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jcmc_track_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jcmc_track_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jcmc_track_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jcmc_track_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jecmwf_genesis_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jecmwf_genesis_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jecmwf_genesis_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jecmwf_genesis_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jecmwf_track_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jecmwf_track_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jecmwf_track_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jecmwf_track_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jeens_genesis_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jeens_genesis_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jeens_genesis_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jeens_genesis_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jeens_grib_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jeens_grib_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jeens_grib_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jeens_grib_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jeens_track_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jeens_track_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jeens_track_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jeens_track_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jfens_genesis_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jfens_genesis_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jfens_genesis_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jfens_genesis_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jfens_track_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jfens_track_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jfens_track_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jfens_track_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jfsu_tc_genesis_00.ecf_cray b/sorc/ens_tracker.v1.1.15.4/control/cray/jfsu_tc_genesis_00.ecf_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jfsu_tc_genesis_00.ecf_cray rename to sorc/ens_tracker.v1.1.15.4/control/cray/jfsu_tc_genesis_00.ecf_cray diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jfv3_cyclone_genesis_00.ecf_cray b/sorc/ens_tracker.v1.1.15.4/control/cray/jfv3_cyclone_genesis_00.ecf_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jfv3_cyclone_genesis_00.ecf_cray rename to sorc/ens_tracker.v1.1.15.4/control/cray/jfv3_cyclone_genesis_00.ecf_cray diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jfv3_cyclone_track_00.ecf_cray b/sorc/ens_tracker.v1.1.15.4/control/cray/jfv3_cyclone_track_00.ecf_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jfv3_cyclone_track_00.ecf_cray rename to sorc/ens_tracker.v1.1.15.4/control/cray/jfv3_cyclone_track_00.ecf_cray diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jfv3gdas_cyclone_track_00.ecf_cray b/sorc/ens_tracker.v1.1.15.4/control/cray/jfv3gdas_cyclone_track_00.ecf_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jfv3gdas_cyclone_track_00.ecf_cray rename to sorc/ens_tracker.v1.1.15.4/control/cray/jfv3gdas_cyclone_track_00.ecf_cray diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jgefs_genesis_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jgefs_genesis_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jgefs_genesis_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jgefs_genesis_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jgefs_track_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jgefs_track_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jgefs_track_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jgefs_track_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jgfs_genesis_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jgfs_genesis_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jgfs_genesis_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jgfs_genesis_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jgfs_track_avno_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jgfs_track_avno_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jgfs_track_avno_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jgfs_track_avno_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jgfs_track_avnx_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jgfs_track_avnx_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jgfs_track_avnx_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jgfs_track_avnx_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jnavgem_genesis_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jnavgem_genesis_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jnavgem_genesis_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jnavgem_genesis_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jnavgem_track_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jnavgem_track_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jnavgem_track_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jnavgem_track_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jukmet_genesis_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jukmet_genesis_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jukmet_genesis_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jukmet_genesis_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jukmet_grib_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jukmet_grib_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jukmet_grib_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jukmet_grib_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/cray/jukmet_track_00.ecf b/sorc/ens_tracker.v1.1.15.4/control/cray/jukmet_track_00.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/cray/jukmet_track_00.ecf rename to sorc/ens_tracker.v1.1.15.4/control/cray/jukmet_track_00.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/control/dell/jfsu_tc_genesis_00.ecf_dell b/sorc/ens_tracker.v1.1.15.4/control/dell/jfsu_tc_genesis_00.ecf_dell similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/control/dell/jfsu_tc_genesis_00.ecf_dell rename to sorc/ens_tracker.v1.1.15.4/control/dell/jfsu_tc_genesis_00.ecf_dell index 269b804d57..6bb3c04611 100755 --- a/sorc/ens_tracker.v1.1.15.2/control/dell/jfsu_tc_genesis_00.ecf_dell +++ b/sorc/ens_tracker.v1.1.15.4/control/dell/jfsu_tc_genesis_00.ecf_dell @@ -42,7 +42,7 @@ else exit fi -module use ${NWROOT}/ens_tracker.${ens_tracker_ver}/tclogg/modulefiles +module use ${NWROOT}/ens_tracker.${ens_tracker_ver}/ush/FSUgenesisPY/modulefiles module load tclogg # CALL executable job script here diff --git a/sorc/ens_tracker.v1.1.15.2/control/dell/jfv3_cyclone_genesis_00.ecf_dell b/sorc/ens_tracker.v1.1.15.4/control/dell/jfv3_cyclone_genesis_00.ecf_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/dell/jfv3_cyclone_genesis_00.ecf_dell rename to sorc/ens_tracker.v1.1.15.4/control/dell/jfv3_cyclone_genesis_00.ecf_dell diff --git a/sorc/ens_tracker.v1.1.15.2/control/dell/jfv3_cyclone_track_00.ecf_dell b/sorc/ens_tracker.v1.1.15.4/control/dell/jfv3_cyclone_track_00.ecf_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/dell/jfv3_cyclone_track_00.ecf_dell rename to sorc/ens_tracker.v1.1.15.4/control/dell/jfv3_cyclone_track_00.ecf_dell diff --git a/sorc/ens_tracker.v1.1.15.2/control/dell/jfv3gdas_cyclone_track_00.ecf_dell b/sorc/ens_tracker.v1.1.15.4/control/dell/jfv3gdas_cyclone_track_00.ecf_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/dell/jfv3gdas_cyclone_track_00.ecf_dell rename to sorc/ens_tracker.v1.1.15.4/control/dell/jfv3gdas_cyclone_track_00.ecf_dell diff --git a/sorc/ens_tracker.v1.1.15.2/control/hera/jfv3_cyclone_genesis_00.ecf_hera b/sorc/ens_tracker.v1.1.15.4/control/hera/jfv3_cyclone_genesis_00.ecf_hera similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/hera/jfv3_cyclone_genesis_00.ecf_hera rename to sorc/ens_tracker.v1.1.15.4/control/hera/jfv3_cyclone_genesis_00.ecf_hera diff --git a/sorc/ens_tracker.v1.1.15.2/control/hera/jfv3_cyclone_track_00.ecf_hera b/sorc/ens_tracker.v1.1.15.4/control/hera/jfv3_cyclone_track_00.ecf_hera similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/hera/jfv3_cyclone_track_00.ecf_hera rename to sorc/ens_tracker.v1.1.15.4/control/hera/jfv3_cyclone_track_00.ecf_hera diff --git a/sorc/ens_tracker.v1.1.15.2/control/hera/jfv3gdas_cyclone_track_00.ecf_hera b/sorc/ens_tracker.v1.1.15.4/control/hera/jfv3gdas_cyclone_track_00.ecf_hera similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/hera/jfv3gdas_cyclone_track_00.ecf_hera rename to sorc/ens_tracker.v1.1.15.4/control/hera/jfv3gdas_cyclone_track_00.ecf_hera diff --git a/sorc/ens_tracker.v1.1.15.2/control/jet/jfv3_cyclone_genesis_00.ecf_jet b/sorc/ens_tracker.v1.1.15.4/control/jet/jfv3_cyclone_genesis_00.ecf_jet similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/jet/jfv3_cyclone_genesis_00.ecf_jet rename to sorc/ens_tracker.v1.1.15.4/control/jet/jfv3_cyclone_genesis_00.ecf_jet diff --git a/sorc/ens_tracker.v1.1.15.2/control/jet/jfv3_cyclone_track_00.ecf_jet b/sorc/ens_tracker.v1.1.15.4/control/jet/jfv3_cyclone_track_00.ecf_jet similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/jet/jfv3_cyclone_track_00.ecf_jet rename to sorc/ens_tracker.v1.1.15.4/control/jet/jfv3_cyclone_track_00.ecf_jet diff --git a/sorc/ens_tracker.v1.1.15.2/control/jet/jfv3gdas_cyclone_track_00.ecf_jet b/sorc/ens_tracker.v1.1.15.4/control/jet/jfv3gdas_cyclone_track_00.ecf_jet similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/jet/jfv3gdas_cyclone_track_00.ecf_jet rename to sorc/ens_tracker.v1.1.15.4/control/jet/jfv3gdas_cyclone_track_00.ecf_jet diff --git a/sorc/ens_tracker.v1.1.15.2/control/orion/jfv3_cyclone_genesis_00.ecf_orion b/sorc/ens_tracker.v1.1.15.4/control/orion/jfv3_cyclone_genesis_00.ecf_orion similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/orion/jfv3_cyclone_genesis_00.ecf_orion rename to sorc/ens_tracker.v1.1.15.4/control/orion/jfv3_cyclone_genesis_00.ecf_orion diff --git a/sorc/ens_tracker.v1.1.15.2/control/orion/jfv3_cyclone_track_00.ecf_orion b/sorc/ens_tracker.v1.1.15.4/control/orion/jfv3_cyclone_track_00.ecf_orion similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/orion/jfv3_cyclone_track_00.ecf_orion rename to sorc/ens_tracker.v1.1.15.4/control/orion/jfv3_cyclone_track_00.ecf_orion diff --git a/sorc/ens_tracker.v1.1.15.2/control/orion/jfv3gdas_cyclone_track_00.ecf_orion b/sorc/ens_tracker.v1.1.15.4/control/orion/jfv3gdas_cyclone_track_00.ecf_orion similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/control/orion/jfv3gdas_cyclone_track_00.ecf_orion rename to sorc/ens_tracker.v1.1.15.4/control/orion/jfv3gdas_cyclone_track_00.ecf_orion diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/cmce/jcens_tc_genesis.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/cmce/jcens_tc_genesis.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/cmce/jcens_tc_genesis.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/cmce/jcens_tc_genesis.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/cmce/jcens_tc_track.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/cmce/jcens_tc_track.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/cmce/jcens_tc_track.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/cmce/jcens_tc_track.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/cmce/jcmc_tc_genesis.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/cmce/jcmc_tc_genesis.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/cmce/jcmc_tc_genesis.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/cmce/jcmc_tc_genesis.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/cmce/jcmc_tc_track.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/cmce/jcmc_tc_track.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/cmce/jcmc_tc_track.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/cmce/jcmc_tc_track.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ecme/jecmwf_tc_genesis.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ecme/jecmwf_tc_genesis.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ecme/jecmwf_tc_genesis.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ecme/jecmwf_tc_genesis.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ecme/jecmwf_tc_track.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ecme/jecmwf_tc_track.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ecme/jecmwf_tc_track.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ecme/jecmwf_tc_track.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ecme/jeens_grib.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ecme/jeens_grib.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ecme/jeens_grib.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ecme/jeens_grib.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ecme/jeens_tc_genesis.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ecme/jeens_tc_genesis.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ecme/jeens_tc_genesis.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ecme/jeens_tc_genesis.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ecme/jeens_tc_track.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ecme/jeens_tc_track.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ecme/jeens_tc_track.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ecme/jeens_tc_track.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/fens/jfens_tc_genesis.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/fens/jfens_tc_genesis.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/fens/jfens_tc_genesis.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/fens/jfens_tc_genesis.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/fens/jfens_tc_track.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/fens/jfens_tc_track.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/fens/jfens_tc_track.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/fens/jfens_tc_track.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/fens/jnavgem_tc_genesis.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/fens/jnavgem_tc_genesis.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/fens/jnavgem_tc_genesis.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/fens/jnavgem_tc_genesis.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/fens/jnavgem_tc_track.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/fens/jnavgem_tc_track.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/fens/jnavgem_tc_track.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/fens/jnavgem_tc_track.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/gefs/jgefs_tc_genesis.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gefs/jgefs_tc_genesis.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/gefs/jgefs_tc_genesis.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gefs/jgefs_tc_genesis.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/gefs/jgefs_tc_track.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gefs/jgefs_tc_track.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/gefs/jgefs_tc_track.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gefs/jgefs_tc_track.ecf diff --git a/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gefs/jgefs_tc_track.ecf.old0515 b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gefs/jgefs_tc_track.ecf.old0515 new file mode 100644 index 0000000000..c3e42a8df7 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gefs/jgefs_tc_track.ecf.old0515 @@ -0,0 +1,42 @@ +#BSUB -J %E%gefs_tc_track_%CYC% +#BSUB -o %COM%/output/%ENVIR%/today/gefs_tc_track_%CYC%.o%J +#BSUB -P %PROJ%-%PROJENVIR% +#BSUB -q %QUEUE% +#BSUB -L /bin/sh +#BSUB -W 00:25 +# dev - #BSUB -W 0:45 +#BSUB -cwd /tmp +#BSUB -M 1000 +#BSUB -extsched 'CRAYLINUX[]' + +%include +%include + +export cyc=%CYC% + +model=ens_tracker +%include + +#set -x +module load grib_util/${grib_util_ver:?} +module load PrgEnv-intel/${intel_ver} +module load craype-haswell +module load iobuf/${iobuf_ver} +module load cfp-intel-sandybridge/${cfp_ver} +module list + +# EXPORT list here + +export NODES=3 +export APRUNTRACK="aprun -j1 -n21 -N7 -d1" +export IOBUF_PARAMS="*:size=32M:count=4:verbose" + +${HOMEens_tracker}/jobs/JGEFS_TC_TRACK + +%include + +%manual +###################################################################### +#PURPOSE: Executes the job that creates GFS TC track forecasts +###################################################################### +%end diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/gfs/jgfs_tc_genesis.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gfs/jgfs_tc_genesis.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/gfs/jgfs_tc_genesis.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gfs/jgfs_tc_genesis.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/gfs/jgfs_tc_track.ecf.orig_avnx b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gfs/jgfs_tc_track.ecf.orig_avnx similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/gfs/jgfs_tc_track.ecf.orig_avnx rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gfs/jgfs_tc_track.ecf.orig_avnx diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/gfs/jgfs_tc_track_avno.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gfs/jgfs_tc_track_avno.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/gfs/jgfs_tc_track_avno.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gfs/jgfs_tc_track_avno.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/gfs/jgfs_tc_track_avnx.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gfs/jgfs_tc_track_avnx.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/gfs/jgfs_tc_track_avnx.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/gfs/jgfs_tc_track_avnx.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ukmet/jukmet_grib.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ukmet/jukmet_grib.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ukmet/jukmet_grib.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ukmet/jukmet_grib.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ukmet/jukmet_tc_genesis.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ukmet/jukmet_tc_genesis.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ukmet/jukmet_tc_genesis.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ukmet/jukmet_tc_genesis.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ukmet/jukmet_tc_track.ecf b/sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ukmet/jukmet_tc_track.ecf similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/ens_tracker/ukmet/jukmet_tc_track.ecf rename to sorc/ens_tracker.v1.1.15.4/ecf/ens_tracker/ukmet/jukmet_tc_track.ecf diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/para.def b/sorc/ens_tracker.v1.1.15.4/ecf/para.def similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/para.def rename to sorc/ens_tracker.v1.1.15.4/ecf/para.def diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/prod.def b/sorc/ens_tracker.v1.1.15.4/ecf/prod.def similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/prod.def rename to sorc/ens_tracker.v1.1.15.4/ecf/prod.def diff --git a/sorc/ens_tracker.v1.1.15.2/ecf/test.def b/sorc/ens_tracker.v1.1.15.4/ecf/test.def similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ecf/test.def rename to sorc/ens_tracker.v1.1.15.4/ecf/test.def diff --git a/sorc/ens_tracker.v1.1.15.2/fix/cens.genesis_leadtimes_120 b/sorc/ens_tracker.v1.1.15.4/fix/cens.genesis_leadtimes_120 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/cens.genesis_leadtimes_120 rename to sorc/ens_tracker.v1.1.15.4/fix/cens.genesis_leadtimes_120 diff --git a/sorc/ens_tracker.v1.1.15.2/fix/cens.tracker_leadtimes b/sorc/ens_tracker.v1.1.15.4/fix/cens.tracker_leadtimes similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/cens.tracker_leadtimes rename to sorc/ens_tracker.v1.1.15.4/fix/cens.tracker_leadtimes diff --git a/sorc/ens_tracker.v1.1.15.2/fix/cens_hgt_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/cens_hgt_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/cens_hgt_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/cens_hgt_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/cens_tmp_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/cens_tmp_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/cens_tmp_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/cens_tmp_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/cmc.genesis_leadtimes b/sorc/ens_tracker.v1.1.15.4/fix/cmc.genesis_leadtimes similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/cmc.genesis_leadtimes rename to sorc/ens_tracker.v1.1.15.4/fix/cmc.genesis_leadtimes diff --git a/sorc/ens_tracker.v1.1.15.2/fix/cmc.genesis_leadtimes_120 b/sorc/ens_tracker.v1.1.15.4/fix/cmc.genesis_leadtimes_120 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/cmc.genesis_leadtimes_120 rename to sorc/ens_tracker.v1.1.15.4/fix/cmc.genesis_leadtimes_120 diff --git a/sorc/ens_tracker.v1.1.15.2/fix/cmc.tracker_leadtimes b/sorc/ens_tracker.v1.1.15.4/fix/cmc.tracker_leadtimes similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/cmc.tracker_leadtimes rename to sorc/ens_tracker.v1.1.15.4/fix/cmc.tracker_leadtimes diff --git a/sorc/ens_tracker.v1.1.15.2/fix/cmc_hgt_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/cmc_hgt_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/cmc_hgt_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/cmc_hgt_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/cmc_rh_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/cmc_rh_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/cmc_rh_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/cmc_rh_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/cmc_rh_levs.txt_bak b/sorc/ens_tracker.v1.1.15.4/fix/cmc_rh_levs.txt_bak similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/cmc_rh_levs.txt_bak rename to sorc/ens_tracker.v1.1.15.4/fix/cmc_rh_levs.txt_bak diff --git a/sorc/ens_tracker.v1.1.15.2/fix/cmc_tmp_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/cmc_tmp_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/cmc_tmp_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/cmc_tmp_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ecmwf.genesis_leadtimes b/sorc/ens_tracker.v1.1.15.4/fix/ecmwf.genesis_leadtimes similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ecmwf.genesis_leadtimes rename to sorc/ens_tracker.v1.1.15.4/fix/ecmwf.genesis_leadtimes diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ecmwf.genesis_leadtimes_120 b/sorc/ens_tracker.v1.1.15.4/fix/ecmwf.genesis_leadtimes_120 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ecmwf.genesis_leadtimes_120 rename to sorc/ens_tracker.v1.1.15.4/fix/ecmwf.genesis_leadtimes_120 diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ecmwf_hgt_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/ecmwf_hgt_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ecmwf_hgt_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/ecmwf_hgt_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ecmwf_tmp_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/ecmwf_tmp_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ecmwf_tmp_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/ecmwf_tmp_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/eens.genesis_leadtimes_120 b/sorc/ens_tracker.v1.1.15.4/fix/eens.genesis_leadtimes_120 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/eens.genesis_leadtimes_120 rename to sorc/ens_tracker.v1.1.15.4/fix/eens.genesis_leadtimes_120 diff --git a/sorc/ens_tracker.v1.1.15.2/fix/eens_hgt_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/eens_hgt_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/eens_hgt_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/eens_hgt_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/eens_tmp_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/eens_tmp_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/eens_tmp_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/eens_tmp_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ens.genesis_leadtimes_120 b/sorc/ens_tracker.v1.1.15.4/fix/ens.genesis_leadtimes_120 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ens.genesis_leadtimes_120 rename to sorc/ens_tracker.v1.1.15.4/fix/ens.genesis_leadtimes_120 diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ens.tracker_leadtimes b/sorc/ens_tracker.v1.1.15.4/fix/ens.tracker_leadtimes similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ens.tracker_leadtimes rename to sorc/ens_tracker.v1.1.15.4/fix/ens.tracker_leadtimes diff --git a/sorc/ens_tracker.v1.1.15.2/fix/fens.genesis_leadtimes_120 b/sorc/ens_tracker.v1.1.15.4/fix/fens.genesis_leadtimes_120 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/fens.genesis_leadtimes_120 rename to sorc/ens_tracker.v1.1.15.4/fix/fens.genesis_leadtimes_120 diff --git a/sorc/ens_tracker.v1.1.15.2/fix/fens.tracker_leadtimes b/sorc/ens_tracker.v1.1.15.4/fix/fens.tracker_leadtimes similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/fens.tracker_leadtimes rename to sorc/ens_tracker.v1.1.15.4/fix/fens.tracker_leadtimes diff --git a/sorc/ens_tracker.v1.1.15.2/fix/fens_hgt_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/fens_hgt_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/fens_hgt_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/fens_hgt_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/fens_tmp_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/fens_tmp_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/fens_tmp_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/fens_tmp_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/gdas.tracker_leadtimes_3hr b/sorc/ens_tracker.v1.1.15.4/fix/gdas.tracker_leadtimes_3hr similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/gdas.tracker_leadtimes_3hr rename to sorc/ens_tracker.v1.1.15.4/fix/gdas.tracker_leadtimes_3hr diff --git a/sorc/ens_tracker.v1.1.15.2/fix/gdas_hgt_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/gdas_hgt_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/gdas_hgt_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/gdas_hgt_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/gdas_tmp_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/gdas_tmp_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/gdas_tmp_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/gdas_tmp_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/gfs.genesis_leadtimes b/sorc/ens_tracker.v1.1.15.4/fix/gfs.genesis_leadtimes similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/gfs.genesis_leadtimes rename to sorc/ens_tracker.v1.1.15.4/fix/gfs.genesis_leadtimes diff --git a/sorc/ens_tracker.v1.1.15.2/fix/gfs.genesis_leadtimes_120 b/sorc/ens_tracker.v1.1.15.4/fix/gfs.genesis_leadtimes_120 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/gfs.genesis_leadtimes_120 rename to sorc/ens_tracker.v1.1.15.4/fix/gfs.genesis_leadtimes_120 diff --git a/sorc/ens_tracker.v1.1.15.2/fix/gfs.tracker_leadtimes b/sorc/ens_tracker.v1.1.15.4/fix/gfs.tracker_leadtimes similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/gfs.tracker_leadtimes rename to sorc/ens_tracker.v1.1.15.4/fix/gfs.tracker_leadtimes diff --git a/sorc/ens_tracker.v1.1.15.2/fix/gfs_hgt_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/gfs_hgt_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/gfs_hgt_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/gfs_hgt_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/gfs_tmp_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/gfs_tmp_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/gfs_tmp_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/gfs_tmp_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/nce_hgt_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/nce_hgt_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/nce_hgt_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/nce_hgt_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/nce_tmp_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/nce_tmp_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/nce_tmp_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/nce_tmp_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ngps.genesis_leadtimes b/sorc/ens_tracker.v1.1.15.4/fix/ngps.genesis_leadtimes similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ngps.genesis_leadtimes rename to sorc/ens_tracker.v1.1.15.4/fix/ngps.genesis_leadtimes diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ngps.genesis_leadtimes_120 b/sorc/ens_tracker.v1.1.15.4/fix/ngps.genesis_leadtimes_120 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ngps.genesis_leadtimes_120 rename to sorc/ens_tracker.v1.1.15.4/fix/ngps.genesis_leadtimes_120 diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ngps.tracker_leadtimes b/sorc/ens_tracker.v1.1.15.4/fix/ngps.tracker_leadtimes similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ngps.tracker_leadtimes rename to sorc/ens_tracker.v1.1.15.4/fix/ngps.tracker_leadtimes diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ngps_hgt_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/ngps_hgt_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ngps_hgt_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/ngps_hgt_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ngps_tmp_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/ngps_tmp_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ngps_tmp_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/ngps_tmp_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ukmet.genesis_leadtimes b/sorc/ens_tracker.v1.1.15.4/fix/ukmet.genesis_leadtimes similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ukmet.genesis_leadtimes rename to sorc/ens_tracker.v1.1.15.4/fix/ukmet.genesis_leadtimes diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ukmet.genesis_leadtimes_120 b/sorc/ens_tracker.v1.1.15.4/fix/ukmet.genesis_leadtimes_120 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ukmet.genesis_leadtimes_120 rename to sorc/ens_tracker.v1.1.15.4/fix/ukmet.genesis_leadtimes_120 diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ukmet_hgt_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/ukmet_hgt_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ukmet_hgt_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/ukmet_hgt_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/fix/ukmet_tmp_levs.txt b/sorc/ens_tracker.v1.1.15.4/fix/ukmet_tmp_levs.txt similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/fix/ukmet_tmp_levs.txt rename to sorc/ens_tracker.v1.1.15.4/fix/ukmet_tmp_levs.txt diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JCENS_TC_GENESIS b/sorc/ens_tracker.v1.1.15.4/jobs/JCENS_TC_GENESIS similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JCENS_TC_GENESIS rename to sorc/ens_tracker.v1.1.15.4/jobs/JCENS_TC_GENESIS diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JCENS_TC_TRACK b/sorc/ens_tracker.v1.1.15.4/jobs/JCENS_TC_TRACK similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JCENS_TC_TRACK rename to sorc/ens_tracker.v1.1.15.4/jobs/JCENS_TC_TRACK diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JCMC_TC_GENESIS b/sorc/ens_tracker.v1.1.15.4/jobs/JCMC_TC_GENESIS similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JCMC_TC_GENESIS rename to sorc/ens_tracker.v1.1.15.4/jobs/JCMC_TC_GENESIS diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JCMC_TC_TRACK b/sorc/ens_tracker.v1.1.15.4/jobs/JCMC_TC_TRACK similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JCMC_TC_TRACK rename to sorc/ens_tracker.v1.1.15.4/jobs/JCMC_TC_TRACK diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JECMWF_TC_GENESIS b/sorc/ens_tracker.v1.1.15.4/jobs/JECMWF_TC_GENESIS similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JECMWF_TC_GENESIS rename to sorc/ens_tracker.v1.1.15.4/jobs/JECMWF_TC_GENESIS diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JECMWF_TC_TRACK b/sorc/ens_tracker.v1.1.15.4/jobs/JECMWF_TC_TRACK similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JECMWF_TC_TRACK rename to sorc/ens_tracker.v1.1.15.4/jobs/JECMWF_TC_TRACK diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JEENS_GRIB b/sorc/ens_tracker.v1.1.15.4/jobs/JEENS_GRIB similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JEENS_GRIB rename to sorc/ens_tracker.v1.1.15.4/jobs/JEENS_GRIB diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JEENS_TC_GENESIS b/sorc/ens_tracker.v1.1.15.4/jobs/JEENS_TC_GENESIS similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JEENS_TC_GENESIS rename to sorc/ens_tracker.v1.1.15.4/jobs/JEENS_TC_GENESIS diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JEENS_TC_TRACK b/sorc/ens_tracker.v1.1.15.4/jobs/JEENS_TC_TRACK similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JEENS_TC_TRACK rename to sorc/ens_tracker.v1.1.15.4/jobs/JEENS_TC_TRACK diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JFENS_TC_GENESIS b/sorc/ens_tracker.v1.1.15.4/jobs/JFENS_TC_GENESIS similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JFENS_TC_GENESIS rename to sorc/ens_tracker.v1.1.15.4/jobs/JFENS_TC_GENESIS diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JFENS_TC_TRACK b/sorc/ens_tracker.v1.1.15.4/jobs/JFENS_TC_TRACK similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JFENS_TC_TRACK rename to sorc/ens_tracker.v1.1.15.4/jobs/JFENS_TC_TRACK diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JFSU_TC_GENESIS_JP b/sorc/ens_tracker.v1.1.15.4/jobs/JFSU_TC_GENESIS_JP similarity index 63% rename from sorc/ens_tracker.v1.1.15.2/jobs/JFSU_TC_GENESIS_JP rename to sorc/ens_tracker.v1.1.15.4/jobs/JFSU_TC_GENESIS_JP index 1bc0248b6d..42dea33e7f 100755 --- a/sorc/ens_tracker.v1.1.15.2/jobs/JFSU_TC_GENESIS_JP +++ b/sorc/ens_tracker.v1.1.15.4/jobs/JFSU_TC_GENESIS_JP @@ -46,13 +46,15 @@ export HOMEens_tracker=${HOMEens_tracker:-${NWROOT:?}/ens_tracker.${ens_tracker_ export EXECens_tracker=${EXECens_tracker:-$HOMEens_tracker/exec} export FIXens_tracker=${FIXens_tracker:-$HOMEens_tracker/fix} export USHens_tracker=${USHens_tracker:-$HOMEens_tracker/ush} +export SCRIPTens_tracker=${SCRIPTens_tracker:-$HOMEens_tracker/scripts} +export BINens_tracker=${BINens_tracker:-$HOMEens_tracker/ush/FSUgenesisPY/bin} ############################## # Run setpdy and initialize PDY variables ############################## #setpdy.sh #. PDY -export PDY=20200605 +export PDY=20200805 ############################################## # Define COM directories @@ -66,49 +68,9 @@ postmsg "$jlogfile" "$msg" env ############################################################# -# Execute the script -export cmodel=gfs -export ymdh=${PDY}${cyc} -export gfsdir=/gpfs/dell1/nco/ops/com/gfs/prod - -export pert="p01" -pertdir=${DATA}/${cmodel}/${pert} -mkdir -p $pertdir - -#-----------input data checking ----------------- -#${USHens_tracker}/data_check.sh -#${USHens_tracker}/data_check_gfs.sh -${USHens_tracker}/data_check_gfs_180hr.sh -## exit code 6 = missing data of opportunity -if [ $? -eq 6 ]; then exit; fi -#------------------------------------------------ - -outfile=${pertdir}/fsugenesis.${cmodel}.${pert}.${ymdh}.out - -if [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then - # We are on NOAA Luna or Surge - machine=cray - ${APRUNTRACK} ${USHens_tracker}/extrkr_fsu.sh ${cmodel} ${ymdh} ${pertdir} ${gfsdir} #2>&1 >${outfile} - -elif [[ -L /usrx && "$( readlink /usrx 2> /dev/null )" =~ dell ]] ; then - # We are on NOAA Mars or Venus - machine=dell - ${USHens_tracker}/extrkr_fsu.sh ${cmodel} ${ymdh} ${pertdir} ${gfsdir} #2>&1 >${outfile} - -else - export machine=unknown - echo Job failed: unknown platform 1>&2 - err_exit "FAILED ${jobid} - ERROR IN unknown platform - ABNORMAL EXIT" - -fi +${SCRIPTens_tracker}/exgfs_fsu_genesis.sh export err=$?; err_chk -if [ "$SENDCOM" = 'YES' ]; then - cp -r ${pertdir}/tracker/${cmodel}/* ${COMOUT} -fi - -############################################################# - msg="JOB COMPLETED NORMALLY" postmsg "$jlogfile" "$msg" diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JFV3GDAS_CYCLONE_TRACK_JP b/sorc/ens_tracker.v1.1.15.4/jobs/JFV3GDAS_CYCLONE_TRACK_JP similarity index 65% rename from sorc/ens_tracker.v1.1.15.2/jobs/JFV3GDAS_CYCLONE_TRACK_JP rename to sorc/ens_tracker.v1.1.15.4/jobs/JFV3GDAS_CYCLONE_TRACK_JP index 641be4d605..3291d26bd1 100755 --- a/sorc/ens_tracker.v1.1.15.2/jobs/JFV3GDAS_CYCLONE_TRACK_JP +++ b/sorc/ens_tracker.v1.1.15.4/jobs/JFV3GDAS_CYCLONE_TRACK_JP @@ -46,13 +46,14 @@ export HOMEens_tracker=${HOMEens_tracker:-${NWROOT:?}/ens_tracker.${ens_tracker_ export EXECens_tracker=${EXECens_tracker:-$HOMEens_tracker/exec} export FIXens_tracker=${FIXens_tracker:-$HOMEens_tracker/fix} export USHens_tracker=${USHens_tracker:-$HOMEens_tracker/ush} +export SCRIPTens_tracker=${SCRIPTens_tracker:-$HOMEens_tracker/scripts} ############################## # Run setpdy and initialize PDY variables ############################## #setpdy.sh #. PDY -export PDY=20200612 +export PDY=20200805 ############################################## # Define COM directories @@ -60,23 +61,17 @@ export PDY=20200612 #mac=`echo ${SITE}` #mac2=$(hostname | cut -c1-2) -if [[ -d /dcom ]] ; then - # We are on NOAA Tide or Gyre - export COMINgfs=${COMINgfs:-$(compath.py gfs/prod/gfs.$PDY)} - export COMINgdas=${COMINgdas:-$(compath.py gfs/prod/gdas.$PDY)} - export COMINsyn=${COMINsyn:-$(compath.py arch/prod/syndat)} - -elif [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then +if [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then # We are on NOAA Luna or Surge export COMINgfs=${COMINgfs:-$(compath.py gfs/prod/gfs.$PDY)} export COMINgdas=${COMINgdas:-$(compath.py gfs/prod/gdas.$PDY)} - export COMINsyn=${COMINsyn:-$(compath.py arch/prod/syndat)} + export COMINsyn=${COMINsyn:-$(compath.py gfs/prod/syndat)} elif [[ -L /usrx && "$( readlink /usrx 2> /dev/null )" =~ dell ]] ; then # We are on NOAA Mars or Venus export COMINgfs=${COMINgfs:-$(compath.py gfs/prod/gfs.$PDY)} export COMINgdas=${COMINgdas:-$(compath.py gfs/prod/gdas.$PDY)} - export COMINsyn=${COMINsyn:-$(compath.py arch/prod/syndat)} + export COMINsyn=${COMINsyn:-$(compath.py gfs/prod/syndat)} elif [[ -d /scratch2 ]] ; then # We are on NOAA HERA @@ -122,64 +117,7 @@ postmsg "$jlogfile" "$msg" env ############################################################# -# Execute the script -export cmodel=gdas -export loopnum=1 -export ymdh=${PDY}${cyc} - -export gdasdir=${COMINgdas}/${cyc} -#export gdasdir=${COMINgdas} - -export pert="p01" -pertdir=${DATA}/${cmodel}/${pert} -mkdir -p $pertdir - -#-----------input data checking ----------------- -${USHens_tracker}/data_check_gdas.sh -# exit code 6 = missing data of opportunity -if [ $? -eq 6 ]; then exit; fi -#------------------------------------------------ - -#outfile=${pertdir}/trkr.${cmodel}.${pert}.${ymdh}.out - -if [[ -d /dcom ]] ; then - # We are on NOAA Tide or Gyre - machine=wcoss - ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} - -elif [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then - # We are on NOAA Luna or Surge - machine=cray - ${APRUNTRACK} ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} - -elif [[ -L /usrx && "$( readlink /usrx 2> /dev/null )" =~ dell ]] ; then - # We are on NOAA Mars or Venus - machine=dell - ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} - -elif [[ -d /scratch2 ]] ; then - # We are on NOAA Hera - machine=hera - ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} - -elif [[ -d /work ]] ; then - # We are on MSU Orion - machine=orion - ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} - -elif [[ -d /lfs3 ]] ; then - # We are on NOAA Jet - machine=jet - ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} -else - export machine=unknown - echo Job failed: unknown platform 1>&2 - err_exit "FAILED ${jobid} - ERROR IN unknown platform - ABNORMAL EXIT" - -fi -export err=$?; err_chk - -############################################################# +${SCRIPTens_tracker}/exgdas_tc_track.sh msg="JOB COMPLETED NORMALLY" postmsg "$jlogfile" "$msg" diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JFV3_CYCLONE_GENESIS_JP b/sorc/ens_tracker.v1.1.15.4/jobs/JFV3_CYCLONE_GENESIS_JP similarity index 83% rename from sorc/ens_tracker.v1.1.15.2/jobs/JFV3_CYCLONE_GENESIS_JP rename to sorc/ens_tracker.v1.1.15.4/jobs/JFV3_CYCLONE_GENESIS_JP index 2764cc9055..3ca2692a54 100755 --- a/sorc/ens_tracker.v1.1.15.2/jobs/JFV3_CYCLONE_GENESIS_JP +++ b/sorc/ens_tracker.v1.1.15.4/jobs/JFV3_CYCLONE_GENESIS_JP @@ -53,25 +53,20 @@ export SCRIPTens_tracker=${SCRIPTens_tracker:-$HOMEens_tracker/scripts} ############################## #setpdy.sh #. PDY -export PDY=20200612 +export PDY=20200805 ############################################## # Define COM directories ############################################## -if [[ -d /dcom ]] ; then - # We are on NOAA Tide or Gyre - export COMINgfs=${COMINgfs:-$(compath.py gfs/prod/gfs.$PDY)} - export COMINsyn=${COMINsyn:-$(compath.py arch/prod/syndat)} - -elif [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then +if [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then # We are on NOAA Luna or Surge export COMINgfs=${COMINgfs:-$(compath.py gfs/prod/gfs.$PDY)} - export COMINsyn=${COMINsyn:-$(compath.py arch/prod/syndat)} + export COMINsyn=${COMINsyn:-$(compath.py gfs/prod/syndat)} elif [[ -L /usrx && "$( readlink /usrx 2> /dev/null )" =~ dell ]] ; then # We are on NOAA Mars or Venus export COMINgfs=${COMINgfs:-$(compath.py gfs/prod/gfs.$PDY)} - export COMINsyn=${COMINsyn:-$(compath.py arch/prod/syndat)} + export COMINsyn=${COMINsyn:-$(compath.py gfs/prod/syndat)} elif [[ -d /scratch2 ]] ; then # We are on NOAA Hera @@ -113,19 +108,6 @@ postmsg "$jlogfile" "$msg" env ############################################################# -# Execute the script -export cmodel=gfs -#export loopnum=1 -#export ymdh=${PDY}${cyc} -export gfsdir=${COMINgfs}/${cyc} -#export gfsdir=${COMINgfs} - -#-----------input data checking ----------------- -${USHens_tracker}/data_check_gfs.sh -# exit code 6 = missing data of opportunity -if [ $? -eq 6 ]; then exit; fi -#------------------------------------------------ - ${SCRIPTens_tracker}/exgfs_tc_genesis.sh export err=$?; err_chk diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JFV3_CYCLONE_TRACK_JP b/sorc/ens_tracker.v1.1.15.4/jobs/JFV3_CYCLONE_TRACK_JP similarity index 59% rename from sorc/ens_tracker.v1.1.15.2/jobs/JFV3_CYCLONE_TRACK_JP rename to sorc/ens_tracker.v1.1.15.4/jobs/JFV3_CYCLONE_TRACK_JP index 7e8692c7e5..dcb161bad9 100755 --- a/sorc/ens_tracker.v1.1.15.2/jobs/JFV3_CYCLONE_TRACK_JP +++ b/sorc/ens_tracker.v1.1.15.4/jobs/JFV3_CYCLONE_TRACK_JP @@ -46,12 +46,14 @@ export HOMEens_tracker=${HOMEens_tracker:-${NWROOT:?}/ens_tracker.${ens_tracker_ export EXECens_tracker=${EXECens_tracker:-$HOMEens_tracker/exec} export FIXens_tracker=${FIXens_tracker:-$HOMEens_tracker/fix} export USHens_tracker=${USHens_tracker:-$HOMEens_tracker/ush} +export SCRIPTens_tracker=${SCRIPTens_tracker:-$HOMEens_tracker/scripts} ############################## # Run setpdy and initialize PDY variables ############################## -setpdy.sh -. PDY +#setpdy.sh +#. PDY +export PDY=20200805 ############################################## # Define COM directories @@ -59,20 +61,15 @@ setpdy.sh #mac=`echo ${SITE}` #mac2=$(hostname | cut -c1-2) -if [[ -d /dcom ]] ; then - # We are on NOAA Tide or Gyre - export COMINgfs=${COMINgfs:-$(compath.py gfs/prod/gfs.$PDY)} - export COMINsyn=${COMINsyn:-$(compath.py arch/prod/syndat)} - -elif [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then +if [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then # We are on NOAA Luna or Surge export COMINgfs=${COMINgfs:-$(compath.py gfs/prod/gfs.$PDY)} - export COMINsyn=${COMINsyn:-$(compath.py arch/prod/syndat)} + export COMINsyn=${COMINsyn:-$(compath.py gfs/prod/syndat)} elif [[ -L /usrx && "$( readlink /usrx 2> /dev/null )" =~ dell ]] ; then # We are on NOAA Mars or Venus export COMINgfs=${COMINgfs:-$(compath.py gfs/prod/gfs.$PDY)} - export COMINsyn=${COMINsyn:-$(compath.py arch/prod/syndat)} + export COMINsyn=${COMINsyn:-$(compath.py gfs/prod/syndat)} elif [[ -d /scratch2 ]] ; then # We are on NOAA Hera @@ -89,10 +86,13 @@ elif [[ -d /work ]] ; then # export COMINsyn=/work/noaa/global/kfriedma/glopara/git/tracker/syndat export COMINsyn=/work/noaa/global/Jiayi.Peng/syndat -elif [[ -d /lfs4 ]] ; then +elif [[ -d /lfs3 ]] ; then # We are on NOAA Jet - export COMINgfs=${COMINgfs:-${COMROOTp1}/gfs/prod/gfs.${PDY}} +# export COMINgfs=${COMINgfs:-${COMROOTp1}/gfs/prod/gfs.${PDY}} +# export COMINsyn=${COMINsyn:-${COMROOTp1}/arch/prod/syndat} +# export COMINgfs=${COMINgfs:-/mnt/lfs3/projects/hwrfv3/Jiayi.Peng/data/gfs.${PDY}} export COMINsyn=${COMINsyn:-/mnt/lfs4/HFIP/hwrf-data/hwrf-input/SYNDAT} + export COMINgfs=${COMINgfs:-/mnt/lfs4/HFIP/hwrfv3/Jiayi.Peng/data/gfs.20200612} else echo Job failed: unknown platform 1>&2 err_exit "FAILED ${jobid} - ERROR IN unknown platform - ABNORMAL EXIT" @@ -110,73 +110,9 @@ postmsg "$jlogfile" "$msg" env ############################################################# -# Execute the script -export cmodel=gfs -export loopnum=1 -export ymdh=${PDY}${cyc} - -export gfsdir=${COMINgfs}/${cyc} -#export gfsdir=${COMINgfs} - -export pert="p01" -pertdir=${DATA}/${cmodel}/${pert} -mkdir -p $pertdir - -#-----------input data checking ----------------- -#${USHens_tracker}/data_check.sh -${USHens_tracker}/data_check_gfs.sh -## exit code 6 = missing data of opportunity -if [ $? -eq 6 ]; then exit; fi -#------------------------------------------------ - -outfile=${pertdir}/trkr.${cmodel}.${pert}.${ymdh}.out - -if [[ -d /dcom ]] ; then - # We are on NOAA Tide or Gyre - machine=wcoss - ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} - -elif [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then - # We are on NOAA Luna or Surge - machine=cray - ${APRUNTRACK} ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} - -elif [[ -L /usrx && "$( readlink /usrx 2> /dev/null )" =~ dell ]] ; then - # We are on NOAA Mars or Venus - machine=dell - ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} - -elif [[ -d /scratch2 ]] ; then - # We are on NOAA Hera - machine=hera - ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} - -elif [[ -d /work ]] ; then - # We are on MSU Orion - machine=orion - ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} - -elif [[ -d /lfs3 ]] ; then - # We are on NOAA Jet - machine=jet - ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} - -else - export machine=unknown - echo Job failed: unknown platform 1>&2 - err_exit "FAILED ${jobid} - ERROR IN unknown platform - ABNORMAL EXIT" - -fi +${SCRIPTens_tracker}/exgfs_tc_track.sh export err=$?; err_chk -#if [ "$SENDCOM" = 'YES' ]; then -# cat ${pertdir}/trak.gfso.atcfunix.${PDY}${cyc} | \ -# sed s:GFSO:AVNO:g \ -# > ${COMOUT}/avn.t${cyc}z.cyclone.trackatcfunix -#fi - -############################################################# - msg="JOB COMPLETED NORMALLY" postmsg "$jlogfile" "$msg" diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JGEFS_TC_GENESIS b/sorc/ens_tracker.v1.1.15.4/jobs/JGEFS_TC_GENESIS similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JGEFS_TC_GENESIS rename to sorc/ens_tracker.v1.1.15.4/jobs/JGEFS_TC_GENESIS diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JGEFS_TC_TRACK b/sorc/ens_tracker.v1.1.15.4/jobs/JGEFS_TC_TRACK similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JGEFS_TC_TRACK rename to sorc/ens_tracker.v1.1.15.4/jobs/JGEFS_TC_TRACK diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JGFS_TC_GENESIS b/sorc/ens_tracker.v1.1.15.4/jobs/JGFS_TC_GENESIS similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JGFS_TC_GENESIS rename to sorc/ens_tracker.v1.1.15.4/jobs/JGFS_TC_GENESIS diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JGFS_TC_TRACK_AVNO b/sorc/ens_tracker.v1.1.15.4/jobs/JGFS_TC_TRACK_AVNO similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JGFS_TC_TRACK_AVNO rename to sorc/ens_tracker.v1.1.15.4/jobs/JGFS_TC_TRACK_AVNO diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JGFS_TC_TRACK_AVNX b/sorc/ens_tracker.v1.1.15.4/jobs/JGFS_TC_TRACK_AVNX similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JGFS_TC_TRACK_AVNX rename to sorc/ens_tracker.v1.1.15.4/jobs/JGFS_TC_TRACK_AVNX diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JNAVGEM_TC_GENESIS b/sorc/ens_tracker.v1.1.15.4/jobs/JNAVGEM_TC_GENESIS similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JNAVGEM_TC_GENESIS rename to sorc/ens_tracker.v1.1.15.4/jobs/JNAVGEM_TC_GENESIS diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JNAVGEM_TC_TRACK b/sorc/ens_tracker.v1.1.15.4/jobs/JNAVGEM_TC_TRACK similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JNAVGEM_TC_TRACK rename to sorc/ens_tracker.v1.1.15.4/jobs/JNAVGEM_TC_TRACK diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JUKMET_GRIB b/sorc/ens_tracker.v1.1.15.4/jobs/JUKMET_GRIB similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JUKMET_GRIB rename to sorc/ens_tracker.v1.1.15.4/jobs/JUKMET_GRIB diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JUKMET_TC_GENESIS b/sorc/ens_tracker.v1.1.15.4/jobs/JUKMET_TC_GENESIS similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JUKMET_TC_GENESIS rename to sorc/ens_tracker.v1.1.15.4/jobs/JUKMET_TC_GENESIS diff --git a/sorc/ens_tracker.v1.1.15.2/jobs/JUKMET_TC_TRACK b/sorc/ens_tracker.v1.1.15.4/jobs/JUKMET_TC_TRACK similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/jobs/JUKMET_TC_TRACK rename to sorc/ens_tracker.v1.1.15.4/jobs/JUKMET_TC_TRACK diff --git a/sorc/ens_tracker.v1.1.15.2/parm/transfer_ens_tracker.list b/sorc/ens_tracker.v1.1.15.4/parm/transfer_ens_tracker.list similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/parm/transfer_ens_tracker.list rename to sorc/ens_tracker.v1.1.15.4/parm/transfer_ens_tracker.list diff --git a/sorc/ens_tracker.v1.1.15.2/scripts/excens_tc_genesis.sh b/sorc/ens_tracker.v1.1.15.4/scripts/excens_tc_genesis.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/scripts/excens_tc_genesis.sh rename to sorc/ens_tracker.v1.1.15.4/scripts/excens_tc_genesis.sh diff --git a/sorc/ens_tracker.v1.1.15.2/scripts/excmc_tc_genesis.sh b/sorc/ens_tracker.v1.1.15.4/scripts/excmc_tc_genesis.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/scripts/excmc_tc_genesis.sh rename to sorc/ens_tracker.v1.1.15.4/scripts/excmc_tc_genesis.sh diff --git a/sorc/ens_tracker.v1.1.15.2/scripts/execmwf_tc_genesis.sh b/sorc/ens_tracker.v1.1.15.4/scripts/execmwf_tc_genesis.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/scripts/execmwf_tc_genesis.sh rename to sorc/ens_tracker.v1.1.15.4/scripts/execmwf_tc_genesis.sh diff --git a/sorc/ens_tracker.v1.1.15.2/scripts/exeens_tc_genesis.sh b/sorc/ens_tracker.v1.1.15.4/scripts/exeens_tc_genesis.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/scripts/exeens_tc_genesis.sh rename to sorc/ens_tracker.v1.1.15.4/scripts/exeens_tc_genesis.sh diff --git a/sorc/ens_tracker.v1.1.15.2/scripts/exfens_tc_genesis.sh b/sorc/ens_tracker.v1.1.15.4/scripts/exfens_tc_genesis.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/scripts/exfens_tc_genesis.sh rename to sorc/ens_tracker.v1.1.15.4/scripts/exfens_tc_genesis.sh diff --git a/sorc/ens_tracker.v1.1.15.4/scripts/exgdas_tc_track.sh b/sorc/ens_tracker.v1.1.15.4/scripts/exgdas_tc_track.sh new file mode 100755 index 0000000000..73f32e8665 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/scripts/exgdas_tc_track.sh @@ -0,0 +1,55 @@ +#!/bin/ksh +set -x + +export cmodel=gdas +export loopnum=1 +export ymdh=${PDY}${cyc} + +#export gdasdir=${COMINgdas}/${cyc} +#export gdasdir=${COMINgdas} +export COMPONENT=${COMPONENT:-atmos} +export gdasdir=${COMINgdas}/${cyc}/${COMPONENT} + +export pert="p01" +pertdir=${DATA}/${cmodel}/${pert} +mkdir -p $pertdir + +#-----------input data checking ----------------- +${USHens_tracker}/data_check_gdas.sh +# exit code 6 = missing data of opportunity +if [ $? -eq 6 ]; then exit; fi +#------------------------------------------------ + +#outfile=${pertdir}/trkr.${cmodel}.${pert}.${ymdh}.out + +if [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then + # We are on NOAA Luna or Surge + machine=cray + ${APRUNTRACK} ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} + +elif [[ -L /usrx && "$( readlink /usrx 2> /dev/null )" =~ dell ]] ; then + # We are on NOAA Mars or Venus + machine=dell + ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} + +elif [[ -d /scratch2 ]] ; then + # We are on NOAA Hera + machine=hera + ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} + +elif [[ -d /work ]] ; then + # We are on MSU Orion + machine=orion + ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} + +elif [[ -d /lfs3 ]] ; then + # We are on NOAA Jet + machine=jet + ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} +else + export machine=unknown + echo Job failed: unknown platform 1>&2 + err_exit "FAILED ${jobid} - ERROR IN unknown platform - ABNORMAL EXIT" + +fi +export err=$?; err_chk diff --git a/sorc/ens_tracker.v1.1.15.2/scripts/exgefs_tc_genesis.sh b/sorc/ens_tracker.v1.1.15.4/scripts/exgefs_tc_genesis.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/scripts/exgefs_tc_genesis.sh rename to sorc/ens_tracker.v1.1.15.4/scripts/exgefs_tc_genesis.sh diff --git a/sorc/ens_tracker.v1.1.15.4/scripts/exgfs_fsu_genesis.sh b/sorc/ens_tracker.v1.1.15.4/scripts/exgfs_fsu_genesis.sh new file mode 100755 index 0000000000..9c34827b22 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/scripts/exgfs_fsu_genesis.sh @@ -0,0 +1,43 @@ +#!/bin/ksh +set -x + +export cmodel=gfs +export ymdh=${PDY}${cyc} +#export gfsdir=/gpfs/dell1/nco/ops/com/gfs/prod +export gfsdir=${gfsdir:-/gpfs/dell1/nco/ops/com/gfs/prod} + +export pert="p01" +pertdir=${DATA}/${cmodel}/${pert} +mkdir -p $pertdir + +#-----------input data checking ----------------- +#${USHens_tracker}/data_check.sh +#${USHens_tracker}/data_check_gfs.sh +${USHens_tracker}/data_check_gfs_180hr.sh +## exit code 6 = missing data of opportunity +if [ $? -eq 6 ]; then exit; fi +#------------------------------------------------ + +outfile=${pertdir}/fsugenesis.${cmodel}.${pert}.${ymdh}.out + +if [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then + # We are on NOAA Luna or Surge + machine=cray + ${APRUNTRACK} ${USHens_tracker}/extrkr_fsu.sh ${cmodel} ${ymdh} ${pertdir} ${gfsdir} #2>&1 >${outfile} + +elif [[ -L /usrx && "$( readlink /usrx 2> /dev/null )" =~ dell ]] ; then + # We are on NOAA Mars or Venus + machine=dell + ${USHens_tracker}/extrkr_fsu.sh ${cmodel} ${ymdh} ${pertdir} ${gfsdir} #2>&1 >${outfile} + +else + export machine=unknown + echo Job failed: unknown platform 1>&2 + err_exit "FAILED ${jobid} - ERROR IN unknown platform - ABNORMAL EXIT" + +fi +export err=$?; err_chk + +if [ "$SENDCOM" = 'YES' ]; then + cp -r ${pertdir}/tracker/${cmodel}/* ${COMOUT} +fi diff --git a/sorc/ens_tracker.v1.1.15.2/scripts/exgfs_tc_genesis.sh b/sorc/ens_tracker.v1.1.15.4/scripts/exgfs_tc_genesis.sh similarity index 87% rename from sorc/ens_tracker.v1.1.15.2/scripts/exgfs_tc_genesis.sh rename to sorc/ens_tracker.v1.1.15.4/scripts/exgfs_tc_genesis.sh index a9433903ca..cbbce5fc88 100755 --- a/sorc/ens_tracker.v1.1.15.2/scripts/exgfs_tc_genesis.sh +++ b/sorc/ens_tracker.v1.1.15.4/scripts/exgfs_tc_genesis.sh @@ -1,6 +1,22 @@ #!/bin/ksh set -x +export cmodel=gfs +#export loopnum=1 +#export ymdh=${PDY}${cyc} +#export gfsdir=${COMINgfs}/${cyc} +#export gfsdir=${COMINgfs} +export COMPONENT=${COMPONENT:-atmos} +#export gfsdir=${COMINgfs}/${cyc}/${COMPONENT} +export gfsdir=${gfsdir:-${COMINgfs}/${cyc}/${COMPONENT}} + +#-----------input data checking ----------------- +${USHens_tracker}/data_check_gfs.sh +# exit code 6 = missing data of opportunity +if [ $? -eq 6 ]; then exit; fi +#------------------------------------------------ + + #export cmodel=gfs export loopnum=1 export ymdh=${PDY}${cyc} @@ -24,12 +40,7 @@ mkdir -p $pertdir #outfile=${pertdir}/trkr.${regtype}.${cmodel}.${pert}.${ymdh}.out -if [[ -d /dcom ]] ; then - # We are on NOAA Tide or Gyre - machine=wcoss - ${USHens_tracker}/extrkr_tcv_gfs.sh ${loopnum} ${cmodel} ${pert} ${pertdir} #2>&1 >${outfile} - -elif [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then +if [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then # We are on NOAA Luna or Surge machine=cray ${APRUNTRACK} ${USHens_tracker}/extrkr_tcv_gfs.sh ${loopnum} ${cmodel} ${pert} ${pertdir} #2>&1 >${outfile} @@ -89,12 +100,7 @@ mkdir -p $pertdir #outfile=${pertdir}/trkr.${regtype}.${cmodel}.${pert}.${ymdh}.out -if [[ -d /dcom ]] ; then - # We are on NOAA Tide or Gyre - machine=wcoss - ${USHens_tracker}/extrkr_gen_gfs.sh ${loopnum} ${cmodel} ${pert} ${pertdir} #2>&1 >${outfile} - -elif [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then +if [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then # We are on NOAA Luna or Surge machine=cray ${APRUNTRACK} ${USHens_tracker}/extrkr_gen_gfs.sh ${loopnum} ${cmodel} ${pert} ${pertdir} #2>&1 >${outfile} diff --git a/sorc/ens_tracker.v1.1.15.4/scripts/exgfs_tc_genesis.sh_07292019 b/sorc/ens_tracker.v1.1.15.4/scripts/exgfs_tc_genesis.sh_07292019 new file mode 100755 index 0000000000..0c63aae079 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/scripts/exgfs_tc_genesis.sh_07292019 @@ -0,0 +1,101 @@ +#!/bin/ksh +set -x + +export cmodel=gfs +export loopnum=1 +export ymdh=${PDY}${cyc} + +export gfsdir=${COMINgfs}/${cyc} + +#---- first run to get GFS genesis vital at time=00 06 12 18Z ----------- + +export trkrtype=tcgen +export trkrebd=350.0 +export trkrwbd=105.0 +export trkrnbd=30.0 +export trkrsbd=5.0 +export mslpthresh=0.0015 +export v850thresh=1.5000 +export regtype=altg + +export pert=p01 +export pertdir=${DATA}/${cmodel}/${pert} +mkdir -p $pertdir + +#outfile=${pertdir}/trkr.${regtype}.${cmodel}.${pert}.${ymdh}.out + +mac=`echo ${SITE}` +if [ $mac = TIDE -o $mac = GYRE ] ; then # For WCOSS + machine=wcoss + ${USHens_tracker}/extrkr_tcv_g2.sh ${loopnum} ${cmodel} ${pert} ${pertdir} #2>&1 >${outfile} +elif [ $mac = LUNA -o $mac = SURGE ] ; then # For CRAY + machine=cray + ${APRUNTRACK} ${USHens_tracker}/extrkr_tcv_g2.sh ${loopnum} ${cmodel} ${pert} ${pertdir} #2>&1 >${outfile} +fi +export err=$?; err_chk + +#### NCEP/GFS genesis tcvitals ########################### +num_gen_vits=`cat ${COMINgenvit}/genesis.vitals.gfs.gfso.${JYYYY} | wc -l` +if [ ${num_gen_vits} -gt 0 ] +then + . prep_step + + # Input file + export FORT41=${COMINgenvit}/genesis.vitals.gfs.gfso.${JYYYY} + + # Output files + export FORT42=gen_tc.vitals.gfs.gfso.${JYYYY} + + ${EXECens_tracker}/tcvital_ch_gfs + export err=$?; err_chk + + cat $FORT42 >> ${COMOUTgenvit}/all.vitals.gfs.gfso.${JYYYY} +else + touch ${COMOUTgenvit}/all.vitals.gfs.gfso.${JYYYY} +fi + +#---- second run genesis --------------------------------------- + +export pertdir=${DATA}/${cmodel}/${pert} +mkdir -p $pertdir + +#outfile=${pertdir}/trkr.${regtype}.${cmodel}.${pert}.${ymdh}.out + +mac=`echo ${SITE}` +if [ $mac = TIDE -o $mac = GYRE ] ; then # For WCOSS + machine=wcoss + ${USHens_tracker}/extrkr_gen_g2.sh ${loopnum} ${cmodel} ${pert} ${pertdir} #2>&1 >${outfile} +elif [ $mac = LUNA -o $mac = SURGE ] ; then # For CRAY + machine=cray + ${APRUNTRACK} ${USHens_tracker}/extrkr_gen_g2.sh ${loopnum} ${cmodel} ${pert} ${pertdir} #2>&1 >${outfile} +fi +export err=$?; err_chk + +export atcfout=gfso +export TRKDATA=${DATA}/${cmodel}/${pert} +${USHens_tracker}/sort_tracks.gen.sh >${TRKDATA}/sort.${regtype}.${atcfout}.${ymdh}.out +export err=$?; err_chk + +#cp ${pertdir}/trak.gfso.atcfunix.altg.${ymdh} ${COMOUT}/ +#cp ${pertdir}/storms.gfso.atcf_gen.altg.${ymdh} ${COMOUT}/ + +#### filtering weak storms for TC genesis ##### +. prep_step + +# Input file +export FORT41=storms.gfso.atcf_gen.altg.${ymdh} +cpreq ${COMOUT}/$FORT41 . + +# Output files +export FORT42=storms.gfso.atcf_gen.${ymdh} +export FORT43=trak.gfso.atcfunix.${ymdh} + +${EXECens_tracker}/filter_gen_gfs +export err=$?; err_chk + +if [ "$SENDCOM" = YES ]; then + cp $FORT42 $FORT43 ${COMOUT}/ + if [ $? -ne 0 ]; then + echo "WARNING: Filtering did not produce any files... perhaps there were no storms to begin with." + fi +fi diff --git a/sorc/ens_tracker.v1.1.15.4/scripts/exgfs_tc_track.sh b/sorc/ens_tracker.v1.1.15.4/scripts/exgfs_tc_track.sh new file mode 100755 index 0000000000..787151f993 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/scripts/exgfs_tc_track.sh @@ -0,0 +1,67 @@ +#!/bin/ksh +set -x + +export cmodel=gfs +export loopnum=1 +export ymdh=${PDY}${cyc} + +#export gfsdir=${COMINgfs}/${cyc} +#export gfsdir=${COMINgfs} + +export COMPONENT=${COMPONENT:-atmos} +export gfsdir=${COMINgfs}/${cyc}/${COMPONENT} + +export pert="p01" +pertdir=${DATA}/${cmodel}/${pert} +mkdir -p $pertdir + +#-----------input data checking ----------------- +#${USHens_tracker}/data_check.sh +${USHens_tracker}/data_check_gfs.sh +## exit code 6 = missing data of opportunity +if [ $? -eq 6 ]; then exit; fi +#------------------------------------------------ + +outfile=${pertdir}/trkr.${cmodel}.${pert}.${ymdh}.out + +if [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then + # We are on NOAA Luna or Surge + machine=cray + ${APRUNTRACK} ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} + +elif [[ -L /usrx && "$( readlink /usrx 2> /dev/null )" =~ dell ]] ; then + # We are on NOAA Mars or Venus + machine=dell + ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} + +elif [[ -d /scratch2 ]] ; then + # We are on NOAA Hera + machine=hera + ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} + +elif [[ -d /work ]] ; then + # We are on MSU Orion + machine=orion + ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} + +elif [[ -d /lfs3 ]] ; then + # We are on NOAA Jet + machine=jet + ${USHens_tracker}/extrkr_gfs.sh ${loopnum} ${cmodel} ${ymdh} ${pert} ${pertdir} #2>&1 >${outfile} + +else + export machine=unknown + echo Job failed: unknown platform 1>&2 + err_exit "FAILED ${jobid} - ERROR IN unknown platform - ABNORMAL EXIT" + +fi +export err=$?; err_chk + +#if [ "$SENDCOM" = 'YES' ]; then +# cat ${pertdir}/trak.gfso.atcfunix.${PDY}${cyc} | \ +# sed s:GFSO:AVNO:g \ +# > ${COMOUT}/avn.t${cyc}z.cyclone.trackatcfunix +#fi + +############################################################# + diff --git a/sorc/ens_tracker.v1.1.15.2/scripts/exnavgem_tc_genesis.sh b/sorc/ens_tracker.v1.1.15.4/scripts/exnavgem_tc_genesis.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/scripts/exnavgem_tc_genesis.sh rename to sorc/ens_tracker.v1.1.15.4/scripts/exnavgem_tc_genesis.sh diff --git a/sorc/ens_tracker.v1.1.15.2/scripts/exukmet_grib.sh b/sorc/ens_tracker.v1.1.15.4/scripts/exukmet_grib.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/scripts/exukmet_grib.sh rename to sorc/ens_tracker.v1.1.15.4/scripts/exukmet_grib.sh diff --git a/sorc/ens_tracker.v1.1.15.2/scripts/exukmet_tc_genesis.sh b/sorc/ens_tracker.v1.1.15.4/scripts/exukmet_tc_genesis.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/scripts/exukmet_tc_genesis.sh rename to sorc/ens_tracker.v1.1.15.4/scripts/exukmet_tc_genesis.sh diff --git a/sorc/ens_tracker.v1.1.15.2/scripts/exwsr_ecmwfens.sh b/sorc/ens_tracker.v1.1.15.4/scripts/exwsr_ecmwfens.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/scripts/exwsr_ecmwfens.sh rename to sorc/ens_tracker.v1.1.15.4/scripts/exwsr_ecmwfens.sh diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Cray b/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/Module_ens_tracker.v1.1.15_for_Cray rename to sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Cray diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Dell b/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Dell new file mode 100644 index 0000000000..7167ebe00d --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Dell @@ -0,0 +1,27 @@ +#%Module##################################################### +## Module file for ens_tracker +############################################################# + +#module load ips/18.0.1.163 +#module load impi/18.0.1 +module use /usrx/local/nceplibs/dev/hpc-stack/libs/hpc-stack/modulefiles/stack +module load hpc/1.1.0 +module load hpc-ips/18.0.1.163 +module load hpc-impi/18.0.1 + +module load w3emc/2.7.3 +module load w3nco/2.4.1 +module load bacio/2.4.1 +module load sp/2.3.3 +module load nemsio/2.5.2 +module load nemsiogfs/2.5.3 +module load sigio/2.3.2 +module load g2/3.4.1 + +module load jasper/2.0.25 +module load png/1.6.35 +module load zlib/1.2.11 +module load hdf5/1.10.6 +module load netcdf/4.7.4 +module load PNetCDF/1.8.1 + diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Hera b/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Hera new file mode 100644 index 0000000000..0232c38f32 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Hera @@ -0,0 +1,24 @@ +#%Module##################################################### +## Module file for ens_tracker +############################################################# + +module use /scratch2/NCEPDEV/nwprod/hpc-stack/libs/hpc-stack/modulefiles/stack +module load hpc/1.1.0 +module load hpc-intel/18.0.5.274 +module load hpc-impi/2018.0.4 + +module load w3emc/2.7.3 +module load w3nco/2.4.1 +module load bacio/2.4.1 +module load sp/2.3.3 +module load nemsio/2.5.2 +module load nemsiogfs/2.5.3 +module load sigio/2.3.2 +module load g2/3.4.1 + +module load jasper/2.0.25 +module load png/1.6.35 +module load zlib/1.2.11 +module load hdf5/1.10.6 +module load netcdf/4.7.4 +module load PNetCDF/1.8.1 diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Jet b/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Jet new file mode 100644 index 0000000000..ca5f1e83b5 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Jet @@ -0,0 +1,24 @@ +#%Module for JET ################################################ +## Module file for ens_tracker +############################################################# + +module use /mnt/lfs4/HFIP/hfv3gfs/nwprod/hpc-stack/libs/modulefiles/stack +module load hpc/1.1.0 +module load hpc-intel/18.0.5.274 +module load hpc-impi/2018.4.274 + +module load w3emc/2.7.3 +module load w3nco/2.4.1 +module load bacio/2.4.1 +module load sp/2.3.3 +module load nemsio/2.5.2 +module load nemsiogfs/2.5.3 +module load sigio/2.3.2 +module load g2/3.4.1 + +module load jasper/2.0.25 +module load png/1.6.35 +module load zlib/1.2.11 +module load hdf5/1.10.6 +module load netcdf/4.7.4 +module load pnetcdf/1.10.0 diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Jet0612 b/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Jet0612 new file mode 100644 index 0000000000..ef8e19f88d --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Jet0612 @@ -0,0 +1,31 @@ +#%Module for JET ################################################ +#setenv NCEPLIBS /mnt/lfs3/projects/hfv3gfs/gwv/ljtjet/lib +setenv NCEPLIBS /mnt/lfs4/HFIP/hfv3gfs/gwv/l0530/lib + +module use /apps/modules/modulefamilies/intel +module use /apps/modules/modulefiles +module use /apps/modules/modulefamilies/intel_mvapich2/1.8 +#module use /mnt/lfs3/projects/hfv3gfs/gwv/ljtjet/lib/modulefiles +module use /mnt/lfs4/HFIP/hfv3gfs/gwv/l0530/lib/modulefiles + +module load intel/18.0.5.274 +module load impi/2018.4.274 +module load nco/4.1.0 +module load szip/2.1 + +module load png-intel-sandybridge/1.2.44 +module load w3emc-intel-sandybridge/2.3.0 +module load w3nco-intel-sandybridge/2.0.6 +module load bacio-intel-sandybridge/2.0.1 +module load g2-intel-sandybridge/3.1.0 + +module load zlib-intel-sandybridge/1.2.6 +module load jasper-intel-sandybridge/1.900.1 +module load sigio-intel-sandybridge/2.0.1 +module load sp-intel-sandybridge/2.0.2 +module load ip-intel-sandybridge/3.0.0 + +module load netcdf/3.6.3 +module load hdf5/1.8.9 +#module load netcdf4/4.2 +module load pnetcdf/1.7.0 diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Jet_06112020 b/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Jet_06112020 new file mode 100644 index 0000000000..3115cab06b --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Jet_06112020 @@ -0,0 +1,29 @@ +#%Module for JET ################################################ +setenv NCEPLIBS /mnt/lfs3/projects/hfv3gfs/gwv/ljtjet/lib + +module use /apps/modules/modulefamilies/intel +module use /apps/modules/modulefiles +module use /apps/modules/modulefamilies/intel_mvapich2/1.8 +module use /mnt/lfs3/projects/hfv3gfs/gwv/ljtjet/lib/modulefiles + +module load intel/14.0.3 +#module load impi/5.1.3.181 +module load nco/4.1.0 +module load szip/2.1 + +module load png-intel-sandybridge/1.2.44 +module load w3emc-intel-sandybridge/2.3.0 +module load w3nco-intel-sandybridge/2.0.6 +module load bacio-intel-sandybridge/2.0.1 +module load g2-intel-sandybridge/3.1.0 + +module load zlib-intel-sandybridge/1.2.6 +module load jasper-intel-sandybridge/1.900.1 +module load sigio-intel-sandybridge/2.0.1 +module load sp-intel-sandybridge/2.0.2 +module load ip-intel-sandybridge/3.0.0 + +module load netcdf/3.6.3 +module load hdf5/1.8.9 +#module load netcdf4/4.2 +module load pnetcdf/1.5.0 diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Orion b/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Orion new file mode 100644 index 0000000000..f5a17dccec --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/Module_ens_tracker.v1.1.15_for_Orion @@ -0,0 +1,23 @@ +#%Module for Orion ################################################ +# +module use /apps/contrib/NCEP/libs/hpc-stack/modulefiles/stack +module use /apps/contrib/NCEP/libs/hpc-stack/modulefiles/stack +module load hpc/1.1.0 +module load hpc-intel/2018.4 +module load hpc-impi/2018.4 + +module load w3emc/2.7.3 +module load w3nco/2.4.1 +module load bacio/2.4.1 +module load sp/2.3.3 +module load nemsio/2.5.2 +module load nemsiogfs/2.5.3 +module load sigio/2.3.2 +module load g2/3.4.1 + +module load jasper/2.0.25 +module load png/1.6.35 +module load zlib/1.2.11 +module load hdf5/1.10.6 +module load netcdf/4.7.4 +module load PNetCDF/1.8.1 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/README.build b/sorc/ens_tracker.v1.1.15.4/sorc/README.build similarity index 54% rename from sorc/ens_tracker.v1.1.15.2/sorc/README.build rename to sorc/ens_tracker.v1.1.15.4/sorc/README.build index 532e57c2df..caa4eb5818 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/README.build +++ b/sorc/ens_tracker.v1.1.15.4/sorc/README.build @@ -1,16 +1,11 @@ Libraries are defined in: Module_ens_tracker.v1.1.15_for_Dell Module_ens_tracker.v1.1.15_for_Cray -Module_ens_tracker.v1.1.15_for_Wcoss -Module_ens_tracker.v1.1.15_for_Theia Module_ens_tracker.v1.1.15_for_Jet Module_ens_tracker.v1.1.15_for_Hera Module_ens_tracker.v1.1.15_for_Orion To build codes: - - cd /XXXX/sorc - 1) module use . - 2) module load Module_ens_tracker.v1.1.15_for_Orion - 3) ./build.sh + cd /XXXX/sorc + ./build.sh diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/build.sh b/sorc/ens_tracker.v1.1.15.4/sorc/build.sh new file mode 100755 index 0000000000..c468c898b9 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/build.sh @@ -0,0 +1,129 @@ +#!/bin/sh + +#--------------------------------------------------------- +set -x +if [[ ! -d ../exec ]] ; then mkdir ../exec ; fi + +set +x +# Purge current modules +module purge +# Use modules within +module use . +module list +set -x + +#--------------------------------------------------------- +if [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then + # Load module file for Cray (NOAA Luna or Surge) + module load Module_ens_tracker.v1.1.15_for_Cray + + machine=cray + export INC="${G2_INCd} ${NETCDF_INCLUDE} -I${PNETCDF_INC} ${HDF5_INCLUDE}" + export LIBS="${W3EMC_LIBd} ${W3NCO_LIBd} ${BACIO_LIB4} ${G2_LIBd} ${PNG_LIB} ${JASPER_LIB} ${Z_LIB} ${NETCDF_LDFLAGS_CXX} -L${PNETCDF_LIB} ${HDF5_LDFLAGS}" + export LIBS_SUP="${W3EMC_LIBd} ${W3NCO_LIBd}" + export LIBS_UK="${W3NCO_LIB4} ${BACIO_LIB4}" + +for dir in *.fd; do + cd $dir + make clean + make -f makefile_cray + make install + cd .. +done + +elif [[ -L /usrx && "$( readlink /usrx 2> /dev/null )" =~ dell ]] ; then + # Load module file for Dell (NOAA Mars or Venus) + module load Module_ens_tracker.v1.1.15_for_Dell + + machine=dell + export NETCDF_LDFLAGS="-L${NETCDF_ROOT}/lib -lnetcdff -lnetcdf -L${HDF5_ROOT}/lib -lhdf5_hl -lhdf5 -L${ZLIB_ROOT}/lib -lz -ldl -lm" + export NETCDF_INCLUDES="-I${NETCDF_ROOT}/include -I${HDF5_ROOT}/include" +# export INC="${G2_INCd} ${NETCDF_INCLUDE} ${HDF5_INCLUDE}" +# export LIBS="${W3EMC_LIBd} ${W3NCO_LIBd} ${BACIO_LIB4} ${G2_LIBd} ${PNG_LIB} ${JASPER_LIB} ${Z_LIB} ${NETCDF_LDFLAGS} ${HDF5_LDFLAGS}" + export INC="${G2_INCd} ${NETCDF_INCLUDES} ${PNetCDF_INCLUDE}" + export LIBS="${W3EMC_LIBd} ${W3NCO_LIBd} ${BACIO_LIB4} ${G2_LIBd} ${PNG_ROOT}/lib64/libpng.a ${JASPER_ROOT}/lib64/libjasper.a ${ZLIB_ROOT}/lib/libz.a ${NETCDF_LDFLAGS} ${PNetCDF_LDFLAGS}" + export LIBS_SUP="${W3EMC_LIBd} ${W3NCO_LIBd}" + export LIBS_UK="${W3NCO_LIB4} ${BACIO_LIB4}" + +for dir in *.fd; do +#for dir in gettrk_gfs.fd; do + cd $dir + make clean + make -f makefile + make install + cd .. +done + +elif [[ -d /scratch2 ]] ; then + # Load module file for NOAA Hera + module load Module_ens_tracker.v1.1.15_for_Hera + +machine=hera +export NETCDF_LDFLAGS="-L${NETCDF_ROOT}/lib -lnetcdff -lnetcdf -L${HDF5_ROOT}/lib -lhdf5_hl -lhdf5 -L${ZLIB_ROOT}/lib -lz -ldl -lm" +export NETCDF_INCLUDES="-I${NETCDF_ROOT}/include -I${HDF5_ROOT}/include" + +export INC="${G2_INCd} ${NETCDF_INCLUDES}" +export LIBS="${W3EMC_LIBd} ${W3NCO_LIBd} ${BACIO_LIB4} ${G2_LIBd} ${PNG_ROOT}/lib64/libpng.a ${JASPER_ROOT}/lib64/libjasper.a ${ZLIB_ROOT}/lib/libz.a ${NETCDF_LDFLAGS}" +export LIBS_SUP="${W3EMC_LIBd} ${W3NCO_LIBd}" +export LIBS_UK="${W3NCO_LIB4} ${BACIO_LIB4}" + +for dir in *.fd; do +#for dir in leadtime.fd; do + cd $dir + make clean + make -f makefile_hera + make install + cd .. +done + +elif [[ -d /work ]] ; then + # Load module file for MSU Orion + module load Module_ens_tracker.v1.1.15_for_Orion + +machine=orion +export NETCDF_LDFLAGS="-L${NETCDF_ROOT}/lib -lnetcdff -lnetcdf -L${HDF5_ROOT}/lib -lhdf5_hl -lhdf5 -L${ZLIB_ROOT}/lib -lz -ldl -lm" +export NETCDF_INCLUDES="-I${NETCDF_ROOT}/include -I${HDF5_ROOT}/include" + +export INC="${G2_INCd} ${NETCDF_INCLUDES}" +export LIBS="${W3EMC_LIBd} ${W3NCO_LIBd} ${BACIO_LIB4} ${G2_LIBd} ${PNG_ROOT}/lib64/libpng.a ${JASPER_ROOT}/lib64/libjasper.a ${ZLIB_ROOT}/lib/libz.a ${NETCDF_LDFLAGS}" +export LIBS_SUP="${W3EMC_LIBd} ${W3NCO_LIBd}" +export LIBS_UK="${W3NCO_LIB4} ${BACIO_LIB4}" + +for dir in *.fd; do +#for dir in gettrk_gfs.fd; do + cd $dir + make clean + make -f makefile_orion + make install + cd .. +done + +elif [[ -d /lfs4 ]] ; then + # Load module file for Jet + set +x + module load Module_ens_tracker.v1.1.15_for_Jet + set -x + + machine=jet + +export NETCDF_LDFLAGS="-L${NETCDF_ROOT}/lib -lnetcdff -lnetcdf -L${HDF5_ROOT}/lib -lhdf5_hl -lhdf5 -L${ZLIB_ROOT}/lib -lz -ldl -lm" +export NETCDF_INCLUDES="-I${NETCDF_ROOT}/include -I${HDF5_ROOT}/include" + +export INC="${G2_INCd} ${NETCDF_INCLUDES}" +export LIBS="${W3EMC_LIBd} ${W3NCO_LIBd} ${BACIO_LIB4} ${G2_LIBd} ${PNG_ROOT}/lib64/libpng.a ${JASPER_ROOT}/lib64/libjasper.a ${ZLIB_ROOT}/lib/libz.a ${NETCDF_LDFLAGS}" +export LIBS_SUP="${W3EMC_LIBd} ${W3NCO_LIBd}" +export LIBS_UK="${W3NCO_LIB4} ${BACIO_LIB4}" + +for dir in *.fd; do +#for dir in gettrk_gfs.fd; do + cd $dir + make clean + make -f makefile_jet + make install + cd .. +done + +else + export machine=unknown + echo Job failed: unknown platform 1>&2 +fi diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/build.sh_05222020 b/sorc/ens_tracker.v1.1.15.4/sorc/build.sh_05222020 new file mode 100755 index 0000000000..64f379da04 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/build.sh_05222020 @@ -0,0 +1,95 @@ +#!/bin/sh + +#--------------------------------------------------------- +if [[ -d /dcom ]] ; then + # We are on NOAA Tide or Gyre + +machine=wcoss +export INC="${G2_INCd} ${NETCDF_INCLUDE} -I${PNETCDF}/include ${HDF5_INCLUDE}" +export LIBS="${W3EMC_LIBd} ${W3NCO_LIBd} ${BACIO_LIB4} ${G2_LIBd} ${PNG_LIB} ${JASPER_LIB} ${Z_LIB} ${SP_LIBd} ${IP_LIBd} ${NETCDF_LDFLAGS} -L${PNETCDF}/lib -lpnetcdf ${HDF5_LDFLAGS}" +export LIBS_SUP="${W3EMC_LIBd} ${W3NCO_LIBd}" +export LIBS_UK="${W3NCO_LIB4} ${BACIO_LIB4}" + +for dir in *.fd; do + cd $dir + make clean + make -f makefile_wcoss + make install + cd .. +done + +elif [[ -d /gpfs/hps && -e /etc/SuSE-release ]] ; then + # We are on NOAA Luna or Surge + + machine=cray + export INC="${G2_INCd} ${NETCDF_INCLUDE} -I${PNETCDF_INC} ${HDF5_INCLUDE}" + export LIBS="${W3EMC_LIBd} ${W3NCO_LIBd} ${BACIO_LIB4} ${G2_LIBd} ${PNG_LIB} ${JASPER_LIB} ${Z_LIB} ${NETCDF_LDFLAGS_CXX} -L${PNETCDF_LIB} ${HDF5_LDFLAGS}" + export LIBS_SUP="${W3EMC_LIBd} ${W3NCO_LIBd}" + export LIBS_UK="${W3NCO_LIB4} ${BACIO_LIB4}" + +for dir in *.fd; do + cd $dir + make clean + make -f makefile_cray + make install + cd .. +done + +elif [[ -L /usrx && "$( readlink /usrx 2> /dev/null )" =~ dell ]] ; then + # We are on NOAA Mars or Venus + + machine=dell + export INC="${G2_INCd} ${NETCDF_INCLUDE} ${PNetCDF_INCLUDE}" + export LIBS="${W3EMC_LIBd} ${W3NCO_LIBd} ${BACIO_LIB4} ${G2_LIBd} ${PNG_LIB} ${JASPER_LIB} ${Z_LIB} ${NETCDF_LDFLAGS} ${PNetCDF_LDFLAGS}" + export LIBS_SUP="${W3EMC_LIBd} ${W3NCO_LIBd}" + export LIBS_UK="${W3NCO_LIB4} ${BACIO_LIB4}" + +for dir in *.fd; do + cd $dir + make clean + make -f makefile + make install + cd .. +done + +elif [[ -d /scratch2 ]] ; then + # We are on NOAA Hera + +machine=hera +export INC="${G2_INCd} -I${NETCDF}/include -I${HDF5}/include " +#export LIBS="${W3EMC_LIBd} ${W3NCO_LIBd} ${BACIO_LIB4} ${G2_LIBd} ${PNG_LIB} ${JASPER_LIB} ${Z_LIB} ${SP_LIBd} ${IP_LIBd} -L${NETCDF}/lib -lnetcdff -lnetcdf -L${HDF5}/lib -lhdf5_hl -lhdf5hl_fortran -lhdf5 -lhdf5_fortran " +export LIBS="${W3EMC_LIBd} ${W3NCO_LIBd} ${BACIO_LIB4} ${G2_LIBd} ${PNG_LIB} ${JASPER_LIB} ${Z_LIB} ${SP_LIBd} ${IP_LIBd} -L${NETCDF}/lib -lnetcdff -lnetcdf -L${HDF5}/lib -lhdf5_hl -lhdf5 " +export LIBS_SUP="${W3EMC_LIBd} ${W3NCO_LIBd}" +export LIBS_UK="${W3NCO_LIB4} ${BACIO_LIB4}" + +for dir in *.fd; do +#for dir in leadtime.fd; do + cd $dir + make clean + make -f makefile_wcoss + make install + cd .. +done + +elif [[ -d /lfs3 ]] ; then + # We are on NOAA Jet + +machine=jet +export INC="${G2_INCd} -I${NETCDF}/include ${PNETCDF_INCLUDE} ${HDF5_INCLUDE_OPTS} " +export LIBS="${W3EMC_LIBd} ${W3NCO_LIBd} ${BACIO_LIB4} ${G2_LIBd} ${PNG_LIB} ${JASPER_LIB} ${Z_LIB} ${SP_LIBd} ${IP_LIBd} -L${NETCDF}/lib -lnetcdf ${PNETCDF_LD_OPTS} ${HDF5_LINK_OPTS}" + +export LIBS_SUP="${W3EMC_LIBd} ${W3NCO_LIBd}" +export LIBS_UK="${W3NCO_LIB4} ${BACIO_LIB4}" + +for dir in *.fd; do + cd $dir + make clean + make -f makefile_jet + make install + cd .. +done + +else + export machine=unknown + echo Job failed: unknown platform 1>&2 +fi diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/build.sh b/sorc/ens_tracker.v1.1.15.4/sorc/build.sh_08122020 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/build.sh rename to sorc/ens_tracker.v1.1.15.4/sorc/build.sh_08122020 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/build.sh_origin b/sorc/ens_tracker.v1.1.15.4/sorc/build.sh_origin similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/build.sh_origin rename to sorc/ens_tracker.v1.1.15.4/sorc/build.sh_origin diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/copy_makefile.sh b/sorc/ens_tracker.v1.1.15.4/sorc/copy_makefile.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/copy_makefile.sh rename to sorc/ens_tracker.v1.1.15.4/sorc/copy_makefile.sh diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/copy_makefile_4_cray.sh b/sorc/ens_tracker.v1.1.15.4/sorc/copy_makefile_4_cray.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/copy_makefile_4_cray.sh rename to sorc/ens_tracker.v1.1.15.4/sorc/copy_makefile_4_cray.sh diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/copy_makefile_4_dell.sh b/sorc/ens_tracker.v1.1.15.4/sorc/copy_makefile_4_dell.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/copy_makefile_4_dell.sh rename to sorc/ens_tracker.v1.1.15.4/sorc/copy_makefile_4_dell.sh diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/copy_makefile_4_jet.sh b/sorc/ens_tracker.v1.1.15.4/sorc/copy_makefile_4_jet.sh similarity index 85% rename from sorc/ens_tracker.v1.1.15.2/sorc/copy_makefile_4_jet.sh rename to sorc/ens_tracker.v1.1.15.4/sorc/copy_makefile_4_jet.sh index dc15fe8d75..a7741efcfb 100755 --- a/sorc/ens_tracker.v1.1.15.2/sorc/copy_makefile_4_jet.sh +++ b/sorc/ens_tracker.v1.1.15.4/sorc/copy_makefile_4_jet.sh @@ -1,7 +1,7 @@ #copy makefile_jet -export inputdir=/gpfs/dell2/emc/modeling/noscrub/Jiayi.Peng/ens_tracker.v1.1.13.2/sorc -export outputdir=/gpfs/dell2/emc/modeling/noscrub/Jiayi.Peng/ens_tracker.v1.1.15.1/sorc +export inputdir=/lfs1/NESDIS/nesdis-rdo2/David.Huber/TC_Tracker/TC_tracker.v1.1.15.4/sorc +export outputdir=/lfs1/NESDIS/nesdis-rdo2/David.Huber/TC_Tracker/TC_tracker.v1.1.15.4/sorc cp $inputdir/ens_trak_ave_2d.fd/makefile_jet $outputdir/ens_trak_ave_2d.fd/makefile_jet cp $inputdir/ens_trak_ave.fd/makefile_jet $outputdir/ens_trak_ave.fd/makefile_jet @@ -13,11 +13,13 @@ cp $inputdir/filter_ukmet.fd/makefile_jet $outputdir/filter_ukmet.fd/makefile_je cp $inputdir/gettrk_g1.fd/makefile_jet $outputdir/gettrk_g1.fd/makefile_jet cp $inputdir/gettrk_g2.fd/makefile_jet $outputdir/gettrk_g2.fd/makefile_jet -#cp $inputdir/gettrk_gfs.fd/makefile_jet $outputdir/gettrk_gfs.fd/makefile_jet +cp $inputdir/gettrk_gfs.fd/makefile_jet $outputdir/gettrk_gfs.fd/makefile_jet cp $inputdir/gettrk_gen_g1.fd/makefile_jet $outputdir/gettrk_gen_g1.fd/makefile_jet cp $inputdir/gettrk_gen_g2.fd/makefile_jet $outputdir/gettrk_gen_g2.fd/makefile_jet +cp $inputdir/gettrk_gen_gfs.fd/makefile_jet $outputdir/gettrk_gen_gfs.fd/makefile_jet +cp $inputdir/leadtime.fd/makefile_jet $outputdir/leadtime.fd/makefile_jet cp $inputdir/ncep_tcv_cmc.fd/makefile_jet $outputdir/ncep_tcv_cmc.fd/makefile_jet cp $inputdir/ncep_tcv_ecmwf.fd/makefile_jet $outputdir/ncep_tcv_ecmwf.fd/makefile_jet diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/copy_makefile_4_orion.sh b/sorc/ens_tracker.v1.1.15.4/sorc/copy_makefile_4_orion.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/copy_makefile_4_orion.sh rename to sorc/ens_tracker.v1.1.15.4/sorc/copy_makefile_4_orion.sh diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/ens_trak_ave.f b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/ens_trak_ave.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/ens_trak_ave.f rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/ens_trak_ave.f diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/ens_trak_ave.f.orig0326 b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/ens_trak_ave.f.orig0326 new file mode 100644 index 0000000000..d3e53c4fb7 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/ens_trak_ave.f.orig0326 @@ -0,0 +1,3326 @@ + module define_atcfunix_rec + type atcfunix_card ! Define a new type for an atcfunix card + sequence + character*2 atx_basin ! Hurricane Center Acronym + character*2 atx_storm_num ! Storm number (03, etc) + character*10 atx_ymdh ! Fcst start date (yyyymmddhh) + character*2 atx_technum ! Always equal to 03 for models + character*4 atx_model ! Name of forecast model + integer atx_fcsthr ! Fcst hour (i3.3) + integer atx_lat ! Storm Lat (*10), always >0 + character*1 atx_latns ! 'N' or 'S' + integer atx_lon ! Storm Lon (*10), always >0 + character*1 atx_lonew ! 'E' or 'W' + integer atx_vmax ! max sfc wind speed (knots) + integer atx_pcen ! Min central pressure (mb) +c character*2 atx_stype ! Storm type (= 'XX' for now) +c integer atx_wthresh ! Wind threshold (34, 50, 64 kt) +c character*3 atx_quad_id ! NEQ = start with NE quadrant +c integer atx_ne_quad ! Wind radius in NE quad (nm) +c integer atx_se_quad ! Wind radius in SE quad (nm) +c integer atx_sw_quad ! Wind radius in SW quad (nm) +c integer atx_nw_quad ! Wind radius in NW quad (nm) + end type atcfunix_card + end module define_atcfunix_rec + + module maxparms + integer, parameter :: ncmaxmem = 20 ! max # ncep ensemble perts + integer, parameter :: nrmaxmem = 10 ! max # Tom ensemble perts + integer, parameter :: n0maxmem = 20 ! max # ncep_bc ensemble perts + integer, parameter :: h0maxmem = 20 ! max # hwrf ensemble + integer, parameter :: y0maxmem = 10 ! max # COAMPS ensemble + integer, parameter :: d0maxmem = 10 ! max # GFDL ensemble + integer, parameter :: e0maxmem = 40 ! max # HWRF+GFDL+COAMPS ensemble + integer, parameter :: e1maxmem = 30 ! max # HWRF+GFDL + integer, parameter :: e2maxmem = 30 ! max # HWRF+NAVY + integer, parameter :: e3maxmem = 20 ! max # GFDL+NAVY + + integer, parameter :: ukmaxmem = 23 ! max # UKMET ensemble perts + integer, parameter :: frmaxmem = 34 ! max # FRANCE ensemble perts + +cJ.Peng-------2010-09-29-------------------------------------- + integer, parameter :: gcmaxmem = 20 ! max # gv ensemble perts + integer, parameter :: g0maxmem = 20 ! max # FNMOC_bc ensemble perts + + integer, parameter :: ccmaxmem = 20 ! max # cmc ensemble perts + integer, parameter :: c0maxmem = 20 ! max # cmc_bc ensemble perts + + integer, parameter :: ecmaxmem = 50 ! max # ecmwf ensemble perts + integer, parameter :: ezmaxmem = 20 ! max # ecmwf ensemble perts + + integer, parameter :: srmaxmem = 21 ! max # sref ensemble perts +cJ.Peng-------2010-10-29------------2010-11-02----------------- + integer, parameter :: c2maxmem = 40 ! max # NCEP+CMC ens pert + integer, parameter :: f2maxmem = 40 ! max # NCEP+FNMOC ens pert + + integer, parameter :: c3maxmem = 90 ! max # NCEP+CMC+EC ens pert + integer, parameter :: n2maxmem = 70 ! max # NCEP+EC ens pert + integer, parameter :: m2maxmem = 70 ! max # NCEP+EC ens pert + + integer, parameter :: c4maxmem = 110 ! max # NCEP+CMC+EC+FNMOC + integer, parameter :: c5maxmem = 133 ! max # NCEP+CMC+EC+FNMOC+UK + + integer, parameter :: c6maxmem = 153 ! max # NCEP+CMC+EC+FNMOC+UK+ + + integer, parameter :: n3maxmem = 93 ! max # NCEP+EC+UK + + integer, parameter :: f3maxmem = 60 ! max # NCEP+CMC+FNMOC + + integer, parameter :: s3maxmem = 2 ! max # eemn+2emn ens pert + integer, parameter :: p3maxmem = 3 ! max # ee/ae/ce+mn ens pert + + integer, parameter :: d3maxmem = 3 ! max # AVNO+CMC+EMX pert + integer, parameter :: d6maxmem = 6 ! max # AVNO+CMC+EMX+AEMN+.. + + integer, parameter :: x6maxmem = 6 ! max # EEMN+AEMN+CEMN+FEMN+ + integer, parameter :: d4maxmem = 4 ! max # AVNO+CMC+EMX+3EMN + integer, parameter :: d2maxmem = 2 ! max # 3EMN+3DET.. + + integer, parameter :: ncminmem = 8 ! min # of ncep ensemble + ! perts needed at a given fcst hr to get a mean + integer, parameter :: nrminmem = 4 ! min # of Tom ensemble + integer, parameter :: n0minmem = 8 ! min # of ncep_bc ensemble + integer, parameter :: h0minmem = 8 ! min # of hwrf ensemble + integer, parameter :: y0minmem = 4 ! min # of coamps ensemble + integer, parameter :: d0minmem = 4 ! min # of GFDL ensemble + integer, parameter :: e0minmem = 16 ! min # of GFDL+HWRF+COAMPS ensemble + integer, parameter :: e1minmem = 12 ! min # of GFDL+HWRF + integer, parameter :: e2minmem = 12 ! min # of HWRF+NAVY + integer, parameter :: e3minmem = 8 ! min # of GFDL+NAVY + + integer, parameter :: ukminmem = 9 ! min # of UKMET ensemble + integer, parameter :: frminmem = 14 ! min # of FRANCE ensemble + +cJ.Peng----2010-09-29--------------------------------------------- + integer, parameter :: gcminmem = 8 ! min # of gv ensemble + integer, parameter :: g0minmem = 8 ! min # of gv ensemble + + integer, parameter :: ccminmem = 8 ! min # of cmc ensemble + integer, parameter :: c0minmem = 8 ! min # of cmc ensemble + + integer, parameter :: ecminmem = 20 ! min # of ecmwf ensemble + ! perts needed at a given fcst hr to get a mean + + integer, parameter :: ezminmem = 8 ! min # of ecmwf ensemble + + integer, parameter :: srminmem = 8 ! min # of sref ensemble + ! perts needed at a given fcst hr to get a mean +cJ.Peng-------2010-10-29-------------2010-11-02-------------------------- +c integer, parameter :: c2minmem = 20 ! min # of NCEP+CMC ensemble +c ! perts needed at a given fcst hr to get a mean +cJ.Peng------------2010-11-19-------------------------- + integer, parameter :: c2minmem = 22 ! min # of NCEP+CMC ensemble + ! perts needed at a given fcst hr to get a mean + integer, parameter :: f2minmem = 22 ! min # of NCEP+FNMOC ensemble + + integer, parameter :: c3minmem = 50 ! min # of NCEP+CMC+EC ensemble + integer, parameter :: n2minmem = 39 ! min # of NCEP+EC ensemble + integer, parameter :: m2minmem = 39 ! min # of NCEP+EC ensemble + + integer, parameter :: c4minmem = 61 ! min # of NCEP+CMC+EC+FNMOC + integer, parameter :: c5minmem = 74 ! min # of NCEP+CMC+EC+FNMOC+UK + + integer, parameter :: c6minmem = 85 ! min # of NCEP+CMC+EC+FNMOC+UK + integer, parameter :: n3minmem = 52 ! min # of NCEP+EC+UK + + integer, parameter :: f3minmem = 33 ! min # of NCEP+CMC+FNMOC + + integer, parameter :: s3minmem = 2 ! min # of NCEP+CMC+EC ensemble + integer, parameter :: p3minmem = 3 ! min # of NCEP+CMC+EC ensemble + + integer, parameter :: d3minmem = 3 ! min # of AVNO+CMC+EMX ensemble + integer, parameter :: d6minmem = 6 ! min # of AVNO+CMC+EMX+AEMN+.. + + integer, parameter :: x6minmem = 6 ! min # of + integer, parameter :: d4minmem = 4 ! min # of AVNO+CMC+EMX+3EMN + integer, parameter :: d2minmem = 2 ! min # of 3DET+3EMN.. + + integer, parameter :: maxstorms = 15 ! max # storms at a time + integer, parameter :: maxtimes = 65 ! max # fcst times (0-384 + ! every 6 hrs = 65). +c integer, parameter :: max_accum_prob_hour = 384 ! Max hour to +c integer, parameter :: max_accum_prob_hour = 120 ! Max hour to +c ! which we'll carry out the accum probs + real, parameter :: grdspc = 1.00 ! grid spacing of our grid + end module maxparms + + module ensinfo + USE maxparms + character*4 :: ncperts(ncmaxmem) = (/'AP01','AP02','AP03' + & ,'AP04','AP05','AP06','AP07','AP08','AP09','AP10' + & ,'AP11','AP12','AP13','AP14','AP15','AP16','AP17' + & ,'AP18','AP19','AP20'/) + + character*4 :: nrperts(nrmaxmem) = (/'GR01','GR02','GR03' + & ,'GR04','GR05','GR06','GR07','GR08','GR09','GR10' + & /) + +c character*4 :: ncperts(ncmaxmem) = (/'GE01','GE02','GE03' +c & ,'GE04','GE05','GE06','GE07','GE08','GE09','GE10' +c & ,'GE11','GE12','GE13','GE14','GE15','GE16','GE17' +c & ,'GE18','GE19','GE20'/) + + character*4 :: n0perts(n0maxmem) = (/'AP01','AP02','AP03' + & ,'AP04','AP05','AP06','AP07','AP08','AP09','AP10' + & ,'AP11','AP12','AP13','AP14','AP15','AP16','AP17' + & ,'AP18','AP19','AP20'/) + + character*4 :: h0perts(h0maxmem) = (/'HW01','HW02','HW03' + & ,'HW04','HW05','HW06','HW07','HW08','HW09','HW10' + & ,'HW11','HW12','HW13','HW14','HW15','HW16','HW17' + & ,'HW18','HW19','HW20'/) + + character*4 :: y0perts(y0maxmem) = (/'C01C','C02C','C03C' + & ,'C04C','C05C','C06C','C07C','C08C','C09C','C10C' + & /) + + character*4 :: d0perts(d0maxmem) = (/'GP00','GP01','GP02' + & ,'GP03','GP04','GP05','GP06','GP07','GP08','GP09' + & /) + + character*4 :: e0perts(e0maxmem) = (/'GP00','GP01','GP02' + & ,'GP03','GP04','GP05','GP06','GP07','GP08','GP09' + & ,'C01C','C02C','C03C' + & ,'C04C','C05C','C06C','C07C','C08C','C09C','C10C' + & ,'HW01','HW02','HW03' + & ,'HW04','HW05','HW06','HW07','HW08','HW09','HW10' + & ,'HW11','HW12','HW13','HW14','HW15','HW16','HW17' + & ,'HW18','HW19','HW20'/) + + character*4 :: e1perts(e1maxmem) = (/'GP00','GP01','GP02' + & ,'GP03','GP04','GP05','GP06','GP07','GP08','GP09' + & ,'HW01','HW02','HW03' + & ,'HW04','HW05','HW06','HW07','HW08','HW09','HW10' + & ,'HW11','HW12','HW13','HW14','HW15','HW16','HW17' + & ,'HW18','HW19','HW20'/) + + character*4 :: e2perts(e2maxmem) = (/'C01C','C02C','C03C' + & ,'C04C','C05C','C06C','C07C','C08C','C09C','C10C' + & ,'HW01','HW02','HW03' + & ,'HW04','HW05','HW06','HW07','HW08','HW09','HW10' + & ,'HW11','HW12','HW13','HW14','HW15','HW16','HW17' + & ,'HW18','HW19','HW20'/) + + character*4 :: e3perts(e3maxmem) = (/'GP00','GP01','GP02' + & ,'GP03','GP04','GP05','GP06','GP07','GP08','GP09' + & ,'C01C','C02C','C03C' + & ,'C04C','C05C','C06C','C07C','C08C','C09C','C10C' + & /) + + character*4 :: ukperts(ukmaxmem) = (/'UK01','UK02' + & ,'UK03','UK04','UK05','UK06','UK07','UK08','UK09','UK10' + & ,'UK11','UK12','UK13','UK14','UK15','UK16','UK17' + & ,'UK18','UK19','UK20','UK21','UK22','UK23'/) + + character*4 :: frperts(frmaxmem) = (/'FR01','FR02','FR03' + & ,'FR04','FR05','FR06','FR07','FR08','FR09','FR10' + & ,'FR11','FR12','FR13','FR14','FR15','FR16','FR17' + & ,'FR18','FR19','FR20','FR21','FR22','FR23','FR24' + & ,'FR25','FR26','FR27','FR28','FR29','FR30','FR31' + & ,'FR32','FR33','FR34'/) + +c#-------J.Peng----2010-09-29--------------------------------------------- + character*4 :: gcperts(gcmaxmem) = (/'NP01','NP02','NP03' + & ,'NP04','NP05','NP06','NP07','NP08','NP09','NP10' + & ,'NP11','NP12','NP13','NP14','NP15','NP16','NP17' + & ,'NP18','NP19','NP20'/) + character*4 :: g0perts(g0maxmem) = (/'FP01','FP02','FP03' + & ,'FP04','FP05','FP06','FP07','FP08','FP09','FP10' + & ,'FP11','FP12','FP13','FP14','FP15','FP16','FP17' + & ,'FP18','FP19','FP20'/) + + + character*4 :: ccperts(ccmaxmem) = (/'CP01','CP02','CP03' + & ,'CP04','CP05','CP06','CP07','CP08','CP09','CP10' + & ,'CP11','CP12','CP13','CP14','CP15','CP16','CP17' + & ,'CP18','CP19','CP20'/) + character*4 :: c0perts(c0maxmem) = (/'CP01','CP02','CP03' + & ,'CP04','CP05','CP06','CP07','CP08','CP09','CP10' + & ,'CP11','CP12','CP13','CP14','CP15','CP16','CP17' + & ,'CP18','CP19','CP20'/) + +cJ.Peng-------2010-10-29------------------------------------- + character*4 :: c2perts(c2maxmem) = (/'AP01','AP02','AP03' + & ,'AP04','AP05','AP06','AP07','AP08','AP09','AP10' + & ,'AP11','AP12','AP13','AP14','AP15','AP16','AP17' + & ,'AP18','AP19','AP20' + & ,'CP01','CP02','CP03' + & ,'CP04','CP05','CP06','CP07','CP08','CP09','CP10' + & ,'CP11','CP12','CP13','CP14','CP15','CP16','CP17' + & ,'CP18','CP19','CP20'/) + +cJ.Peng-------2012-04-27------------------------------------- + character*4 :: f2perts(f2maxmem) = (/'AP01','AP02','AP03' + & ,'AP04','AP05','AP06','AP07','AP08','AP09','AP10' + & ,'AP11','AP12','AP13','AP14','AP15','AP16','AP17' + & ,'AP18','AP19','AP20' + & ,'FP01','FP02','FP03' + & ,'FP04','FP05','FP06','FP07','FP08','FP09','FP10' + & ,'FP11','FP12','FP13','FP14','FP15','FP16','FP17' + & ,'FP18','FP19','FP20'/) + +cJ.Peng-------2010-11-02------------------------------------------- + character*4 :: c3perts(c3maxmem) = (/'AP01','AP02','AP03' + & ,'AP04','AP05','AP06','AP07','AP08','AP09','AP10' + & ,'AP11','AP12','AP13','AP14','AP15','AP16','AP17' + & ,'AP18','AP19','AP20' + & ,'CP01','CP02','CP03' + & ,'CP04','CP05','CP06','CP07','CP08','CP09','CP10' + & ,'CP11','CP12','CP13','CP14','CP15','CP16','CP17' + & ,'CP18','CP19','CP20' + & ,'EN01','EN02','EN03' + & ,'EN04','EN05','EN06','EN07','EN08','EN09','EN10' + & ,'EN11','EN12','EN13','EN14','EN15','EN16','EN17' + & ,'EN18','EN19','EN20','EN21','EN22','EN23','EN24' + & ,'EN25','EP01','EP02','EP03','EP04','EP05','EP06' + & ,'EP07','EP08','EP09','EP10','EP11','EP12','EP13' + & ,'EP14','EP15','EP16','EP17','EP18','EP19','EP20' + & ,'EP21','EP22','EP23','EP24','EP25'/) + + character*4 :: n2perts(n2maxmem) = (/'AP01','AP02','AP03' + & ,'AP04','AP05','AP06','AP07','AP08','AP09','AP10' + & ,'AP11','AP12','AP13','AP14','AP15','AP16','AP17' + & ,'AP18','AP19','AP20' + & ,'EN01','EN02','EN03' + & ,'EN04','EN05','EN06','EN07','EN08','EN09','EN10' + & ,'EN11','EN12','EN13','EN14','EN15','EN16','EN17' + & ,'EN18','EN19','EN20','EN21','EN22','EN23','EN24' + & ,'EN25','EP01','EP02','EP03','EP04','EP05','EP06' + & ,'EP07','EP08','EP09','EP10','EP11','EP12','EP13' + & ,'EP14','EP15','EP16','EP17','EP18','EP19','EP20' + & ,'EP21','EP22','EP23','EP24','EP25'/) + + character*4 :: m2perts(m2maxmem) = (/'01AI','02AI','03AI' + & ,'04AI','05AI','06AI','07AI','08AI','09AI','10AI' + & ,'11AI','12AI','13AI','14AI','15AI','16AI','17AI' + & ,'18AI','19AI','20AI' + & ,'01N2','02N2','03N2' + & ,'04N2','05N2','06N2','07N2','08N2','09N2','10N2' + & ,'11N2','12N2','13N2','14N2','15N2','16N2','17N2' + & ,'18N2','19N2','20N2','21N2','22N2','23N2','24N2' + & ,'25N2' + & ,'01P2','02P2','03P2','04P2','05P2','06P2' + & ,'07P2','08P2','09P2','10P2','11P2','12P2','13P2' + & ,'14P2','15P2','16P2','17P2','18P2','19P2','20P2' + & ,'21P2','22P2','23P2','24P2','25P2'/) + + character*4 :: f3perts(f3maxmem) = (/'AP01','AP02','AP03' + & ,'AP04','AP05','AP06','AP07','AP08','AP09','AP10' + & ,'AP11','AP12','AP13','AP14','AP15','AP16','AP17' + & ,'AP18','AP19','AP20' + & ,'CP01','CP02','CP03' + & ,'CP04','CP05','CP06','CP07','CP08','CP09','CP10' + & ,'CP11','CP12','CP13','CP14','CP15','CP16','CP17' + & ,'CP18','CP19','CP20' + & ,'FP01','FP02','FP03' + & ,'FP04','FP05','FP06','FP07','FP08','FP09','FP10' + & ,'FP11','FP12','FP13','FP14','FP15','FP16','FP17' + & ,'FP18','FP19','FP20'/) + + + character*4 :: c4perts(c4maxmem) = (/'AP01','AP02','AP03' + & ,'AP04','AP05','AP06','AP07','AP08','AP09','AP10' + & ,'AP11','AP12','AP13','AP14','AP15','AP16','AP17' + & ,'AP18','AP19','AP20' + & ,'CP01','CP02','CP03' + & ,'CP04','CP05','CP06','CP07','CP08','CP09','CP10' + & ,'CP11','CP12','CP13','CP14','CP15','CP16','CP17' + & ,'CP18','CP19','CP20' + & ,'EN01','EN02','EN03' + & ,'EN04','EN05','EN06','EN07','EN08','EN09','EN10' + & ,'EN11','EN12','EN13','EN14','EN15','EN16','EN17' + & ,'EN18','EN19','EN20','EN21','EN22','EN23','EN24' + & ,'EN25','EP01','EP02','EP03','EP04','EP05','EP06' + & ,'EP07','EP08','EP09','EP10','EP11','EP12','EP13' + & ,'EP14','EP15','EP16','EP17','EP18','EP19','EP20' + & ,'EP21','EP22','EP23','EP24','EP25' + & ,'FP01','FP02','FP03' + & ,'FP04','FP05','FP06','FP07','FP08','FP09','FP10' + & ,'FP11','FP12','FP13','FP14','FP15','FP16','FP17' + & ,'FP18','FP19','FP20'/) + + character*4 :: c5perts(c5maxmem) = (/'AP01','AP02','AP03' + & ,'AP04','AP05','AP06','AP07','AP08','AP09','AP10' + & ,'AP11','AP12','AP13','AP14','AP15','AP16','AP17' + & ,'AP18','AP19','AP20' + & ,'CP01','CP02','CP03' + & ,'CP04','CP05','CP06','CP07','CP08','CP09','CP10' + & ,'CP11','CP12','CP13','CP14','CP15','CP16','CP17' + & ,'CP18','CP19','CP20' + & ,'EN01','EN02','EN03' + & ,'EN04','EN05','EN06','EN07','EN08','EN09','EN10' + & ,'EN11','EN12','EN13','EN14','EN15','EN16','EN17' + & ,'EN18','EN19','EN20','EN21','EN22','EN23','EN24' + & ,'EN25','EP01','EP02','EP03','EP04','EP05','EP06' + & ,'EP07','EP08','EP09','EP10','EP11','EP12','EP13' + & ,'EP14','EP15','EP16','EP17','EP18','EP19','EP20' + & ,'EP21','EP22','EP23','EP24','EP25' + & ,'FP01','FP02','FP03' + & ,'FP04','FP05','FP06','FP07','FP08','FP09','FP10' + & ,'FP11','FP12','FP13','FP14','FP15','FP16','FP17' + & ,'FP18','FP19','FP20' + & ,'UK01','UK02','UK03' + & ,'UK04','UK05','UK06','UK07','UK08','UK09','UK10' + & ,'UK11','UK12','UK13','UK14','UK15','UK16','UK17' + & ,'UK18','UK19','UK20','UK21','UK22','UK23'/) + + character*4 :: c6perts(c6maxmem) = (/'AP01','AP02','AP03' + & ,'AP04','AP05','AP06','AP07','AP08','AP09','AP10' + & ,'AP11','AP12','AP13','AP14','AP15','AP16','AP17' + & ,'AP18','AP19','AP20' + & ,'CP01','CP02','CP03' + & ,'CP04','CP05','CP06','CP07','CP08','CP09','CP10' + & ,'CP11','CP12','CP13','CP14','CP15','CP16','CP17' + & ,'CP18','CP19','CP20' + & ,'EN01','EN02','EN03' + & ,'EN04','EN05','EN06','EN07','EN08','EN09','EN10' + & ,'EN11','EN12','EN13','EN14','EN15','EN16','EN17' + & ,'EN18','EN19','EN20','EN21','EN22','EN23','EN24' + & ,'EN25','EP01','EP02','EP03','EP04','EP05','EP06' + & ,'EP07','EP08','EP09','EP10','EP11','EP12','EP13' + & ,'EP14','EP15','EP16','EP17','EP18','EP19','EP20' + & ,'EP21','EP22','EP23','EP24','EP25' + & ,'FP01','FP02','FP03' + & ,'FP04','FP05','FP06','FP07','FP08','FP09','FP10' + & ,'FP11','FP12','FP13','FP14','FP15','FP16','FP17' + & ,'FP18','FP19','FP20' + & ,'UK01','UK02','UK03' + & ,'UK04','UK05','UK06','UK07','UK08','UK09','UK10' + & ,'UK11','UK12','UK13','UK14','UK15','UK16','UK17' + & ,'UK18','UK19','UK20','UK21','UK22','UK23' + & ,'GE01','GE02','GE03' + & ,'GE04','GE05','GE06','GE07','GE08','GE09','GE10' + & ,'GE11','GE12','GE13','GE14','GE15','GE16','GE17' + & ,'GE18','GE19','GE20'/) + + + character*4 :: n3perts(n3maxmem) = (/'AP01','AP02','AP03' + & ,'AP04','AP05','AP06','AP07','AP08','AP09','AP10' + & ,'AP11','AP12','AP13','AP14','AP15','AP16','AP17' + & ,'AP18','AP19','AP20' + & ,'EN01','EN02','EN03' + & ,'EN04','EN05','EN06','EN07','EN08','EN09','EN10' + & ,'EN11','EN12','EN13','EN14','EN15','EN16','EN17' + & ,'EN18','EN19','EN20','EN21','EN22','EN23','EN24' + & ,'EN25','EP01','EP02','EP03','EP04','EP05','EP06' + & ,'EP07','EP08','EP09','EP10','EP11','EP12','EP13' + & ,'EP14','EP15','EP16','EP17','EP18','EP19','EP20' + & ,'EP21','EP22','EP23','EP24','EP25' + & ,'UK01','UK02','UK03' + & ,'UK04','UK05','UK06','UK07','UK08','UK09','UK10' + & ,'UK11','UK12','UK13','UK14','UK15','UK16','UK17' + & ,'UK18','UK19','UK20','UK21','UK22','UK23'/) + + character*4 :: s3perts(s3maxmem) = (/'EEMN','2EMN'/) + character*4 :: p3perts(p3maxmem) = (/'EEMN','AEMN','CEMN'/) + + character*4 :: d3perts(d3maxmem) = (/'AVNO',' EMX',' CMC'/) + + character*4 :: d6perts(d6maxmem) = (/'AEMN','CEMN','EEMN' + & ,'AVNO',' CMC',' EMX'/) + + character*4 :: x6perts(x6maxmem) = (/'AEMN','CEMN','EEMN' + & ,'FEMN','UKMN','HPMN'/) + + character*4 :: d4perts(d4maxmem) = (/'AVNO',' EMX',' CMC' + & ,'3EMN'/) + + character*4 :: d2perts(d2maxmem) = (/'3EMN','3DET'/) + + + character*4 :: ecperts(ecmaxmem) = (/'EN01','EN02','EN03' + & ,'EN04','EN05','EN06','EN07','EN08','EN09','EN10' + & ,'EN11','EN12','EN13','EN14','EN15','EN16','EN17' + & ,'EN18','EN19','EN20','EN21','EN22','EN23','EN24' + & ,'EN25','EP01','EP02','EP03','EP04','EP05','EP06' + & ,'EP07','EP08','EP09','EP10','EP11','EP12','EP13' + & ,'EP14','EP15','EP16','EP17','EP18','EP19','EP20' + & ,'EP21','EP22','EP23','EP24','EP25'/) + + character*4 :: ezperts(ezmaxmem) = (/'EN01','EN02','EN03' + & ,'EN04','EN05','EN06','EN07','EN08','EN09','EN10' + & ,'EP01','EP02','EP03','EP04','EP05','EP06' + & ,'EP07','EP08','EP09','EP10'/) + + character*4 :: srperts(srmaxmem) = (/'EMC1','EMN1','EMN2' + & ,'EMN3','EMP1','EMP2','EMP3','NBC1','NBN1','NBN2' + & ,'NBN3','NBP1','NBP2','NBP3','NMC1','NMN1','NMN2' + & ,'NMN3','NMP1','NMP2','NMP3'/) + + integer :: ncfcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: nrfcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: n0fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: h0fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: y0fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: d0fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: e0fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: e1fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: e2fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: e3fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: ukfcsthrs(maxtimes) = (/0,12,24,36,48,60,72,84,96 + & ,108,120,132 + & ,144,156,168,180,192,204,216,228,240,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: frfcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + +c#-------J.Peng----2010-09-29--------------------------------------------- + integer :: gcfcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + integer :: g0fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: ccfcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + integer :: c0fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + +cJ.Peng-------2010-10-29------------------------------------- + integer :: c2fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: f2fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + +cJ.Peng-------2010-11-02------------------------------------------ +c integer :: c3fcsthrs(maxtimes) = (/0,12,24,36,48,60,72,84,96 +c & ,108,120,132,144,156,168,180,192,204,216,228,240 +c & ,999,999,999,999,999,999,999,999,999,999,999,999 +c & ,999,999,999,999,999,999,999,999,999,999,999,999 +c & ,999,999,999,999,999,999,999,999,999,999,999,999 +c & ,999,999,999,999,999,999,999,999/) + + integer :: c3fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: n2fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: m2fcsthrs(maxtimes) = (/0,12,24,36,48,60,72,84,96 + & ,108,120,132 + & ,144,156,168,180,192,204,216,228,240,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: f3fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: c4fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: c5fcsthrs(maxtimes) = (/0,12,24,36,48,60,72,84,96 + & ,108,120,132 + & ,144,156,168,180,192,204,216,228,240,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: c6fcsthrs(maxtimes) = (/0,12,24,36,48,60,72,84,96 + & ,108,120,132 + & ,144,156,168,180,192,204,216,228,240,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: n3fcsthrs(maxtimes) = (/0,12,24,36,48,60,72,84,96 + & ,108,120,132 + & ,144,156,168,180,192,204,216,228,240,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: s3fcsthrs(maxtimes) = (/0,12,24,36,48,60,72,84,96 + & ,108,120,132,144,156,168,180,192,204,216,228,240 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999/) + integer :: p3fcsthrs(maxtimes) = (/0,12,24,36,48,60,72,84,96 + & ,108,120,132,144,156,168,180,192,204,216,228,240 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999/) + + integer :: d3fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: d6fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: x6fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: d4fcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: d2fcsthrs(maxtimes) = (/0,12,24,36,48,60,72,84,96 + & ,108,120,132,144,156,168,180,192,204,216,228,240 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999/) + +c------J.Peng------2011-01-03---------------------------------------- +c integer :: ecfcsthrs(maxtimes) = (/0,12,24,36,48,60,72,84,96 +c & ,108,120,132,144,156,168,180,192,204,216,228,240 +c & ,999,999,999,999,999,999,999,999,999,999,999,999 +c & ,999,999,999,999,999,999,999,999,999,999,999,999 +c & ,999,999,999,999,999,999,999,999,999,999,999,999 +c & ,999,999,999,999,999,999,999,999/) + + integer :: ecfcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + integer :: ezfcsthrs(maxtimes) = (/0,6,12,18,24,30,36,42,48,54 + & ,60,66,72,78,84,90,96,102,108,114,120,126,132,138 + & ,144,150,156,162,168,174,180,186,192,198,204,210 + & ,216,222,228,234,240,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + + + integer :: srfcsthrs(maxtimes) = (/0,3,6,9,12,15,18,21,24,27 + & ,30,33,36,39,42,45,48,51,54,57,60,63,66,69 + & ,72,75,78,81,84,87,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999,999,999,999,999,999,999,999 + & ,999,999,999,999,999/) + character*2, save :: run_basin + end module ensinfo + + module grid_bounds + integer, parameter :: maxbasins = 11 +c---J.Peng-----2015-01-09--------------------------------------------- + character*2 :: basin(maxbasins) = (/'AL','EP','WP','CP' + & ,'AA','BB','SI','SP','SC','EC','AU'/) + real :: xmaxlat(maxbasins) = (/ 60.0, 60.0, 60.0, 60.0, 60.0 + & , 60.0, -1.0, -1.0,999.0,999.0,999.0/) + + real :: xminlat(maxbasins) = (/ 1.0, 1.0, 1.0, 1.0, 1.0 + & , 1.0,-60.0,-60.0,999.0,999.0,999.0/) + + real :: xmaxlon(maxbasins) = (/345.0,285.0,185.0,255.0,125.0 + & ,145.0,125.0,185.0,999.0,999.0,999.0/) + + real :: xminlon(maxbasins) = (/250.0,190.0, 90.0,160.0, 30.0 + & , 50.0, 30.0, 90.0,999.0,999.0,999.0/) + end module grid_bounds + + module trig_vals + real, save :: pi, dtr + real, save :: dtk = 111.1949 ! Dist (km) over 1 deg lat + ! using erad=6371.0e+3 + real, save :: erad = 6371.0e+3 ! Earth's radius (m) + real, save :: ecircum = 40030.2 ! Earth's circumference + ! (km) using erad=6371.e3 + real, save :: omega = 7.292e-5 + end module trig_vals + +c +c-------------------------------------------------------------------- +c + program trakave +c +c$$$ MAIN PROGRAM DOCUMENTATION BLOCK +c +c Main Program: TRAKAVE Get average of ensemble tracks +C PRGMMR: MARCHOK ORG: NP22 DATE: 2005-08-01 +c +c ABSTRACT: This program reads in the tropical cyclone track files +c (in "atcfunix" format) from the various ensemble perturbation +c members and produces an ensemble mean track. Currently, the +c program can handle members from the NCEP, ECMWF and CANADIAN +c ensembles. +c +c Program history log: +c 2005-08-01 Marchok - Original operational version. +c 2006-08-02 Marchok - Changed pertubation IDs for Canadian ens +c (they no longer have "negative" perts). +c 2007-08-08 Marchok - Added code for averaging SREF ensemble tracks. +c +c Input files: +c unit 11 Text file containing the concatenated atcfunix tracks +c with all storms for all ensemble members at all +c forecast hours. +c +c Output files: +c unit 52 Opened and closed internally, 1 file for each storm, +c this series of output files contains the gridded +c strike probabilities, both for the individual forecast +c hours as well as the accumulated probabilities. +c pt (1,1) is the far NW point on this output grid, +c pt (imax,jmax) is the far SE point on this output grid. +c unit 53 Output atcfunix file with mean ensemble track forecast +c positions for each storm at each forecast hour. +c unit 54 Output atcf file with mean ensemble track forecast +c positions for each storm forecast hours 12-72. +c unit 55 Output track file with mean ensemble track forecast +c positions for each storm forecast hours 0-72. +c unit 56 Output text file with ensemble track forecast spread +c values for each storm at each forecast hour. +c unit 57 Output text file with ensemble track forecast mode +c values for each storm at each forecast hour. +c +c Subprograms called: +c read_nlists Read input namelists for input date & storm number info +c +c Attributes: +c Language: Fortran90 +c +c Notes: +c maxmem This is the max number of members expected for a +c given Center's ensemble forecast +c minmem This is the minimum number of member tracks that need to +c exist at a given forecast hour in order to produce a mean +c +c +c$$$ +c +c------- +c + USE define_atcfunix_rec; USE maxparms; USE trig_vals + USE ensinfo + + type (atcfunix_card) saverec + + real,allocatable :: traklat(:,:,:) + real,allocatable :: traklon(:,:,:) +cJ.Peng-04-15-2013 + real,allocatable :: tcvmx(:,:,:) + real,allocatable :: tcpce(:,:,:) + + real xmeanlat(maxstorms,maxtimes) + real xmeanlon(maxstorms,maxtimes) +cJ.Peng-04-15-2013 + real xmeanvmx(maxstorms,maxtimes) + real xmeanpce(maxstorms,maxtimes) + + real xspread(maxstorms,maxtimes) + real dthresh + integer :: fcsthrs(maxtimes) + integer minmem,maxmem,fcst_interval + character*4,allocatable :: perts(:) + character*1 :: do_probs = 'y' + character*1 :: modeflag = 'y' + character*4 :: stormarr(maxstorms) + character*4 :: catcf,catcf_lc + character*5 :: cmodel +c + pi = 4. * atan(1.) ! Both pi and dtr were declared in module + dtr = pi/180.0 ! trig_vals, but were not yet defined. + + xmeanlat = -9999.0; xmeanlon = -9999.0 +cJ.Peng-04-15-2013 + xmeanvmx = -9999.0; xmeanpce = -9999.0 + + xspread = -999.0 + + call read_nlists (dthresh,cmodel) + + select case (cmodel) + case ('ens'); maxmem = ncmaxmem; fcsthrs(:) = ncfcsthrs(:) + case ('ref'); maxmem = nrmaxmem; fcsthrs(:) = nrfcsthrs(:) + + case ('ensb'); maxmem = n0maxmem; fcsthrs(:) = n0fcsthrs(:) + case ('hf21'); maxmem = h0maxmem; fcsthrs(:) = h0fcsthrs(:) + case ('ny10'); maxmem = y0maxmem; fcsthrs(:) = y0fcsthrs(:) + case ('gd10'); maxmem = d0maxmem; fcsthrs(:) = d0fcsthrs(:) + case ('egn3'); maxmem = e0maxmem; fcsthrs(:) = e0fcsthrs(:) + case ('egt2'); maxmem = e1maxmem; fcsthrs(:) = e1fcsthrs(:) + case ('ent2'); maxmem = e2maxmem; fcsthrs(:) = e2fcsthrs(:) + case ('gnt2'); maxmem = e3maxmem; fcsthrs(:) = e3fcsthrs(:) + + case ('ukes'); maxmem = ukmaxmem; fcsthrs(:) = ukfcsthrs(:) + + case ('fres'); maxmem = frmaxmem; fcsthrs(:) = frfcsthrs(:) + +c----------J.Peng----------2010-09-29------------------------------- +c case ('gve'); maxmem = gcmaxmem; fcsthrs(:) = gcfcsthrs(:) + case ('fens'); maxmem = gcmaxmem; fcsthrs(:) = gcfcsthrs(:) + case ('fenb'); maxmem = g0maxmem; fcsthrs(:) = g0fcsthrs(:) + + case ('cens'); maxmem = ccmaxmem; fcsthrs(:) = ccfcsthrs(:) + case ('cenb'); maxmem = c0maxmem; fcsthrs(:) = c0fcsthrs(:) + + +cJ.Peng-------2010-10-29--------------2010-11-02--------------- + case ('g2c'); maxmem = c2maxmem; fcsthrs(:) = c2fcsthrs(:) + + case ('naf2'); maxmem = f2maxmem; fcsthrs(:) = f2fcsthrs(:) + + case ('gce3'); maxmem = c3maxmem; fcsthrs(:) = c3fcsthrs(:) + case ('nae2'); maxmem = n2maxmem; fcsthrs(:) = n2fcsthrs(:) + case ('inae'); maxmem = m2maxmem; fcsthrs(:) = m2fcsthrs(:) + + case ('ncf3'); maxmem = f3maxmem; fcsthrs(:) = f3fcsthrs(:) + + case ('gce4'); maxmem = c4maxmem; fcsthrs(:) = c4fcsthrs(:) + case ('gce5'); maxmem = c5maxmem; fcsthrs(:) = c5fcsthrs(:) + + case ('gce6'); maxmem = c6maxmem; fcsthrs(:) = c6fcsthrs(:) + case ('neu3'); maxmem = n3maxmem; fcsthrs(:) = n3fcsthrs(:) + + + case ('gce3a'); maxmem = s3maxmem; fcsthrs(:) = s3fcsthrs(:) + case ('gce3b'); maxmem = p3maxmem; fcsthrs(:) = p3fcsthrs(:) + + case ('sing'); maxmem = d3maxmem; fcsthrs(:) = d3fcsthrs(:) + case ('tot6'); maxmem = d6maxmem; fcsthrs(:) = d6fcsthrs(:) + + case ('six6'); maxmem = x6maxmem; fcsthrs(:) = x6fcsthrs(:) + case ('tot4'); maxmem = d4maxmem; fcsthrs(:) = d4fcsthrs(:) + + case ('tot2'); maxmem = d2maxmem; fcsthrs(:) = d2fcsthrs(:) + + case ('eens'); maxmem = ecmaxmem; fcsthrs(:) = ecfcsthrs(:) + case ('eefs'); maxmem = ezmaxmem; fcsthrs(:) = ezfcsthrs(:) + + case ('sref'); maxmem = srmaxmem; fcsthrs(:) = srfcsthrs(:) + end select + + allocate (traklat(maxstorms,maxmem,maxtimes),stat=itla) + allocate (traklon(maxstorms,maxmem,maxtimes),stat=itlo) +cJ.Peng-04-15-2013 + allocate (tcvmx(maxstorms,maxmem,maxtimes),stat=itlp) + allocate (tcpce(maxstorms,maxmem,maxtimes),stat=itlq) + + allocate (perts(maxmem),stat=ipa) + if (itla/=0.or.itlo/=0.or.ipa/=0.or.itlp/=0.or.itlq/=0) then + print *,' ' + print *,'!!! ERROR in trakave allocating arrays.' + print *,'!!! itla = ',itla,' itlo= ',itlo,' ipa= ',ipa + stop 94 + endif + + traklat = -9999.0; traklon = -9999.0 +cJ.Peng-04-15-2013 + tcvmx = -9999.0 + tcpce = -9999.0 + + select case (cmodel) + case ('ens'); perts(:) = ncperts(:) + minmem = ncminmem + catcf = 'AEMN' + catcf_lc = 'aemn' + + case ('ref'); perts(:) = nrperts(:) + minmem = nrminmem + catcf = 'GRMN' + catcf_lc = 'grmn' + + case ('ensb'); perts(:) = n0perts(:) + minmem = n0minmem + catcf = 'AEMB' + catcf_lc = 'aemb' + + case ('hf21'); perts(:) = h0perts(:) + minmem = h0minmem + catcf = 'HEMN' + catcf_lc = 'hemn' + + case ('ny10'); perts(:) = y0perts(:) + minmem = y0minmem + catcf = 'YEMN' + catcf_lc = 'yemn' + + case ('gd10'); perts(:) = d0perts(:) + minmem = d0minmem + catcf = 'GEMN' + catcf_lc = 'gemn' + + case ('egn3'); perts(:) = e0perts(:) + minmem = e0minmem + catcf = 'MEMN' + catcf_lc = 'memn' + + case ('egt2'); perts(:) = e1perts(:) + minmem = e1minmem + catcf = 'GHMN' + catcf_lc = 'ghmn' + + case ('ent2'); perts(:) = e2perts(:) + minmem = e2minmem + catcf = 'HYMN' + catcf_lc = 'hymn' + + case ('gnt2'); perts(:) = e3perts(:) + minmem = e3minmem + catcf = 'GYMN' + catcf_lc = 'gymn' + + case ('ukes'); perts(:) = ukperts(:) + minmem = ukminmem + catcf = 'UKMN' + catcf_lc = 'ukmn' + + case ('fres'); perts(:) = frperts(:) + minmem = frminmem + catcf = 'FRMN' + catcf_lc = 'frmn' + +c case ('gve'); perts(:) = gcperts(:) +c minmem = gcminmem +c catcf = 'GVMN' +c catcf_lc = 'gvmn' +c-------J.Peng---------2010-09-29-------------------------- + case ('fens'); perts(:) = gcperts(:) + minmem = gcminmem +c catcf = 'FEMN' +c catcf_lc = 'femn' + catcf = 'NEMN' + catcf_lc = 'nemn' + + + case ('fenb'); perts(:) = g0perts(:) + minmem = g0minmem + catcf = 'FEMB' + catcf_lc = 'femb' + + + case ('cens'); perts(:) = ccperts(:) + minmem = ccminmem + catcf = 'CEMN' + catcf_lc = 'cemn' + + case ('cenb'); perts(:) = c0perts(:) + minmem = c0minmem + catcf = 'CEMB' + catcf_lc = 'cemb' + +cJ.Peng-------2010-10-29------------------------------------- + case ('g2c'); perts(:) = c2perts(:) + minmem = c2minmem + catcf = '2EMN' + catcf_lc = '2emn' + + case ('naf2'); perts(:) = f2perts(:) + minmem = f2minmem + catcf = '2NAF' + catcf_lc = '2naf' + +cJ.Peng-------2010-11-02----------------------------- + case ('gce3'); perts(:) = c3perts(:) + minmem = c3minmem + catcf = '3EMN' + catcf_lc = '3emn' + + case ('nae2'); perts(:) = n2perts(:) + minmem = n2minmem + catcf = '2NAE' + catcf_lc = '2nae' + + case ('inae'); perts(:) = m2perts(:) + minmem = m2minmem + catcf = 'NAEI' + catcf_lc = 'naei' + + case ('ncf3'); perts(:) = f3perts(:) + minmem = f3minmem + catcf = '3NCF' + catcf_lc = '3ncf' + + case ('gce4'); perts(:) = c4perts(:) + minmem = c4minmem + catcf = '4EMN' + catcf_lc = '4emn' + + case ('gce5'); perts(:) = c5perts(:) + minmem = c5minmem + catcf = '5EMN' + catcf_lc = '5emn' + + case ('gce6'); perts(:) = c6perts(:) + minmem = c6minmem + catcf = 'MIX6' + catcf_lc = 'mix6' + + case ('neu3'); perts(:) = n3perts(:) + minmem = n3minmem + catcf = '3NEU' + catcf_lc = '3neu' + + case ('gce3a'); perts(:) = s3perts(:) + minmem = s3minmem + catcf = 'SEMN' + catcf_lc = 'semn' + case ('gce3b'); perts(:) = p3perts(:) + minmem = p3minmem + catcf = 'PEMN' + catcf_lc = 'pemn' + + case ('sing'); perts(:) = d3perts(:) + minmem = d3minmem + catcf = '3DET' + catcf_lc = '3det' + case ('tot6'); perts(:) = d6perts(:) + minmem = d6minmem + catcf = '6EMN' + catcf_lc = '6emn' + + case ('six6'); perts(:) = x6perts(:) + minmem = x6minmem + catcf = 'SIXM' + catcf_lc = 'sixm' + + case ('tot4'); perts(:) = d4perts(:) + minmem = d4minmem + catcf = 'T4MN' + catcf_lc = 't4mn' + + case ('tot2'); perts(:) = d2perts(:) + minmem = d2minmem + catcf = 'S6MN' + catcf_lc = 's6mn' + + + case ('eens'); perts(:) = ecperts(:) + minmem = ecminmem + catcf = 'EEMN' + catcf_lc = 'eemn' + + case ('eefs'); perts(:) = ezperts(:) + minmem = ezminmem + catcf = 'TEMN' + catcf_lc = 'temn' + + case ('sref'); perts(:) = srperts(:) + minmem = srminmem + catcf = 'SFMN' + catcf_lc = 'sfmn' + end select + + fcst_interval = fcsthrs(2) - fcsthrs(1) + +cJ.Peng-04-15-2013 + call read_atcfunix (traklat,traklon,tcvmx,tcpce, + & saverec,stormarr,maxmem,fcst_interval,perts) + + do i = 1,maxstorms + print *,'i= ',i,' stormarr(i)= ',stormarr(i) + enddo + +cJ.Peng-04-15-2013 + call get_mean_track (traklat,traklon,xmeanlat,xmeanlon + & ,tcvmx,tcpce,xmeanvmx,xmeanpce + & ,stormarr,saverec,maxmem,minmem,catcf,fcsthrs) + + call get_spread (traklat,traklon,xmeanlat,xmeanlon,xspread + & ,stormarr,saverec,maxmem,minmem,fcsthrs) + + if (do_probs == 'y') then + call get_probs (traklat,traklon,xmeanlat,xmeanlon + & ,stormarr,saverec%atx_ymdh,dthresh,'indiv' + & ,modeflag,catcf,maxmem,fcsthrs,catcf_lc) + call get_probs (traklat,traklon,xmeanlat,xmeanlon + & ,stormarr,saverec%atx_ymdh,dthresh,'accum' + & ,modeflag,catcf,maxmem,fcsthrs,catcf_lc) + endif + + print *,' ' + print *,'NORMAL END TO TRAKAVE....' + print *,' ' + + deallocate (traklat,stat=ictla) + deallocate (traklon,stat=ictlo) + +cJ.Peng-04-15-2013 + deallocate (tcvmx,stat=ictlp) + deallocate (tcpce,stat=ictlq) + + deallocate (perts,stat=icpa) + + if (ictla/=0.or.ictlo/=0.or.icpa/=0.or.ictlp/=0.or.ictlq/=0) then + print *,' ' + print *,'!!! ERROR in trakave deallocating arrays.' + print *,'!!! ictla = ',ictla,' ictlo= ',ictlo,' icpa= ',icpa + stop 94 + endif + + stop + end + +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine get_probs (traklat,traklon,xmeanlat,xmeanlon + & ,stormarr,cymdh,dthresh,prob_type + & ,modeflag,catcf,maxmem,fcsthrs,catcf_lc) +c +c ABSTRACT: This subroutine calculates the probability of a +c given location (gridpoint) being within a specified distance +c of an ensemble member trackpoint. For the domain, we'll sort +c through all the track points and get the most extreme +c N, E, S and W points, then add a certain amount onto that on +c each side. Then, for all the gridpoints that are included, +c we'll go one by one to each gridpoint and calculate the +c distances from each forecast track point at that hour to +c each gridpoint. If that distance is less than the specified +c distance threshold, we add it to the total, and then divide +c that total count by the total number of members to get the +c probability at that gridopoint for that forecast hour. +c +c NOTE: This particular subroutine can calculate the +c probabilities individually for each forecast hour or for +c the total accumulated probability over the forecast, +c depending on the value of the input variable prob_type. +c If we are doing this for individual cases, then we will +c also call get_prob_mode in order to get the ensemble +c probability mode and print it out in a separate atcfunix +c record. + + + USE maxparms; USE grid_bounds; USE trig_vals; USE ensinfo + + real traklat(maxstorms,maxmem,maxtimes) + real traklon(maxstorms,maxmem,maxtimes) + real xmeanlat(maxstorms,maxtimes) + real xmeanlon(maxstorms,maxtimes) + real, allocatable :: xprob(:,:) + real :: xnm_per_degree = 60.0 + real xdist,dthresh,xlondiff,xlatdiff,xinterplon,xinterplat + character*1 :: modeflag,hold_input_modeflag + character*2 :: cbasin + character*4 :: stormarr(maxstorms),cstorm,catcf,catcf_lc + character*5 :: prob_type + character*10 cymdh + character*12 cacc + character*46 outgrfile + character*33 outtxtfile + character cthresh1*1,cthresh2*2,cthresh3*3,cthresh4*4 + integer :: fcsthrs(maxtimes) + integer basinix,ifh_interval,ibct + integer, allocatable :: accum_ct(:,:) + integer max_accum_prob_hour + logical(1),allocatable :: gridpoint_already_hit(:,:,:) +c +c outtxtfile = 'enstrkprob.'//cymdh//'.ctlinfo.txt' +c open (unit=51,file=outtxtfile,status='unknown') + + if (catcf_lc == 'sfmn') then + max_accum_prob_hour = 87 ! SREF max fcst hour = 87 in Aug 2007 + else +c--------J.Peng----2011-01-11--------------------------------------- + max_accum_prob_hour = 168 ! For all others, go out to 120h + endif + + hold_input_modeflag = modeflag + + stormloop: do ist = 1,maxstorms + + modeflag = hold_input_modeflag + + ! As of June, 2002, we will only compute these probabilities + ! for storms in the AL, EP and WP basins. This next code + ! checks for those basin ID's and will cycle the stormloop + ! if it's not one of those basins.... + + cbasin = stormarr(ist)(1:2) +c--------J.Peng----2015-01-09--------------------------------- + if (cbasin == 'AL' .or. cbasin == 'EP' .or. cbasin == 'WP' + & .or. cbasin == 'CP' .or. cbasin == 'AA' .or. cbasin == 'BB' + & .or. cbasin == 'SI' .or. cbasin == 'SP' )then + + continue + else + print *,' ' + print *,'+++ NOTE: In get_probs, we are NOT calculating' + print *,'+++ the probabilities for storm ' + & ,stormarr(ist) + print *,'+++ It is not in a basin we do probs for.' + print *,' ' + cycle stormloop + endif + + ! Get the index for the basin we're in. + + basinix = -99 + ibct = 1 + basinloop: do while (ibct <= maxbasins) + if (cbasin == basin(ibct)) then + basinix = ibct + exit basinloop + endif + ibct = ibct + 1 + enddo basinloop + if (basinix == -99) then + print *,' ' + print *,'!!! ERROR IN GET_PROBS. BASIN IS NOT ' + print *,'!!! RECOGNIZED. BASIN = ',cbasin + print *,'!!! EXITING....' + stop 99 + endif + + ! In an older version of this program, we narrowed the area + ! over which we computed the probabilities to keep the file + ! size smaller, but that meant that each output file had + ! different starting and ending points. In this new version + ! we just calculate it over the whole basin. + + maxlat = int(xmaxlat(basinix)) + maxlon = int(xmaxlon(basinix)) + minlat = int(xminlat(basinix)) + minlon = int(xminlon(basinix)) + + numlat = abs(maxlat-minlat) + 1 + numlon = abs(maxlon-minlon) + 1 + + itemp = int(dthresh) + if (itemp < 10) then + write (cthresh1,'(i1)') itemp + outgrfile = catcf_lc//'.trkprob.'//stormarr(ist)//'.'// + & cthresh1//'nm.'//cymdh//'.'//prob_type//'.ieee' + else if (itemp >= 10 .and. itemp < 100) then + write (cthresh2,'(i2)') itemp + outgrfile = catcf_lc//'.trkprob.'//stormarr(ist)//'.'// + & cthresh2//'nm.'//cymdh//'.'//prob_type//'.ieee' + else if (itemp >= 100 .and. itemp < 1000) then + write (cthresh3,'(i3)') itemp + outgrfile = catcf_lc//'.trkprob.'//stormarr(ist)//'.'// + & cthresh3//'nm.'//cymdh//'.'//prob_type//'.ieee' + else if (itemp >= 1000) then + write (cthresh4,'(i4)') itemp + outgrfile = catcf_lc//'.trkprob.'//stormarr(ist)//'.'// + & cthresh4//'nm.'//cymdh//'.'//prob_type//'.ieee' + endif + + print *,' ' + print *,'Before open 52, stormarr(ist) = ',stormarr(ist) + print *,' ' + + if (xmeanlon(ist,1) > 0.0) then + print *,' ' + print *,'!!! OPEN: ' + print *,' open for prob_type = ',prob_type + print *,' ist = ',ist,' xmeanlon = ',xmeanlon(ist,1) + print *,' file name = ',outgrfile + print *,' dthresh = ',dthresh + inquire (52,access=cacc) + print *,' cacc 1 = ',cacc + open (unit=52,file=outgrfile,access='direct' + & ,form='unformatted',iostat=ios + & ,status='unknown',recl=numlat*numlon*4) + print *,' ios = ',ios + inquire (52,access=cacc) + print *,' cacc 2 = ',cacc + else + cycle stormloop + endif + + cstorm = stormarr(ist) + + if (prob_type == 'indiv') then + write (51,82) stormarr(ist),cymdh,numlon,minlon,numlat,minlat + endif + + 82 format (a4,1x,a10,' nx= ',i4,' beglon= ',i3 + & ,' ny= ',i4,' beglat= ',i3) + + ! Allocate the xprob array. Pt (1,1) is in the far NW part + ! of the subgrid. + + if (.not.allocated(xprob)) then + allocate (xprob(numlon,numlat),stat=ipa) + if (ipa /= 0) then + print *,' ' + print *,'!!! ERROR in sub get_probs allocating xprob array.' + print *,'!!! ipa = ',ipa + igpret = 94 + return + endif + endif + + if (.not.allocated(gridpoint_already_hit)) then + allocate (gridpoint_already_hit(numlon,numlat,maxmem) + & ,stat=iga) + if (iga /= 0) then + print *,' ' + print *,'!!! ERROR in sub get_probs allocating ' + print *,'!!! gridpoint_already_hit array.' + print *,'!!! iga = ',iga + igpret = 94 + return + endif + endif + + if (.not.allocated(accum_ct)) then + allocate (accum_ct(numlon,numlat),stat=iaa) + if (iaa /= 0) then + print *,' ' + print *,'!!! ERROR in sub get_probs allocating ' + print *,'!!! accum_ct array.' + print *,'!!! iaa = ',iaa + igpret = 94 + return + endif + endif + + ! Now go through each forecast time and get the + ! probabilities at each grid point. We check the xmeanlon + ! at each forecast time to make sure that we still have a + ! mean track (i.e., that the forecast isn't over or that + ! the storm didn't dissipate). + ! + ! Note below that the indexing of the xprob array will go + ! so that pt (1,1) is in the upper left (most NW) corner + ! of the subgrid, and point (maxlon,maxlat) is in the + ! lower right (most SE) corner of the subgrid. + + print *,' ' + print *,' ' + print *,'++++++++++++++++++++++++ ' + print *,' +++ NEW STORM +++' + print *,'++++++++++++++++++++++++ ' + print *,'ist= ',ist,' maxlat= ',maxlat,' minlat= ',minlat + print *,' ' + +c--------J.Peng-----2010-10-26--------------------- + gridpoint_already_hit = .false. + accum_ct = 0 + + ifhloop: do ifh = 1,maxtimes + + ifhour = fcsthrs(ifh) + print *,' ' + print *,'++++++++++++++++++++++++ ' + print *,' +++ NEW TIME +++' + print *,'++++++++++++++++++++++++ ' + print *,'ifh= ',ifh,' ist= ',ist,' forecast hour = ',ifhour + + if (ifhour > max_accum_prob_hour .and. prob_type == 'accum') + & then + print *,' ' + print *,'!!! NOT CALCULATING ACCUM PROBS PAST ' + & ,max_accum_prob_hour,'hours' + exit ifhloop + endif + + if (xmeanlon(ist,ifh) > 0.0) then ! valid mean fcst at ifh + + xprob = 0.0 + latloop: do jlat = maxlat,minlat,-1 + +c print *,'jlat= ',jlat + + jix = maxlat - jlat + 1 + ylat = float(jlat) + + lonloop: do ilon = minlon,maxlon + +c print *,'ilon= ',ilon + + iix = ilon - minlon + 1 + xlon = float(ilon) + + ict = 0 + ipt = 0 + memberloop: do im = 1,maxmem + + ict = ict + 1 + if (traklon(ist,im,ifh) > 0.0) then + call calcdist (xlon,ylat,traklon(ist,im,ifh) + & ,traklat(ist,im,ifh),dtr,xdist) + if (xdist <= dthresh) then + if (prob_type == 'accum') then +c--------J.Peng-----2010-10-26--------------------- + if (gridpoint_already_hit(iix,jix,im)) then + continue + else + accum_ct(iix,jix) = accum_ct(iix,jix) + 1 + gridpoint_already_hit(iix,jix,im) = .true. + endif +c accum_ct(iix,jix) = accum_ct(iix,jix) + 1 + else + ipt = ipt + 1 +c print *,'++YES, dist= ',xdist,' ipt= ',ipt + endif + endif + endif + + ! * * * * * * * * * * * * * * * * * * * * * * * * * * + ! INTERPOLATE TRACKS FOR "ACCUMULATED" PROBABILITIES: + ! For accum probs, interpolate the tracks in between + ! time periods to get a smooth track and not miss + ! any "in-between" points. Don't need to do this for + ! the individual, instantaneous track probs at each + ! forecast hour. Currently, since TPC only does their + ! accumulated probabilities out to 120h, we will only + ! do ours to 120h as well, so don't interpolate if the + ! the next forecast hour would be > 120. + ! * * * * * * * * * * * * * * * * * * * * * * * * * * + + if (prob_type == 'accum' .and. ifh < maxtimes .and. + & fcsthrs(ifh+1) <= max_accum_prob_hour) then + if (traklon(ist,im,ifh) > 0.0 .and. + & traklon(ist,im,ifh+1) > 0.0) then + ifh_interval = fcsthrs(ifh+1) - fcsthrs(ifh) + xlondiff = traklon(ist,im,ifh+1) - + & traklon(ist,im,ifh) + xlatdiff = traklat(ist,im,ifh+1) - + & traklat(ist,im,ifh) + interploop: do iint = 1,ifh_interval-1 + xinterplon = traklon(ist,im,ifh) + + & (float(iint)/float(ifh_interval) * + & xlondiff) + xinterplat = traklat(ist,im,ifh) + + & (float(iint)/float(ifh_interval) * + & xlatdiff) + call calcdist (xlon,ylat,xinterplon + & ,xinterplat,dtr,xdist) + if (xdist <= dthresh) then +c--------J.Peng-----2010-10-26--------------------- + if (gridpoint_already_hit(iix,jix,im)) then + continue + else + accum_ct(iix,jix) = accum_ct(iix,jix) + 1 + gridpoint_already_hit(iix,jix,im) = .true. + endif +c accum_ct(iix,jix) = accum_ct(iix,jix) + 1 + endif + enddo interploop + endif + endif + + enddo memberloop + + if (maxmem > 0) then +c print *,'xprob: ipt= ',ipt,' ict= ',ict,' prob= ' +c & ,float(ipt)/float(maxmem) + xprob(iix,jix) = float(ipt)/float(maxmem) + endif + + enddo lonloop + + enddo latloop + +c If the probability type is for an individual forecast hour, +c then write out the data right now for this hour, and then +c if the modeflag is set to find the probability mode, call +c the subroutine get_prob_mode. + + if (prob_type == 'indiv') then +c do iii = 1,numlon +c do jjj = 1,numlat +c if (xprob(iii,jjj) > 0.0) then +c print *,'HEY, iii= ',iii,' jjj= ',jjj,' xprob= ' +c & ,xprob(iii,jjj) +c endif +c enddo +c enddo +cccccc-------J.Peng.------2010-09-30-------------------------------- + print *,'JPENG_ifh=',ifh + write (52,rec=ifh) ((xprob(i,j),i=1,numlon),j=1,numlat) + if (modeflag == 'y') then + call get_prob_mode (numlon,numlat,minlon,maxlon,minlat + & ,maxlat,xprob,xmodelon,xmodelat,xmodeval,ifhour + & ,cstorm,cymdh,ifh,dthresh,xmeanlon,xmeanlat,ist + & ,basinix,catcf,fcsthrs,modeflag) + endif + endif + + else + + cycle ifhloop + + endif + + enddo ifhloop + + if (prob_type == 'accum') then + jloop: do jlat = maxlat,minlat,-1 + jix = maxlat - jlat + 1 + ylat = float(jlat) + iloop: do ilon = minlon,maxlon + iix = ilon - minlon + 1 + xlon = float(ilon) + xprob(iix,jix) = float(accum_ct(iix,jix)) / + & float(maxmem) +c print *,'jix= ',jix,' iix= ',iix,' xprob= ',xprob(iix,jix) +c write (61,120) jix,iix,float(jlat),float(ilon) +c & ,xprob(iix,jix) + enddo iloop + enddo jloop + irec=1 + write (52,rec=irec) ((xprob(i,j),i=1,numlon),j=1,numlat) + endif + + deallocate (xprob,stat=icxa) + if (icxa /= 0) then + print *,' ' + print *,'!!! ERROR in trakave deallocating xprob array.' + print *,'!!! icxa = ',icxa + stop 94 + endif + + close (52) + + enddo stormloop + + 120 format (1x,'jix= ',i3,' iix= ',i3,' jlat= ',f6.1,' ilon= ' + & ,f6.1,' xprob= ',f6.2) + + icgaha = 0 + icaca = 0 + + if (allocated(gridpoint_already_hit)) then + deallocate (gridpoint_already_hit,stat=icgaha) + endif + + if (allocated(accum_ct)) then + deallocate (accum_ct,stat=icaca) + endif + + if (icgaha /= 0 .or. icaca /= 0) then + print *,' ' + print *,'!!! ERROR in trakave deallocating gridpoint' + print *,'!!! or accum_ct arrays.' + print *,'!!! icgaha = ',icgaha,' icaca= ',icaca + stop 94 + endif +c + return + end + +c +c---------------------------------------------------------------- +c +c---------------------------------------------------------------- + subroutine get_mean_track (traklat,traklon,xmeanlat,xmeanlon + & ,tcvmx,tcpce,xmeanvmx,xmeanpce + & ,stormarr,saverec,maxmem,minmem,catcf,fcsthrs) + +c ABSTRACT: This subroutine calculates the mean lat and lon +c positions for each storm at each forecast hour. maxmem is +c the maximum number of members for a given Center's forecast, +c while minmem is the minimum number of member tracks that must +c exist at a given forecast hour in order to produce a forecast +c track. Currently, I have this set at ~40% of maxmem, so for +c NCEP (maxmem=10,minmem=4), CMC (maxmem=16,minmem=6), +c ECMWF (maxmem=50,minmem=20) + + USE define_atcfunix_rec; USE maxparms + USE trig_vals + + type (atcfunix_card) saverec + + real traklat(maxstorms,maxmem,maxtimes) + real traklon(maxstorms,maxmem,maxtimes) + real xmeanlat(maxstorms,maxtimes) + real xmeanlon(maxstorms,maxtimes) +cJ.Peng-04-15-2013---- + real tcvmx(maxstorms,maxmem,maxtimes) + real tcpce(maxstorms,maxmem,maxtimes) + real xmeanvmx(maxstorms,maxtimes) + real xmeanpce(maxstorms,maxtimes) + real stdvmx(maxstorms,maxtimes) + real stdpce(maxstorms,maxtimes) + real yspread(maxstorms,maxtimes) + + integer :: fcsthrs(maxtimes) + integer pairct + character*4 :: stormarr(maxstorms) + character*4 :: catcf + yspread =-999.0 +c + print *,' ' + print *,'************************** ' + print *,'*** In get_mean_track.... ' + print *,'************************** ' + print *,' ' + + stormloop: do ist = 1,maxstorms + + print *,' ' + print *,'-------------------' + print *,'storm number = ',ist + print *,'storm name = ',stormarr(ist) + print *,' ' + + timeloop: do ifh = 1,maxtimes + + ict = 0 + xlatsum = 0.0 + xlonsum = 0.0 + +cJ.Peng-04-15-2013----- + xvmxsum = 0.0 + xpcesum = 0.0 + + print *,' ' + print *,' time index (ifh) = ',ifh + print *,' ' + + do imem = 1,maxmem + if (traklon(ist,imem,ifh) > 0.0) then + ict = ict + 1 + xlatsum = xlatsum + traklat(ist,imem,ifh) +cJ.Peng------03/27/2015----------------------------------- +c xlonsum = xlonsum + traklon(ist,imem,ifh) + if(traklon(ist,imem,ifh) < 30.0) then + xlonsum = xlonsum + traklon(ist,imem,ifh)+360.0 + else + xlonsum = xlonsum + traklon(ist,imem,ifh) + endif +cJ.Peng------03/27/2015----------------------------------- + +cJ.Peng-04-15-2013----- + xvmxsum = xvmxsum + tcvmx(ist,imem,ifh) + xpcesum = xpcesum + tcpce(ist,imem,ifh) + + print *,'mem= ',imem,' lat= ',traklat(ist,imem,ifh) + & ,' lon= ',traklon(ist,imem,ifh) + else + print *,'mem= ',imem,' lat= ',traklat(ist,imem,ifh) + & ,' lon= ',traklon(ist,imem,ifh) + endif + enddo + + +c pairct = 0 +c if (traklon(ist,1,ifh) > 0.0 .and. traklon(ist,6,ifh) > 0.0) +c & then +c pairct = pairct + 1 +c endif +c if (traklon(ist,2,ifh) > 0.0 .and. traklon(ist,7,ifh) > 0.0) +c & then +c pairct = pairct + 1 +c endif +c if (traklon(ist,3,ifh) > 0.0 .and. traklon(ist,8,ifh) > 0.0) +c & then +c pairct = pairct + 1 +c endif +c if (traklon(ist,4,ifh) > 0.0 .and. traklon(ist,9,ifh) > 0.0) +c & then +c pairct = pairct + 1 +c endif +c if (traklon(ist,5,ifh) > 0.0 .and. traklon(ist,10,ifh) > 0.0) +c & then +c pairct = pairct + 1 +c endif +c if (ict > 2 .and. pairct >= 2) then + + + if (ict >= minmem) then + xmeanlat(ist,ifh) = xlatsum / ict + xmeanlon(ist,ifh) = xlonsum / ict + print *,'MEAN: LAT= ',xmeanlat(ist,ifh) + & ,' LON= ',xmeanlon(ist,ifh) + +cJ.Peng-04-15-2013----------- + xmeanvmx(ist,ifh) = xvmxsum / ict + xmeanpce(ist,ifh) = xpcesum / ict +cJ.Peng-04-15-2013---spread for intensity----wind and SLP + jct = 0 + svmxsum = 0.0 + spcesum = 0.0 + do imem = 1,maxmem + if (traklon(ist,imem,ifh) > 0.0) then + jct = jct + 1 + svmxsum = svmxsum + + & (tcvmx(ist,imem,ifh)-xmeanvmx(ist,ifh))**2 + spcesum = spcesum + + & (tcpce(ist,imem,ifh)-xmeanpce(ist,ifh))**2 + endif + enddo + if (jct >= minmem) then + stdvmx(ist,ifh) = sqrt(svmxsum/jct) + stdpce(ist,ifh) = sqrt(spcesum/jct) + endif +cJ.Peng-04-15-2013---spread for track------------------ + xdistsum = 0.0 + imemct = 0 + do imem = 1,maxmem + if (traklon(ist,imem,ifh) > 0.0) then + call calcdist (xmeanlon(ist,ifh),xmeanlat(ist,ifh) + & ,traklon(ist,imem,ifh),traklat(ist,imem,ifh) + & ,dtr,xdist) + xdistsum = xdistsum + xdist + imemct = imemct + 1 + endif + enddo + if (imemct >= minmem) then + yspread(ist,ifh) = xdistsum / float(imemct) + endif +c--------------------------------------------------------------- +c call output_atcfunix (stormarr(ist),saverec%atx_ymdh +c & ,xmeanlat(ist,ifh),xmeanlon(ist,ifh),ifh,'mean' +c & ,-99.0,-99.0,catcf,fcsthrs) +cJ.Peng-04-15-2013--------- + call output_atcfunix1 (stormarr(ist),saverec%atx_ymdh + & ,xmeanlat(ist,ifh),xmeanlon(ist,ifh) + & ,stdvmx(ist,ifh),stdpce(ist,ifh),yspread(ist,ifh) + & ,xmeanvmx(ist,ifh),xmeanpce(ist,ifh),ifh,'mean' + & ,-99.0,-99.0,catcf,fcsthrs) + +c else if (ict > 0 .and. ict <= 2) then + else if (ict > 0 .and. ict < minmem) then + xmlat = xlatsum / ict + xmlon = xlonsum / ict + print *,' ' + print *,'!!! NOT ENOUGH MEMBERS FOR MEAN FOR STORM #',ist + print *,'!!! STORM NAME = ',stormarr(ist) + print *,'!!! MODEL NAME = ',catcf + print *,'!!! NUMBER OF MEMBERS = ',ict,' TIME INDEX = ',ifh + print *,'!!! MINIMUM NUMBER OF MEMBERS NEEDED= ',minmem + print *,'!!! MEAN POSITION WOULD HAVE BEEN: ' + print *,'!!! LAT: ',xmlat,' LON: ',xmlon + else + print *,'!!! UNDEFINED MEAN POSITION !!!' + endif + + enddo timeloop +c------J.Peng---------2010-09-29------------------------------------ +c if (xmeanlon(ist,1) > 0.0) then +c call output_atcf (stormarr(ist),xmeanlon,xmeanlat +c & ,saverec%atx_ymdh,ist,catcf) +c call output_all (stormarr(ist),xmeanlon,xmeanlat +c & ,saverec%atx_ymdh,ist,catcf) +c & +c endif + + enddo stormloop + + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_spread (traklat,traklon,xmeanlat,xmeanlon + & ,xspread,stormarr,saverec,maxmem,minmem,fcsthrs) +c +c ABSTRACT: This subroutine calculates the spread of the +c ensemble track forecasts, defined as the average distance +c of the ensemble members to the mean track. + + USE trig_vals; USE maxparms; USE define_atcfunix_rec + USE ensinfo + + type (atcfunix_card) saverec + + real traklat(maxstorms,maxmem,maxtimes) + real traklon(maxstorms,maxmem,maxtimes) + real xmeanlat(maxstorms,maxtimes) + real xmeanlon(maxstorms,maxtimes) + real xspread(maxstorms,maxtimes) + real xdistsum,xdist + integer :: fcsthrs(maxtimes) + integer imemct,ict,ifh_interval,ifhour + character*4 :: stormarr(maxstorms) +c + print *,' ' + print *,'************************** ' + print *,'*** In get_spread.... ' + print *,'************************** ' + print *,' ' + + ifh_interval = fcsthrs(2) - fcsthrs(1) + + stormloop: do ist = 1,maxstorms + + print *,' ' + print *,'-------------------' + print *,'Spread calculation ' + print *,'storm number = ',ist + print *,'storm name = ',stormarr(ist) + print *,' ' + + timeloop: do ifh = 1,maxtimes + + ifhour = (ifh - 1) * ifh_interval + + print *,' ' + print *,' time index (ifh) = ',ifh,' fhour= ' + & ,ifhour + print *,' ' + + if (xmeanlat(ist,ifh) > -999.0) then + + ! We have a valid mean for this storm and tau + + xdistsum = 0.0 + imemct = 0 + + write (*,81) 'MEAN: LAT= ',xmeanlat(ist,ifh) + & ,' LON= ',xmeanlon(ist,ifh) + + do imem = 1,maxmem + if (traklon(ist,imem,ifh) > 0.0) then + call calcdist (xmeanlon(ist,ifh),xmeanlat(ist,ifh) + & ,traklon(ist,imem,ifh),traklat(ist,imem,ifh) + & ,dtr,xdist) + write (*,83) 'pert: LAT= ',traklat(ist,imem,ifh) + & ,' LON= ',traklon(ist,imem,ifh),' pert= ',imem + & ,' xdist= ',xdist,' nm' + xdistsum = xdistsum + xdist + imemct = imemct + 1 + endif + enddo + + if (imemct >= minmem) then + xspread(ist,ifh) = xdistsum / float(imemct) + write (*,85) 'SPREAD: ',xspread(ist,ifh),' nmem= ' + & ,imemct,' ifh= ',ifh + write (56,95) stormarr(ist),saverec%atx_ymdh,ifhour + & ,'SPREAD: ',xspread(ist,ifh),' nmem= ',imemct + & ,' ifh= ',ifh + else + xspread(ist,ifh) = -999.0 + write (*,87) 'SPREAD: NOT ENOUGH MEMBERS, nmem= ',imemct + write (56,95) stormarr(ist),saverec%atx_ymdh,ifhour + & ,'SPREAD: ',-999.0,' nmem= ',imemct + & ,' ifh= ',ifh + endif + + else + + write (*,89) 'SPREAD: No mean track for this time' + write (56,95) stormarr(ist),saverec%atx_ymdh,ifhour + & ,'SPREAD: ',-999.0,' nmem= ',0 + & ,' ifh= ',ifh + + endif + + enddo timeloop + + if (xmeanlat(ist,1) > -999.0) then + ! We have at least a valid initial time for this storm, so we + ! should print out the spread array.... + write (65,91) saverec%atx_ymdh,stormarr(ist) + & ,(xspread(ist,ifh),ifh=1,8) + endif + + enddo stormloop + + 81 format (1x,a12,f7.2,2x,a6,f7.2) + 83 format (1x,a12,f7.2,2x,a6,f7.2,2x,a8,i2,2x,a9,f7.2,a3) + 85 format (1x,a8,f7.2,2x,a8,i2,a7,i2) + 87 format (1x,a34,i2) + 89 format (1x,a35) + 91 format (1x,a10,3x,a4,8(3x,f7.2)) + 95 format (1x,a4,2x,a10,2x,'fhour= ',i3,3x,a8,f8.2,2x,a8,i2,a7,i2) + + return + end +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_prob_mode (numlon,numlat,minlon,maxlon,minlat + & ,maxlat,xprob,xmodelon,xmodelat,xmodeval,ifhour + & ,cstorm,cymdh,ifh,dthresh,xmeanlon,xmeanlat,ist + & ,basinix,catcf,fcsthrs,modeflag) +c +c ABSTRACT: This subroutine will scan the array of probabilities +c in order to find the highest probability, or mode. Since there +c is a very good chance that you may have bimodal or multimodal +c cases in which there is more than one probability mode, we need +c a way to select only one mode. A barnes analysis, used for +c conducting the search, effectively applies a gaussian filter +c to provide a sort of area-averaged value. This will help to +c highlight one area among any other similar areas. +c +c re input e-folding radius for barnes analysis +c ri input influence radius for searching for min/max + + USE maxparms; USE trig_vals; USE grid_bounds + + real xprob(numlon,numlat) + real rlon(numlon),rlat(numlat) + real xmeanlon(maxstorms,maxtimes) + real xmeanlat(maxstorms,maxtimes) + real re,ri,blat,blon,bval,ctlon,ctlat,dell + real tlat,tlon,fixlon,fixlat,dthresh + real xmodelon,xmodelat,xmodeval + integer fcsthrs(maxtimes) + integer numlon,numlat,minlon,minlat,maxlon,maxlat,basinix + + character*1 modeflag + character*4 cstorm,catcf + character(*) cymdh + +c re = 150.0 +c ri = 300.0 + re = 120.0 + ri = 240.0 + fmax = -1.0e+10 + dell = grdspc + nhalf = 3 + +c First and foremost we need to check to see if our storm is out +c of our grid bounds that we have prescribed for this probability +c mode analysis. If in fact it is outside those bounds, then +c quit this analysis and return (this should only happen on very +c rare occasions, such as if TPC keeps a storm under their control +c that passes into the central Pacific instead of handing it off +c to CPHC). + + if (xmeanlon(ist,ifh) > 0.0 .and. + & xmeanlon(ist,ifh) >= xminlon(basinix) .and. + & xmeanlon(ist,ifh) <= xmaxlon(basinix) .and. + & xmeanlat(ist,ifh) >= xminlat(basinix) .and. + & xmeanlat(ist,ifh) <= xmaxlat(basinix)) then + continue + else + print *,' ' + print *,'!!! WARNING: in get_prob_mode, the current storm' + print *,'!!! mean position is outside the bounds for this' + print *,'!!! specified ocean domain. Therefore, we cannot' + print *,'!!! calculate the mode at this hour.' + print *,'!!! RETURNING.....' + print *,' ' + print *,' Storm info:' + print *,' Storm index= ',ist + print *,' Forecast hour index= ',ifh + print *,' Mean longitude= ',xmeanlon(ist,ifh) + print *,' Mean latitude= ',xmeanlat(ist,ifh) + print *,' ' + print *,' Grid info:' + print *,' basinix= ',basinix + print *,' min longitude= ',xminlon(basinix) + print *,' max longitude= ',xmaxlon(basinix) + print *,' min latitude= ',xminlat(basinix) + print *,' max latitude= ',xmaxlat(basinix) + print *,' ' + return + endif + + +c First we need to be able to narrow our scan. We will do this +c by first scanning the array to find the northernmost, southern- +c most, easternmost and westernmost extent of the probabilities +c which are > 0. + + mini = 9999 + minj = 9999 + maxi = -9999 + maxj = -9999 + + dmax = -1.0e+10 + do i = 1,numlon + do j = 1,numlat + if (xprob(i,j) > dmax) then + dmax = xprob(i,j) + id = i + jd = j + endif + if (xprob(i,j) > 0.0) then + if (i < mini) mini = i + if (i > maxi) maxi = i + if (j < minj) minj = j + if (j > maxj) maxj = j + endif + enddo + enddo + + print *,' ' + print *,'dmax: at beg of get_prob_mode, dmax= ',dmax + print *,'i= ',id,' j= ',jd + print *,'mini= ',mini + print *,'maxi= ',maxi + print *,'minj= ',minj + print *,'maxj= ',maxj + + if (mini == 9999 .or. minj == 9999 .or. + & maxi == -9999 .or. maxj == -9999) then + print *,' ' + print *,'!!! ERROR in get_prob_mode, one or more of the max or' + print *,'!!! min indeces for the xprob array could not ' + print *,'!!! be found. The xprob array must = 0.' + print *,'!!! EXITING.... ' + print *,' ' + STOP 99 + endif + +c Load all the lat and lon values into arrays. Remember that +c the xprob array goes from upper left (NW) to lower right (SE). + + do i = 1,numlon + rlon(i) = float(minlon + i - 1) + enddo + + do j = numlat,1,-1 + jix = numlat - j + 1 + rlat(jix) = float(maxlat - jix + 1) + enddo + +c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +c First pass through barnes analysis.... +c +c For the first round through the barnes analysis, we use the +c full grid resolution, and at each grid point, we look at an +c area roughly 2x(ri). + + npts = ceiling(ri/(dtk*grdspc)) * 2 + + ibct=0 + ibarnes_loopct = 0 + +c print *,' ' +c print *,'before first barnes, mini= ',mini,' maxi= ',maxi +c print *,'before first barnes, minj= ',minj,' maxj= ',maxj +c print *,'npts= ',npts + + do i = mini,maxi + do j = minj,maxj + + blon = rlon(i) + blat = rlat(j) + +c write (*,*) ' ' +c write (*,*) ' - - - - - - - - - - - -' +c write (*,*) ' ' +c write (*,59) cstorm,ifhour +c write (*,61) 'before 1st barnes, i= ',i,j,blon,360.-blon +c & ,blat,-99.0 + + jjbeg = j - npts + if (jjbeg < 1) jjbeg = 1 + jjend = j + npts + if (jjend > numlat) jjend = numlat + iibeg = i - npts + if (iibeg < 1) iibeg = 1 + iiend = i + npts + if (iiend > numlon) iiend = numlon + + ibct = ibct + 1 + call barnes (blon,blat,rlon,rlat,numlon,numlat,iibeg,jjbeg + & ,iiend,jjend,xprob,re,ri,bval,icount) + +c write (*,61) 'after 1st barnes, i= ',i,j,blon,360.-blon +c & ,blat,bval + + ibarnes_loopct = ibarnes_loopct + icount + + if (bval > fmax) then + fmax = bval + ctlon = blon + ctlat = blat + endif + + enddo + enddo + + 59 format (1x,'Storm= ',a4,' forecast hour= ',i4) + 61 format (1x,a22,i3,' j= ',i3,' blon= ',f7.2,'E (',f7.2,'W) blat= ' + & ,f7.2,' bval= ',f10.6) + + print *,' ' + print *,'++++ FIRST BARNES LOOP COMPLETED ++++' + print *,' ' + print *,'After first loop, # calls to barnes = ',ibct + print *,'Total # of internal barnes loop iterations = ' + & ,ibarnes_loopct + + write (*,59) cstorm,ifhour + write (6,63) ctlon,360.-ctlon,ctlat,fmax + 63 format (' After first run, ctlon= ',f8.3,'E (',f8.3,'W) ctlat= ' + & ,f8.3,' barnval = ',f10.6) + print *,' ' + + if (ctlon <= xminlon(basinix) .or. ctlon >= xmaxlon(basinix) .or. + & ctlat <= xminlat(basinix) .or. ctlat >= xmaxlat(basinix)) then + print *,' ' + print *,'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + print *,'!!! EXIT: In get_prob_mode, the mode position that ' + print *,'!!! was found after 1 iteration of the barnes loop is' + print *,'!!! equal to or greater than 1 of the boundaries, ' + print *,'!!! meaning that we are pushing up against the' + print *,'!!! boundary with our search and it is possible that' + print *,'!!! we are not finding the true mode.' + print *,'!!! ' + print *,'!!! ctlon= ',ctlon,' ctlat= ',ctlat + print *,'!!! grid minlon = ',xminlon(basinix) + print *,'!!! grid maxlon = ',xmaxlon(basinix) + print *,'!!! grid minlat = ',xminlat(basinix) + print *,'!!! grid maxlat = ',xmaxlat(basinix) + print *,'!!! ' + print *,'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + modeflag = 'n' + return + endif + +c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +c Second pass through barnes analysis.... +c +c Now that we've gotten a first estimate of the position, we +c go through this 3 more times, cutting the grid resolution +c in half each time through in order to sharpen the estimate. +c Cut the number of points (npts) in half from what we looked +c at in the first barnes iteration so that our first position +c estimate is restricted from moving too much. + + npts = ceiling(ri/(dtk*grdspc)) + + ibct = 0 + ibarnes_loopct = 0 + kloop: do k = 1,nhalf + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat + fmax = -1.0e+15 + + print *,' ' + write (*,67) k,ctlon,360.-ctlon,ctlat + 67 format (1x,'In second barnes loop, k= ',i3,' ctlon= ',f8.3 + & ,'E (',f8.3,'W) ctlat= ',f8.3) + print *,' ' + + jloop: do j = -npts,npts + + blat = tlat + dell*float(j) + + if (blat > float(maxlat) .or. blat < float(minlat)) then + print *,' ' + print *,'!!! WARNING: In get_prob_mode 2nd barnes loop for' + print *,'!!! nhalf = ',nhalf,', blat is outside of the ' + print *,'!!! grid lat bounds. blat= ',blat + print *,'!!! maxlat= ',maxlat,' minlat= ',minlat + cycle jloop + endif + + iloop: do i = -npts,npts + + blon = tlon + dell*float(i) + + if (blon > float(maxlon) .or. blon < float(minlon)) then + print *,' ' + print *,'!!! WARNING: In get_prob_mode 2nd barnes loop' + print *,'!!! for nhalf = ',nhalf,', blon is outside of' + print *,'!!! the grid lon bounds. blon= ',blon + print *,'!!! maxlon= ',maxlon,' minlon= ',minlon + cycle iloop + endif + + ifix = int(blon - float(minlon)) + 1 + jfix = int(float(maxlat) - blat) + 1 + +c write (*,*) ' ' +c write (*,*) ' - - - - - - - - - - - -' +c write (*,*) ' ' +c write (*,59) cstorm,ifhour +c write (*,71) 'before 2nd barnes, ifix= ',ifix,jfix,k,blon +c & ,360.-blon,blat,-99.0 + + ! We cut npts in half so that the center position is + ! restricted from moving too much in this refinement, + ! but we need to double it here to make sure we + ! include enough points in the barnes analysis. + + jjbeg = jfix - (npts*2) + if (jjbeg < 1) jjbeg = 1 + jjend = jfix + (npts*2) + if (jjend > numlat) jjend = numlat + iibeg = ifix - (npts*2) + if (iibeg < 1) iibeg = 1 + iiend = ifix + (npts*2) + if (iiend > numlon) iiend = numlon + + ibct = ibct + 1 + call barnes (blon,blat,rlon,rlat,numlon,numlat,iibeg,jjbeg + & ,iiend,jjend,xprob,re,ri,bval,icount) + +c write (*,71) 'after 2nd barnes, ifix= ',ifix,jfix,k,blon +c & ,360.-blon,blat,bval + + ibarnes_loopct = ibarnes_loopct + icount + + if (bval > fmax) then + fmax = bval + ctlon = blon + ctlat = blat + endif + + enddo iloop + + enddo jloop + + write (6,73) k,ctlon,360.-ctlon,ctlat,fmax + + if (ctlon <= xminlon(basinix) .or. ctlon >= xmaxlon(basinix) + & .or. ctlat <= xminlat(basinix) + & .or. ctlat >= xmaxlat(basinix)) then + print *,' ' + print *,'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + print *,'!!! EXIT: In get_prob_mode, the mode position that ' + print *,'!!! was found after iteration nhalf= ',k,' of the ' + print *,'!!! barnes loop is equal to or greater than 1 of the' + print *,'!!! boundaries, meaning that we are pushing up ' + print *,'!!! against the boundary with our search and it is' + print *,'!!! possible that we are not finding the true mode.' + print *,'!!! ' + print *,'!!! ctlon= ',ctlon,' ctlat= ',ctlat + print *,'!!! grid minlon = ',xminlon(basinix) + print *,'!!! grid maxlon = ',xmaxlon(basinix) + print *,'!!! grid minlat = ',xminlat(basinix) + print *,'!!! grid maxlat = ',xmaxlat(basinix) + print *,'!!! ' + print *,'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + modeflag = 'n' + return + endif + + enddo kloop + + 71 format (1x,a25,i3,' jfix= ',i3,' k= ',i2,' blon= ',f7.2,'E (' + & ,f7.2,'W) blat= ',f7.2,' bval= ',f10.6) + + 73 format (' nhalf barnes, k= ',i2,' ctlon= ',f8.3,'E (',f8.3 + & ,'W) ctlat= ',f8.3,' barnval = ',f10.6) + + print *,' ' + print *,'++++ SECOND BARNES LOOP COMPLETED ++++' + print *,' ' + print *,'After second loop, # calls to barnes = ',ibct + print *,'Total # of internal barnes loop iterations = ' + & ,ibarnes_loopct + + write (*,59) cstorm,ifhour + write (6,77) ctlon,360.-ctlon,ctlat,fmax + 77 format (' After second run, ctlon= ',f8.3,'E (',f8.3,'W) ctlat= ' + & ,f8.3,' barnval = ',f10.6) + print *,' ' + print *,'+ + + + + + + + + + + + + + + + + + + ' + + xmodelon = ctlon + xmodelat = ctlat + bmaxval = fmax + + +c We have now gotten the position of the max probability, or +c the mode, using the barnes analysis. This barnes analysis, +c however, gives us essentially an area-averaged value. But +c what we really need is the actual raw probability value +c calculated previously in this program. So we will look at +c the eight gridpoints surrounding the lat/lon value obtained +c by the barnes analysis and pick out the max raw value. +c For the output atcfunix mode record, however, we will use +c the lat/lon position obtained through the barnes analysis. + + print *,' ' + print *,' Getting (i,j) location of max prob mode....' + print *,' numlon= ',numlon,' numlat= ',numlat + print *,' ifix= ',ifix,' jfix= ',jfix + print *,' ' + + ifix = int(ctlon - float(minlon)) + 1 + jfix = int(float(maxlat) - ctlat) + 1 + + fmax = -1.0e+15 + + if (xprob(ifix,jfix) > fmax) then + ihold = ifix + jhold = jfix + fmax = xprob(ifix,jfix) + endif + + if (ifix < numlon) then + ! Check the point just *east* of our + ! found mode position.... + if (xprob(ifix+1,jfix) > fmax) then + ihold = ifix+1 + jhold = jfix + fmax = xprob(ifix+1,jfix) + endif + endif + + if (ifix > 1) then + ! Check the point just *west* of our + ! found mode position.... + if (xprob(ifix-1,jfix) > fmax) then + ihold = ifix-1 + jhold = jfix + fmax = xprob(ifix-1,jfix) + endif + endif + + if (jfix < numlat) then + ! Check the point just *south* of our + ! found mode position (remember: j=1 is + ! the northernmost point....) + if (xprob(ifix,jfix+1) > fmax) then + ihold = ifix + jhold = jfix+1 + fmax = xprob(ifix,jfix+1) + endif + endif + + if (jfix > 1) then + ! Check the point just *north* of our + ! found mode position (remember: j=1 is + ! the northernmost point....) + if (xprob(ifix,jfix-1) > fmax) then + ihold = ifix + jhold = jfix-1 + fmax = xprob(ifix,jfix-1) + endif + endif + + if (ifix < numlon .and. jfix > 1) then + ! Check the point just *northeast* of our + ! found mode position (remember: j=1 is + ! the northernmost point....) + if (xprob(ifix+1,jfix-1) > fmax) then + ihold = ifix+1 + jhold = jfix-1 + fmax = xprob(ifix+1,jfix-1) + endif + endif + + if (ifix < numlon .and. jfix < numlat) then + ! Check the point just *southeast* of our + ! found mode position (remember: j=1 is + ! the northernmost point....) + if (xprob(ifix+1,jfix+1) > fmax) then + ihold = ifix+1 + jhold = jfix+1 + fmax = xprob(ifix+1,jfix+1) + endif + endif + + if (ifix > 1 .and. jfix < numlat) then + ! Check the point just *southwest* of our + ! found mode position (remember: j=1 is + ! the northernmost point....) + if (xprob(ifix-1,jfix+1) > fmax) then + ihold = ifix-1 + jhold = jfix+1 + fmax = xprob(ifix-1,jfix+1) + endif + endif + + if (ifix > 1 .and. jfix > 1) then + ! Check the point just *northwest* of our + ! found mode position (remember: j=1 is + ! the northernmost point....) + if (xprob(ifix-1,jfix-1) > fmax) then + ihold = ifix-1 + jhold = jfix-1 + fmax = xprob(ifix-1,jfix-1) + endif + endif + + fixlon = float(minlon + ihold - 1) + fixlat = float(maxlat - jhold + 1) + xmodeval = fmax + + print *,' ' + print *,' Location (i,j) of max mode: ' + print *,' i-location: ',ihold,' fixlon: ',fixlon + print *,' j-location: ',jhold,' fixlat: ',fixlat + print *,' fmax = xmodeval = ',xmodeval + print *,' ' + + print *,' ' + print *,'At end of get_prob_mode, ' + write (*,59) cstorm,ifhour + write (*,81) ctlat,xmodelat + write (*,83) ctlon,360.-ctlon,xmodelon,360.-xmodelon + write (*,85) bmaxval,xmodeval + 81 format (1x,'Barnes lat: ',f7.2,' fixlat: ',f7.2) + 83 format (1x,'Barnes lon: ',f7.2,'E (',f7.2,'W) fixlon: ' + & ,f7.2,'E (',f7.2,'W)') + 85 format (1x,'Barnes val: ',f10.6,' fixval: ',f10.6) + print *,'ifix= ',ihold,' jfix= ',jhold + + +c Now make a call to output_atcfunix to create an output +c atcfunix record for this mode value.... + + call output_atcfunix (cstorm,cymdh,xmodelat,xmodelon + & ,ifh,'mode',xmodeval,dthresh,catcf,fcsthrs) + +c + return + end + +c +c---------------------------------------------------------------- +c +c---------------------------------------------------------------- + subroutine barnes(flon,flat,rlon,rlat,imax,jmax,iibeg,jjbeg + & ,iiend,jjend,fxy,re,ri,favg,icount) +c +c ABSTRACT: This routine performs a single-pass barnes anaylsis +c of fxy at the point (flon,flat). The e-folding radius (km) +c and influence radius (km) are re and ri, respectively. +c +c NOTE: The input grid that is searched in this subroutine is most +c likely NOT the model's full, original grid. Instead, a smaller +c subgrid of the original grid is searched, which is determined by +c the npts value passed into this subroutine. +c +c INPUT: +c flon Lon value for center point about which barnes anl is done +c flat Lat value for center point about which barnes anl is done +c rlon Array of lon values for each grid point +c rlat Array of lat values for each grid point +c imax Max number of pts in x-direction on input grid +c jmax Max number of pts in y-direction on input grid +c iibeg i index for grid point to start barnes anlysis (upp left) +c jjbeg j index for grid point to start barnes anlysis (upp left) +c iiend i index for last grid point in barnes anlysis (low right) +c jjend j index for last grid point in barnes anlysis (low right) +c fxy Real array of data on which to perform barnes analysis +c re input e-folding radius for barnes analysis +c ri input influence radius for searching for min/max +c +c OUTPUT: +c favg Average value about the point (flon,flat) +c + real fxy(imax,jmax), rlon(imax), rlat(jmax) + real flon,flat,favg,res,re,ri,wts,dist,wt + +c -------------------------- + + res = re*re + wts = 0.0 + favg = 0.0 + + icount = 0 + +c write (*,62) iibeg,iiend,jjbeg,jjend +c 62 format (1x,'in barnes, iibeg= ',i3,' iiend= ',i3,' jjbeg= ',i3 +c & ,' jjend= ',i3) + + do j=jjbeg,jjend + do i=iibeg,iiend + + icount = icount + 1 + + ! Call a different version of calcdist in order to + ! get the distance in km.... + + call calcdistkm(flon,flat,rlon(i),rlat(j),dist) + +c write (*,65) i,j,flon,flat,rlon(i),rlat(j),dist + + if (dist .gt. ri) cycle + +c write (*,66) i,j,flon,flat,rlon(i),rlat(j),dist,fxy(i,j) +c print *,'+++ dist= ',dist,' fxy= ',fxy(i,j) + + wt = exp(-1.0*dist*dist/res) + wts = wts + wt + favg = favg + wt*fxy(i,j) + + enddo + enddo + +c 65 format (1x,'i= ',i3,' j= ',i3,' flon= ',f6.2,' flat= ',f5.2 +c & ,' rlon(i)= ',f6.2,' rlat(j)= ',f5.2,' dist= ',f7.2) +c 66 format (1x,'i= ',i3,' j= ',i3,' flon= ',f6.2,' flat= ',f5.2 +c & ,' rlon(i)= ',f6.2,' rlat(j)= ',f5.2,' dist= ',f7.2 +c & ,' fxy= ',f5.2) +cJ.Peng_04-22-2013 F4.2 to f5.2 the above line +c write (*,75) 'in barnes before averaging, wts = ',wts,' favg= ' +c & ,favg + + if (wts .gt. 1.0E-5) then + favg = favg/wts + else + favg = 0.0 + endif + iret = 0 + +c write (*,75) 'in barnes after averaging, wts = ',wts,' favg= ' +c & ,favg + +c 75 format (1x,a34,f9.4,a8,f10.6) + +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calcdistkm(rlonb,rlatb,rlonc,rlatc,xdist) +c +c ABSTRACT: This subroutine computes the distance between two +c lat/lon points by using spherical coordinates to +c calculate the great circle distance between the points. +c Figure out the angle (a) between pt.B and pt.C, +c N. Pole then figure out how much of a % of a great +c x circle distance that angle represents. +c / \ +c b/ \ cos(a) = (cos b)(cos c) + (sin b)(sin c)(cos A) +c / \ +c pt./<--A-->\c NOTE: The latitude arguments passed to the +c B / \ subr are the actual lat vals, but in +c \ the calculation we use 90-lat. +c a \ +c \pt. NOTE: You may get strange results if you: +c C (1) use positive values for SH lats AND +c you try computing distances across the +c equator, or (2) use lon values of 0 to +c -180 for WH lons AND you try computing +c distances across the 180E meridian. +c +c NOTE: In the diagram above, (a) is the angle between pt. B and +c pt. C (with pt. x as the vertex), and (A) is the difference in +c longitude (in degrees, absolute value) between pt. B and pt. C. +c +c !!! NOTE !!! -- THE PARAMETER ecircum IS DEFINED (AS OF THE +c ORIGINAL WRITING OF THIS SYSTEM) IN KM, NOT M, SO BE AWARE THAT +c THE DISTANCE RETURNED FROM THIS SUBROUTINE IS ALSO IN KM. +c + USE trig_vals +c + if (rlatb < 0.0 .or. rlatc < 0.0) then + pole = -90. + else + pole = 90. + endif +c + distlatb = (pole - rlatb) * dtr + distlatc = (pole - rlatc) * dtr + difflon = abs( (rlonb - rlonc)*dtr ) +c + cosanga = ( cos(distlatb) * cos(distlatc) + + & sin(distlatb) * sin(distlatc) * cos(difflon)) + +c This next check of cosanga is needed since I have had ACOS crash +c when calculating the distance between 2 identical points (should +c = 0), but the input for ACOS was just slightly over 1 +c (e.g., 1.00000000007), due to (I'm guessing) rounding errors. + + if (cosanga > 1.0) then + cosanga = 1.0 + endif +c added on 04/28/2016-------------------------- + if (cosanga < -1.0) then + cosanga = -1.0 + endif +c added on 04/28/2016-------------------------- + + degrees = acos(cosanga) / dtr + circ_fract = degrees / 360. + xdist = circ_fract * ecircum + +c NOTE: whether this subroutine returns the value of the distance +c in km or m depends on the scale of the parameter ecircum. +c At the original writing of this subroutine (7/97), ecircum +c was given in km. + + return + end + +c +c----------------------------------------------------------------------- +c +cJ.Peng-04-15-2013 -------------------------------------------- + subroutine read_atcfunix (traklat,traklon,tcvmx,tcpce, + & saverec,stormarr,maxmem,fcst_interval,perts) + +c +c ABSTRACT: This subroutine reads in the atcfunix records from +c the concatenated file that contains all these records for all +c storms, all forecast hours and all ensemble members. + + USE define_atcfunix_rec; USE maxparms; USE ensinfo + USE grid_bounds +c + type (atcfunix_card) inprec,saverec + + real traklat(maxstorms,maxmem,maxtimes) + real traklon(maxstorms,maxmem,maxtimes) + +cJ.Peng-04-15-2013 + real tcvmx(maxstorms,maxmem,maxtimes) + real tcpce(maxstorms,maxmem,maxtimes) + + integer maxmem,fcst_interval + character*4 perts(maxmem) + character*4 atcfid,stormarr(maxstorms) + character*1 foundstorm,foundmember,foundmodel +c + stormarr = 'XXXX' + + ict = 0 + istot = 0 + readloop: do while (.true.) + + read (11,85,end=870) inprec + + if (ict == 0) then + saverec = inprec + ict = ict + 1 + endif + + atcfid(1:2) = inprec%atx_basin + atcfid(3:4) = inprec%atx_storm_num + +c Find the array index for the storm that is being read in. As +c we encounter a new storm in the atcfunix file, we add that +c storm to the array and increment the index (istix) by 1. + + foundstorm = 'n' + stormloop: do istorm = 1,maxstorms + if (atcfid == stormarr(istorm)) then + foundstorm = 'y' + istix = istorm + exit stormloop + endif + enddo stormloop + if (foundstorm == 'n') then + istot = istot + 1 + istix = istot + stormarr(istix) = atcfid + endif + +c Similarly, find the lat/lon array index for the ensemble member +c that is being read in. The members are specified in a module. +c If the program cannot find the member, chances are the ensemble +c suite has been updated and new members have been added. + + foundmember = 'n' + memberloop: do imem = 1,maxmem + if (inprec%atx_model == perts(imem)) then + foundmodel = 'y' + imix = imem + exit memberloop + endif + enddo memberloop + if (foundmodel == 'n') then + print *,' ' + print *,'!!! ERROR: IN PROGRAM TRAKAVE, A MODEL/MEMBER NAME' + print *,'!!! IS NOT RECOGNIZED.' + print *,'!!! ' + print *,'!!! MODEL/MEMBER NAME = --->' + & ,inprec%atx_model,'<----' + print *,'!!! ' + print *,'!!! STOPPING PROGRAM.... ' + print *,'!!! ' + stop 990 + endif + +c Calculate the index (ifix) for the forecast hour in lat and +c lon arrays. + + ifix = (inprec%atx_fcsthr / fcst_interval) + 1 + +c Check the lat/lon arrays to see if we've already read in the +c lat/lon positions for this storm/member/forecast_time (traklat +c and traklon are initialized to -9999.0). Yes, it is possible +c to have more than 1 record for a given storm/member/time, +c since there are up to 3 different records written out for each +c forecast time, each containing the 34-, 50- and 64-kt wind +c radii. Note that if the values read in are zeroes, that means +c that the tracker could not find the storm; consider these +c missing values and ignore them. + + if (traklat(istix,imix,ifix) < -9998.0 .and. + & traklon(istix,imix,ifix) < -9998.0) then + + if (inprec%atx_lat > 0 .and. inprec%atx_lon > 0) then + + ! Float the integer latitudes and convert any positive + ! southern latitudes to be negative. + + if (inprec%atx_latns == 'N') then + traklat(istix,imix,ifix) = float(inprec%atx_lat)/10.0 + else + traklat(istix,imix,ifix) = float(inprec%atx_lat)/(-10.0) + endif + + ! Float the integer longitudes and convert any "western" + ! longitudes to be "eastern", 0-360. + + if (inprec%atx_lonew == 'E') then + traklon(istix,imix,ifix) = float(inprec%atx_lon)/10.0 + else + traklon(istix,imix,ifix) = float(3600 - inprec%atx_lon) + & / 10.0 + endif + +cJ.Peng-04-15-2013 + tcvmx(istix,imix,ifix) = float(inprec%atx_vmax) + tcpce(istix,imix,ifix) = float(inprec%atx_pcen) + + endif + + endif + + enddo readloop + +C close (11) + + 85 format (a2,2x,a2,2x,a10,2x,a2,2x,a4,2x,i3,2x,i3,a1,2x,i4,a1,2x + & ,i3,2x,i4) +c & ,i3,2x,i4,2x,a2,2x,i2,2x,a3,4(2x,i3)) +cPENG bug fixed on 04/18/2018 +c 85 format (a2,2x,a2,2x,a10,2x,a2,2x,a4,2x,i3,2x,i3,a1,2x,i4,a1,2x +c & ,i3,2x,i4,2x,a2,3x,i2,2x,a3,4(2x,i4)) + + 870 goto 1090 + + 1010 print *,' ' + print *,'!!! ERROR opening input file. ERROR = ',ios + print *,' ' + + 1090 continue + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calcdist(rlonb,rlatb,rlonc,rlatc,dtr,xdist) +c +c ABSTRACT: This subroutine computes the distance between two +c lat/lon points by using spherical coordinates to +c calculate the great circle distance between the points. +c Figure out the angle (a) between pt.B and pt.C, +c N. Pole then figure out how much of a % of a great +c x circle distance that angle represents. +c / \ +c b/ \ cos(a) = (cos b)(cos c) + (sin b)(sin c)(cos A) +c / \ +c pt./<--A-->\c NOTE: The latitude arguments passed to the +c B / \ subr are the actual lat vals, but in +c \ the calculation we use 90-lat. +c a \ +c \pt. NOTE: You may get strange results if you: +c C (1) use positive values for SH lats AND +c you try computing distances across the +c equator, or (2) use lon values of 0 to +c -180 for WH lons AND you try computing +c distances across the 180E meridian. +c +c NOTE: In the diagram above, (a) is the angle between pt. B and +c pt. C (with pt. x as the vertex), and (A) is the difference in +c longitude (in degrees, absolute value) between pt. B and pt. C. +c +c !!! NOTE !!! -- THE PARAMETER ecircum IS DEFINED (AS OF THE +c WRITING OF THIS SYSTEM) IN NM, NOT M or KM, SO BE AWARE THAT +c THE DISTANCE RETURNED FROM THIS SUBROUTINE IS ALSO IN NM. +c + real :: dtk = 60.005 ! Dist (nm) over 1 deg lat + ! using erad=6371.0e+3 km + real :: erad = 6371.0e+3 ! Earth's radius (m) + real :: ecircum = 21601.8 ! Earth's circumference + ! (nm) using erad=6371.e3 + real dtr +c + if (rlatb < 0.0 .or. rlatc < 0.0) then + pole = -90. + else + pole = 90. + endif +c + distlatb = (pole - rlatb) * dtr + distlatc = (pole - rlatc) * dtr + difflon = abs( (rlonb - rlonc)*dtr ) +c + cosanga = ( cos(distlatb) * cos(distlatc) + + & sin(distlatb) * sin(distlatc) * cos(difflon)) + +c This next check of cosanga is needed since I have had ACOS crash +c when calculating the distance between 2 identical points (should +c = 0), but the input for ACOS was just slightly over 1 +c (e.g., 1.00000000007), due to (I'm guessing) rounding errors. + + if (cosanga > 1.0) then + cosanga = 1.0 + endif +c added on 04/28/2016-------------------------- + if (cosanga < -1.0) then + cosanga = -1.0 + endif +c added on 04/28/2016-------------------------- + + degrees = acos(cosanga) / dtr + circ_fract = degrees / 360. + xdist = circ_fract * ecircum + +c NOTE: whether this subroutine returns the value of the distance in +c km, m or nm depends on the scale of the parameter ecircum. +c At the original writing of this subroutine (2/01), ecircum +c was given in nm. + + return + end + +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_atcfunix (stormid,cymdh,outlat,outlon,ifh + & ,ctype,xprob,dthresh,catcf,fcsthrs) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in the new ATCF UNIX format. +c Unlike the old atcf DOS format in which you waited until the +c whole tracking was over to write the output for all forecast +c hours, with this atcfunix format, each time we are calling this +c subroutine, it is to only write out 1 record, which will be the +c fix info for a particular storm at a given time. +c +c While this new atcfunix format contains much more information than +c the old 1-line atcf dos message, for the purposes of the ensemble +c mean track, we will only use the slots for storm location and NOT +c worry about the intensity parameters. An example is shown below: +c +c AL, 13, 2000092500, 03, AEMN, 036, 243N, 675W +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c stormid Storm ID (EP02, AL13, etc...) +c cymdh initial date/time of forecast (yyyymmddhh) +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c ifh the index for the current forecast hour being output +c ctype 'mean' for ens mean, 'mode' for ens prob mode +c xprob prob mode value for ctype = 'mode'. Not used for mean +c dthresh distance threshold used for prob mode value, to be +c used in model output name for 'mode' case. +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + USE ensinfo; USE maxparms + + real outlon,outlat,xprob + integer fcsthrs(maxtimes) + integer intlon,intlat + character*1 clatns,clonew + character*2 basinid,stormnum + character*4 stormid,ctype,cmode,catcf + character*10 cymdh + character*37 outatxfile + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + +C outatxfile = 'aemn.'//cymdh//'.cyclone.trackatcfunix' +C open (unit=53,file=outatxfile,status='unknown',position='append') + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then +c intlon = 3600 - int(outlon * 10. + 0.5) +c clonew = 'W' +cJ.Peng-----03/27/2015------------------------------------ + if (outlon < 360.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon =int(outlon * 10. + 0.5) - 3600 + clonew = 'E' + endif +cJ.Peng-----03/27/2015------------------------------------ + + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + basinid = stormid(1:2) + stormnum = stormid(3:4) + + if (ctype == 'mean') then + write (53,81) basinid,stormnum,cymdh,catcf,fcsthrs(ifh) + & ,intlat,clatns,intlon,clonew + else + itemp = int(dthresh) + cmode(1:1) = catcf(1:1) + write (cmode(2:4),'(i3.3)') itemp +c print *,'atcfunix, cmode= ',cmode,' itemp= ',itemp +c write (*,79) cmode,itemp + 79 format (1x,' cmode= ....',a4,'.... itemp= ',i3.3) + write (57,83) basinid,stormnum,cymdh,cmode,fcsthrs(ifh) + & ,intlat,clatns,intlon,clonew,int(xprob*100.0) + endif + + 81 format (a2,', ',a2,', ',a10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1) + 83 format (a2,', ',a2,', ',a10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3) +c +C close (53) + return + end + +c +c----------------------------------------------------------------------- + subroutine output_atcf (stormid,xmeanlon,xmeanlat,cymdh,ist,catcf) +c +c ABSTRACT: This subroutine outputs a 1-line message for each storm +c in ATCF format. This message contains the model identifier, the +c forecast initial date, and the positions for 12, 24, 36, 48 and +c 72 hours. This message also contains the intensity +c estimates (in knots) for those same hours. The conversion for +c m/s to knots is to multiply m/s by 1.9427 (3.281 ft/m, +c 1 naut mile/6080 ft, 3600s/h). +c +c NOTE: Intensity estimates are NOT included for the ensemble +c mean tracks. +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by the atcf system at TPC for plotting +c purposes, and the atcf plotting routines need the longitudes in +c 0 - 360, increasing westward. Thus, a necessary adjustment is +c made. + + USE maxparms + + character*4 stormid,catcf + character*10 cymdh + character*8 yymmddhh + character*33 outatcffile + + real xmeanlat(maxstorms,maxtimes) + real xmeanlon(maxstorms,maxtimes) + integer intlon(maxtimes),intlat(maxtimes) +c +C outatcffile = 'aemn.'//cymdh//'.cyclone.trackatcf' +C open (unit=54,file=outatcffile,status='unknown',position='append') + + yymmddhh = cymdh(3:10) + + ifhloop: do ifh = 1,maxtimes + + if (xmeanlon(ist,ifh) < -998.0 .or. xmeanlat(ist,ifh) < -998.0) + & then + + intlon(ifh) = 0 + intlat(ifh) = 0 + + else + intlon(ifh) = 3600 - int(xmeanlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(xmeanlat(ist,ifh)) * 10. + 0.5) + if (xmeanlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + + endif + + enddo ifhloop + + write (54,82) '99',catcf,yymmddhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7) + & ,intlat(9),intlon(9),intlat(13),intlon(13) + & ,0,0,0,0,0,stormid,cymdh(9:10) + + 82 format (a2,a4,a8,10i4,5i3,1x,a4,a2) +c + +C close (54) + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_all (stormid,xmeanlon,xmeanlat,cymdh,ist,catcf) +c +c ABSTRACT: This subroutine outputs a 1-line message for each +c storm. This message contains the model identifier, the forecast +c initial date, and the positions for 0, 12, 24, 36, 48, 60 and 72 +c hours. In the case of the regional models (NGM, Eta), which +c only go out to 48h, zeroes are included for forecast hours +c 60 and 72. +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by Steve Lord for plotting purposes, and his +c plotting routines need the longitudes in 0 - 360, increasing +c westward. Thus, a necessary adjustment is made. +c + USE maxparms + + character*4 stormid,catcf + character*10 cymdh + character*8 yymmddhh + character*2 stormnum + character*3 basinid + character*32 outallfile + + real xmeanlat(maxstorms,maxtimes) + real xmeanlon(maxstorms,maxtimes) + integer intlon(maxtimes),intlat(maxtimes) +c +C outallfile = 'aemn.'//cymdh//'.cyclone.trackall' +C open (unit=55,file=outallfile,status='unknown',position='append') + + yymmddhh = cymdh(3:10) + stormnum = stormid(3:4) +c + ifhloop: do ifh = 1,maxtimes + + if (xmeanlon(ist,ifh) < -998.0 .or. xmeanlat(ist,ifh) < -998.0) + & then + intlon(ifh) = 0 + intlat(ifh) = 0 + else + intlon(ifh) = 3600 - int(xmeanlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(xmeanlat(ist,ifh)) * 10. + 0.5) + if (xmeanlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + endif + + enddo ifhloop + + select case (stormid(1:2)) + case ('AL'); basinid(3:3) = 'L' + case ('CP'); basinid(3:3) = 'C' + case ('EP'); basinid(3:3) = 'E' + case ('WP'); basinid(3:3) = 'W' + case ('SC'); basinid(3:3) = 'O' + case ('EC'); basinid(3:3) = 'T' + case ('AU'); basinid(3:3) = 'U' + case ('SP'); basinid(3:3) = 'P' + case ('SI'); basinid(3:3) = 'S' + case ('BB'); basinid(3:3) = 'B' + case ('AA'); basinid(3:3) = 'A' + case default; basinid(3:3) = '*' + end select + + basinid(1:2) = stormid(3:4) + + write (55,83) '99',catcf,yymmddhh,intlat(1),intlon(1) + & ,intlat(3),intlon(3),intlat(5),intlon(5) + & ,intlat(7),intlon(7),intlat(9),intlon(9) + & ,intlat(11),intlon(11),intlat(13),intlon(13) + & ,basinid + + 83 format (a2,a4,a8,14i4,1x,a3) +c +C close (55) + return + end + +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_nlists (dthresh,cmodel) +c +c ABSTRACT: This subroutine simply reads in the namelists that are +c created in the shell script. Namelist datain currently contains +c just the value of the dthresh parameter. +c + real dthresh + character*5 :: cmodel +c + namelist/datain/dthresh,cmodel +c + read (5,NML=datain,END=801) + 801 continue + + print *,' ' + print *,'After datain namelist read in trakave.f, ' + print *,'namelist parms follow: ' + print *,' ' + print *,'Distance threshold (dthresh) = ',dthresh + print *,'cmodel = ',cmodel +c + return + end +c--------------------------------------------------------------------- +cJ.Peng-04-15-2013-------------------------- + subroutine output_atcfunix1 (stormid,cymdh,outlat,outlon + & ,stdvmx,stdpce,yspread + & ,outvmx,outpce,ifh + & ,ctype,xprob,dthresh,catcf,fcsthrs) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in the new ATCF UNIX format. +c Unlike the old atcf DOS format in which you waited until the +c whole tracking was over to write the output for all forecast +c hours, with this atcfunix format, each time we are calling this +c subroutine, it is to only write out 1 record, which will be the +c fix info for a particular storm at a given time. +c +c While this new atcfunix format contains much more information than +c the old 1-line atcf dos message, for the purposes of the ensemble +c mean track, we will only use the slots for storm location and NOT +c worry about the intensity parameters. An example is shown below: +c +c AL, 13, 2000092500, 03, AEMN, 036, 243N, 675W +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c stormid Storm ID (EP02, AL13, etc...) +c cymdh initial date/time of forecast (yyyymmddhh) +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c ifh the index for the current forecast hour being output +c ctype 'mean' for ens mean, 'mode' for ens prob mode +c xprob prob mode value for ctype = 'mode'. Not used for mean +c dthresh distance threshold used for prob mode value, to be +c used in model output name for 'mode' case. +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + USE ensinfo; USE maxparms + + real outlon,outlat,xprob + real outvmx,outpce,stdvmx,stdpce,yspread + integer fcsthrs(maxtimes) + integer intlon,intlat + character*1 clatns,clonew + character*2 basinid,stormnum + character*4 stormid,ctype,cmode,catcf + character*10 cymdh + character*37 outatxfile + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + +C outatxfile = 'aemn.'//cymdh//'.cyclone.trackatcfunix' +C open (unit=53,file=outatxfile,status='unknown',position='append') + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then +c intlon = 3600 - int(outlon * 10. + 0.5) +c clonew = 'W' +cJ.Peng-----03/27/2015------------------------------------ + if (outlon < 360.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon =int(outlon * 10. + 0.5) - 3600 + clonew = 'E' + endif +cJ.Peng-----03/27/2015------------------------------------ + + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + basinid = stormid(1:2) + stormnum = stormid(3:4) + + if (ctype == 'mean') then +c write (53,81) basinid,stormnum,cymdh,catcf,fcsthrs(ifh) +c & ,intlat,clatns,intlon,clonew +c & ,int(outvmx+0.5),int(outpce+0.5),stdvmx,stdpce +c & ,yspread + + write (53,81) basinid,stormnum,cymdh,catcf,fcsthrs(ifh) + & ,intlat,clatns,intlon,clonew + & ,int(outvmx+0.5),int(outpce+0.5) + & ,int(yspread+0.5),int(stdpce+0.5) + & ,int(stdvmx+0.5) + else + itemp = int(dthresh) + cmode(1:1) = catcf(1:1) + write (cmode(2:4),'(i3.3)') itemp +c print *,'atcfunix, cmode= ',cmode,' itemp= ',itemp +c write (*,79) cmode,itemp + 79 format (1x,' cmode= ....',a4,'.... itemp= ',i3.3) + write (57,83) basinid,stormnum,cymdh,cmode,fcsthrs(ifh) + & ,intlat,clatns,intlon,clonew,int(xprob*100.0) + endif + +c 81 format (a2,', ',a2,', ',a10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',f5.1,', ',f5.1 +c & ,', ',f7.2) + + 81 format (a2,', ',a2,', ',a10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4, + & ', XX, 34, NEQ, 0000, 0000, 0000, 0000', + & ', ',i4,', ',i4,', ',i3) + + 83 format (a2,', ',a2,', ',a10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3) +c +C close (53) + return + end + +c +c----------------------------------------------------------------------- diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile similarity index 94% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile index 2a0cbf35e7..f782196cdc 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian ens_trak_ave: ens_trak_ave.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_hera new file mode 100644 index 0000000000..ce340a85e5 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +ens_trak_ave: ens_trak_ave.f + @echo " " + @echo " Compiling the ensemble track averaging program....." + $(COMP) $(FFLAGS) ens_trak_ave.f -o ens_trak_ave + @echo " " + +CMD = ens_trak_ave + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_jet similarity index 77% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_jet index 839f3d479a..633d50e65d 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 ens_trak_ave: ens_trak_ave.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_orion similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_orion index 897ffb7f24..6559cf8ff3 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/ens_trak_ave_2d.f b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/ens_trak_ave_2d.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/ens_trak_ave_2d.f rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/ens_trak_ave_2d.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile index d1a577ae27..74eadaadf6 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian ens_trak_ave_2d: ens_trak_ave_2d.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_hera new file mode 100644 index 0000000000..29b70f70bb --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +ens_trak_ave_2d: ens_trak_ave_2d.f + @echo " " + @echo " Compiling the ensemble track averaging program....." + $(COMP) $(FFLAGS) ens_trak_ave_2d.f -o ens_trak_ave_2d + @echo " " + +CMD = ens_trak_ave_2d + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_jet similarity index 78% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_jet index 904aae8a9b..b50c41947a 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 ens_trak_ave_2d: ens_trak_ave_2d.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_orion index 24a09d97de..eff7e42c8e 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ens_trak_ave_2d.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/ens_trak_ave_2d.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/filter_gen_cmc.f b/sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/filter_gen_cmc.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/filter_gen_cmc.f rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/filter_gen_cmc.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile similarity index 94% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile index e43adb0d6a..5c04a6b244 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian filter_gen_cmc: filter_gen_cmc.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_hera new file mode 100644 index 0000000000..6516ab5e83 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +filter_gen_cmc: filter_gen_cmc.f + @echo " " + @echo " Compiling the ensemble track filter program....." + $(COMP) $(FFLAGS) filter_gen_cmc.f -o filter_gen_cmc + @echo " " + +CMD = filter_gen_cmc + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_jet similarity index 76% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_jet index d43cccca30..36802026d4 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -axSSE4.2,AVX,CORE-AVX2 filter_gen_cmc: filter_gen_cmc.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_orion similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_orion index 10ff21fa5e..3907f45109 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_cmc.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_cmc.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/filter_gen_ecmwf.f b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/filter_gen_ecmwf.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/filter_gen_ecmwf.f rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/filter_gen_ecmwf.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile index b2486a99f6..cb611bfb54 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian filter_gen_ecmwf: filter_gen_ecmwf.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_hera new file mode 100644 index 0000000000..8f54c748da --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +filter_gen_ecmwf: filter_gen_ecmwf.f + @echo " " + @echo " Compiling the ensemble track filter program....." + $(COMP) $(FFLAGS) filter_gen_ecmwf.f -o filter_gen_ecmwf + @echo " " + +CMD = filter_gen_ecmwf + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_jet similarity index 78% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_jet index a9f390a362..c75d32cc20 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 filter_gen_ecmwf: filter_gen_ecmwf.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_orion index 20a7949294..fbe12ecaa1 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ecmwf.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ecmwf.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/filter_gen_gfs.f b/sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/filter_gen_gfs.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/filter_gen_gfs.f rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/filter_gen_gfs.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile similarity index 94% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile index fe221bdbf7..1ce0374fce 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian filter_gen_gfs: filter_gen_gfs.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_hera new file mode 100644 index 0000000000..c3388a6af3 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +filter_gen_gfs: filter_gen_gfs.f + @echo " " + @echo " Compiling the ensemble track filter program....." + $(COMP) $(FFLAGS) filter_gen_gfs.f -o filter_gen_gfs + @echo " " + +CMD = filter_gen_gfs + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_jet similarity index 77% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_jet index c443b933b7..5c603cac0c 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 filter_gen_gfs: filter_gen_gfs.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_orion similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_orion index 15681f4648..b0f24c0dfc 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_gfs.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_gfs.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/filter_gen_navgem.f b/sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/filter_gen_navgem.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/filter_gen_navgem.f rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/filter_gen_navgem.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile index fba7367dd2..04a3ed95bb 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian filter_gen_navgem: filter_gen_navgem.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_hera new file mode 100644 index 0000000000..2e6944d372 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +filter_gen_navgem: filter_gen_navgem.f + @echo " " + @echo " Compiling the ensemble track filter program....." + $(COMP) $(FFLAGS) filter_gen_navgem.f -o filter_gen_navgem + @echo " " + +CMD = filter_gen_navgem + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_jet similarity index 78% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_jet index 559e559c8e..10371752fa 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 filter_gen_navgem: filter_gen_navgem.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_orion index 946005183a..53b6826d99 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_navgem.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_navgem.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/filter_gen_ukmet.f b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/filter_gen_ukmet.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/filter_gen_ukmet.f rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/filter_gen_ukmet.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile index 2a541336d5..e325ba14ba 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian filter_gen_ukmet: filter_gen_ukmet.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_hera new file mode 100644 index 0000000000..487c263091 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +filter_gen_ukmet: filter_gen_ukmet.f + @echo " " + @echo " Compiling the ensemble track filter program....." + $(COMP) $(FFLAGS) filter_gen_ukmet.f -o filter_gen_ukmet + @echo " " + +CMD = filter_gen_ukmet + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_jet similarity index 78% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_jet index 44e1d56b6a..cfe6131b48 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 filter_gen_ukmet: filter_gen_ukmet.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_orion index a44a848b63..8fe3c6a3c5 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/filter_ukmet.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/filter_ukmet.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/gettrk_main_g1.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/gettrk_main_g1.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/gettrk_main_g1.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/gettrk_main_g1.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/gettrk_modules.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/gettrk_modules.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/gettrk_modules.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/gettrk_modules.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile index 16831f17c1..99e8eefac8 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) gettrk_g1: gettrk_main_g1.f gettrk_modules.o diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_hera similarity index 85% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_hera index 9d76c99849..e9cdc8b2db 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_hera @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -FFLAGS= -O2 -fpe0 -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +FCOMP= mpiifort -nofree +FFLAGS= -O2 -fpe0 -i$(ISIZE) -r$(RSIZE) gettrk_g1: gettrk_main_g1.f gettrk_modules.o @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_jet new file mode 100644 index 0000000000..85c5dc52ea --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_jet @@ -0,0 +1,25 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +FCOMP= mpiifort -nofree +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 + +gettrk_g1: gettrk_main_g1.f gettrk_modules.o + @echo " " + @echo " Compiling the main tracker program ....." + $(FCOMP) $(FFLAGS) gettrk_modules.o gettrk_main_g1.f $(LIBS) -o gettrk_g1 + @echo " " + +gettrk_modules.o: gettrk_modules.f + @echo " " + @echo " Compiling the tracker modules....." + $(FCOMP) $(FFLAGS) -c gettrk_modules.f -o gettrk_modules.o + @echo " " + +CMD = gettrk_g1 + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_orion index 0424a09ab9..ff28dd58e3 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_orion @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g1.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g1.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/cwaitfor.c b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/cwaitfor.c similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/cwaitfor.c rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/cwaitfor.c diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/gettrk_main_g2.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/gettrk_main_g2.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/gettrk_main_g2.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/gettrk_main_g2.f diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/gettrk_main_g2.f_10152019 b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/gettrk_main_g2.f_10152019 new file mode 100644 index 0000000000..cbbf68cf14 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/gettrk_main_g2.f_10152019 @@ -0,0 +1,19742 @@ + program trakmain +c +c$$$ MAIN PROGRAM DOCUMENTATION BLOCK +c +c Main Program: GETTRK Track model vortices +C PRGMMR: MARCHOK ORG: NP22 DATE: 2002-05-20 +c +c ABSTRACT: This program tracks the average of the max or min +c of several parameters in the vicinity of an input +c first guess (lat,lon) position of a vortex in order to give +c forecast position estimates for that vortex for a given numerical +c model. For the levels 700 & 850 mb, the tracked parameters are: +c Relative vorticity (max), wind magnitude (min), and geopotential +c height (min). Also tracked is the min in the MSLP. So many +c parameters are tracked in order to provide more accurate position +c estimates for weaker storms, which often have poorly defined +c structures/centers. Currently, the system is set up to be able +c to process GRIB input data files from the GFS, MRF, UKMET, GDAS, +c ECMWF, NGM, NAM and FNMOC/NOGAPS models. Two 1-line files +c are output from this program, both containing the forecast fix +c positions that the tracker has obtained. One of these output +c files contains the positions at every 12 hours from forecast +c hour 0 to the end of the forecast. The other file is in ATCF +c format, which is the particular format needed by the Tropical +c Prediction Center, and provides the positions at forecast hours +c 12, 24, 36, 48 and 72, plus the maximum wind near the storm center +c at each of those forecast hours. +c +c Program history log: +c 98-03-16 Marchok - Original operational version. +c 98-07-15 Marchok - Added code to calculate radii of gale-, storm-, +c and hurricane-force winds in each quadrant. +c 99-04-01 Marchok - Added code to be able to read in 4-digit years +c off of the TC Vitals records. +c Added code, including subroutine is_it_a_storm, +c to make a better determination of whether or +c not the center that was found at each time is +c the center of a storm, and not just a passing +c vort max, etc. +c 99-06-15 Marchok - Fixed a bug in calcdist that was triggered by a +c rounding error sending a number just above 1 +c into ACOS to get the distance between 2 +c identical points (which, obviously, is 0). +c 00-06-20 Marchok - Added GDAS option for vortex relocation work. +c Changed nhalf from 3 to 5. Relaxed the +c requirements for pthresh and vthresh. +c 00-11-30 Marchok - Added ability to handle GFDL and NCEP Ensemble +c model data. Extended time range to be able to +c handle 5-day capability. Forecast hours are +c now input via a namelist (easiest way to account +c for NAM, GFS and GFDL having different forecast +c lengths at 00/12z and 06/18z). Model ID's are +c now input via a namelist (makes it easier, for +c example, to run for many different ensemble +c members). Added new output, the atcfunix +c format, needed for 5-day forecasts. +c 01-08-24 Marchok Fixed a bug in rvcal and getgridinfo. When a +c grid that was south-->north is flipped in +c conv1d2d_real to be north-->south, the scanning +c mode flag remains 64 and what we would consider +c the max and min latitudes are reversed, so I +c added code to correct this in both routines. +c 02-05-20 Marchok Weakened the mslp gradient threshold and v850 +c threshold in is_it_a_storm to cut down on the +c number of dropped storms. +c 03-03-18 Marchok Fixed a bug in get_ij_bounds that was allowing +c a cos(90) and cos(-90), which then led to a +c divide by zero. +c 05-08-01 Marchok Updated to allow tracking of ECMWF hi-res, ECMWF +c ensemble, CMC hi-res, CMC ensemble, NCEP +c ensemble. +c 06-11-07 Marchok Updated to locate, and report to the atcfunix +c file, the value of the gridpoint minimum value +c of mslp. Previously, the barnes-averaged +c value had been used. +c 08-01-10 Marchok Changed the storm ID for genesis tracking so +c that the ID includes info +c on storm detection location & time. Added +c algorithms for Hart's cyclone phase space. +c Added new output fields to the atcfunix +c records, actually creating a modified atcfunix +c record, to include things such as the mean & +c max values of zeta850 & zeta700 centered on +c the storm, the speed & direction of storm +c translation, and the Hart CPS parameters. +c 10-01-07 Marchok - input grib lead time can be hrs or minutes +c - added code for warm core check +c - added code to detect genesis +c - added code to report on sfc wind structure +c - added buffer ("grid_buffer") to avoid fixing +c center to boundaries on regional grids +c - modified rvcal to report missing zeta values +c as background coriolis instead of -999, since +c the -999 was messing up center-fixing +c - added 10-m wind and sfc zeta as center-fixing +c parms. +c +c 10-05-25 Slocum Add verbose feature to code +c 0 = Not terminal output, 1 = error messages only +c 2 = all output +c +c 10-05-26 Marchok - added flags and code to check the temporal +c consistency of the mslp closed contour and +c Vt850 checks for tcgen and midlat cases. +c +c Input files: +c unit 11 Unblocked GRIB1 file containing model data +c unit 12 Text file containing TC Vitals card for current time +c unit 31 Unblocked GRIB index file +c +c Output files: +c unit 61 Output file with forecast positions every 12h from +c vt=00h to the end of the forecast +c unit 62 Output file in ATCF format, with forecast positions +c at vt = 12, 24, 36, 48 and 72h, plus wind speeds. +c unit 63 Output file with forecast wind radii for 34, 50 and +c 64 knot thresholds in each quadrant of each storm. +c +c Subprograms called: +c read_nlists Read input namelists for input date & storm number info +c read_tcv_card Read TC vitals file to get initial storm position +c getgridinfo Read GRIB file to get basic grid information +c tracker Begin main part of tracking algorithm +c +c Attributes: +c Language: Standard Fortran_90 +c +c$$$ +c +c------- +c +c LOCAL: +c +c ifhours: Integer array holding numerical forecast times for +c the input model (99 = no more times available). +c These values are read in via a namelist. +c Model numbers used: (1) GFS, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) NAM, (7) NOGAPS, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble (13) SREF +c Ensemble, (14) NCEP Ensemble (from ensstat mean +c fields), (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) Ensemble RELOCATION (21) UKMET hi-res (NHC) +c stormswitch: This switch tells how to handle each storm in +c the TCV file: +c 1 = process this storm for this forecast hour. +c 2 = Storm was requested to be tracked, but either +c the storm went off the grid (regional models), +c the storm dissipated, or the program was +c unable to track it. +c 3 = Storm was NOT requested to be tracked at all. +c storm: An array of type tcvcard. Each member of storm +c contains a separate TC Vitals card. +c maxstorm: Maximum number of storms the system is set up to +c handle at any 1 time. +c slonfg,slatfg: Holds first guess positions for storms. The +c very first, first guess position is read from the +c TC vitals card. (maxstorm,maxtime) +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms) +c + USE def_vitals; USE inparms; USE set_max_parms; USE level_parms + USE trig_vals; USE atcf; USE trkrparms; USE verbose_output +c + implicit none +c + logical(1) file_open + integer date_time(8) + character (len=10) big_ben(3) + integer itret,iggret,iicret,igcret,iret,ifhmax,maxstorm,numtcv + integer iocret + integer, parameter :: lugb=11,lugi=31,lucard=12,lgvcard=14,lout=51 +c + type (datecard) inp + type (trackstuff) trkrinfo + +c -------------------------------------------------------- + + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: beginning ... ',i2.2,':',i2.2,':',i2.2) + + call w3tagb('GETTRK ',1999,0104,0058,'NP22 ') + + pi = 4. * atan(1.) ! Both pi and dtr were declared in module + dtr = pi/180.0 ! trig_vals, but were not yet defined. +c + call read_nlists (inp,trkrinfo) + + call read_fhours (ifhmax) + + call read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_tcv_card, num vitals = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_tcv_card, rc= ',iret + endif + goto 890 + endif + + call read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_gen_vitals, total number of vitals (both' + & ,' TC and non-TC) now = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_gen_vitals, rc= ' + & ,iret + endif + goto 890 + endif + + if (inp%file_seq == 'onebig') then + call open_grib_files (inp,lugb,lugi,'dummy','dummy',lout,iret) + if (iret /= 0) then + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in open_grib_files, rc= ' + & ,iret + endif + STOP 113 + endif + endif + + call tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,itret) +c +890 continue + + igcret=0 + iicret=0 + iocret=0 + + inquire (unit=lugb, opened=file_open) + if (file_open) call baclose(lugb,igcret) + inquire (unit=lugi, opened=file_open) + if (file_open) call baclose(lugi,iicret) + inquire (unit=lout, opened=file_open) + if (file_open) call baclose(lout,iocret) + if ( verb .ge. 3 ) then + print *,'baclose: igcret= ',igcret,' iicret= ',iicret + print *,'baclose: iocret= ',iocret + endif + call w3tage('GETTRK ') +c + stop + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,itret) +c +c ABSTRACT: This subroutine is the core of the program. It contains +c the main loop for looping through all the forecast hours and all +c the storms. Basically, the way it works is that it has an outer +c loop that loops on the forecast hour. At the beginning of this +c loop, the data are read in for all parameters and levels needed +c for tracking. The full regional or global grid is read in. +c If vorticity was not read in (some of the centers do not send us +c vorticity), then vorticity calculations are done on the whole +c grid at both 850 and 700 mb. Then the program goes into the inner +c loop, which loops on storm number (program originally set up to +c handle a max of 15 storms). For each storm, subroutine +c find_maxmin is called for the following parameters: Rel Vort and +c geopotential hgt at 700 & 850 mb, and MSLP. Within find_maxmin, +c a barnes analysis is performed over the guess position of the +c storm to find the max or min value, and then iteratively, the +c grid size is cut in half several times and the barnes analysis +c rerun to refine the positioning of the max or min location. After +c the center positions for these parameters have been obtained, +c subroutine get_uv_center is called to get a center fix for the +c minimum in the wind field, specifically, a minimum in the +c magnitude of the wind speed (vmag). The calculation of the vmag +c minimum is done differently than the calculation for the other +c parameters; for vmag, the grid near the storm center guess +c position is interpolated down to a very fine grid, and then +c find_maxmin is called and a barnes analysis is done on that +c smaller grid. For vmag, there are no further calls made to barnes +c with a smaller grid, since the grid has already been interpolated +c down to a smaller grid. Once all of the parameter center fixes +c have been made, subroutine fixcenter is called to average these +c positions together to get a best guess fix position. Then a check +c is done with a call to subroutine is_it_a_storm to make sure that +c the center that we have found does indeed resemble a tropical +c cyclone. Finally, subroutine get_next_ges is called to make a +c guess position for the next forecast time for this storm. +c +c INPUT: +c inp contains input date and model number information +c maxstorm maximum # of storms to be handled +c numtcv number of storms read off of the tcvitals file +c ifhmax max number of analysis & forecast times to be handled +c trkrinfo derived type that holds/describes various tracker parms +c +c OUTPUT: +c itret return code from this subroutine +c +c LOCAL PARAMETERS: +c storm contains the tcvitals for the storms +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c maxtime Max number of forecast times program can track +c maxtp Max number of tracked parameters program will track. +c Currently (7/97), this maxtp is 9, and these 9 are +c listed just a few lines below. +c readflag L Indicates status of read for each of 13 parms: +c 1: 850 mb absolute vorticity +c 2: 700 mb absolute vorticity +c 3: 850 mb u-comp +c 4: 850 mb v-comp +c 5: 700 mb u-comp +c 6: 700 mb v-comp +c 7: 850 mb gp hgt +c 8: 700 mb gp hgt +c 9: MSLP +c 10: near-surface u-comp +c 11: near-surface v-comp +c 12: 500 mb u-comp +c 13: 500 mb v-comp +c 14: Mean temperature, centered at 400 mb +c +c calcparm L indicates which parms to track and which not to. +c Array positions are defined exactly as for clon +c and clat, listed next, except that, in general, when +c flag 3 is set to a value, flag 4 is set to the same +c value as 3, and when flag 5 is set to a value, flag +c 6 is set to the same value as 5. This is because +c 3 & 4 are for the 850 mb winds, and if either u or +c v is missing, we obviously can't calculate the +c magnitude of the wind. The same applies for 5 & 6, +c which are for the 700 mb winds. +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms). +c For the third position (max_#_parms), here they are: +c 1: Relative vorticity at 850 mb +c 2: Relative vorticity at 700 mb +c 3: Vector wind magnitude at 850 mb +c 4: NOT CURRENTLY USED +c 5: Vector wind magnitude at 700 mb +c 6: NOT CURRENTLY USED +c 7: Geopotential height at 850 mb +c 8: Geopotential height at 700 mb +c 9: Mean Sea Level Pressure +c 10: Vector wind magnitude at 10 m +c 11: Relative vorticity at 10 m +c xmaxwind Contains maximum near-surface wind near the storm +c center for each storm at each forecast hour. +c stderr Standard deviation of the position "errors" of the +c different parameters for each storm at each time. +c fixlat,fixlon: Contain the final coordinates for each storm at +c each forecast hour. These coordinates are a +c weighted average of all the individual parameter +c positions (hgt, zeta, mslp, vmag). +c cvort_maxmin: Contains the characters 'max' or 'min', and is +c used when calling the find_maxmin routine for the +c relative vorticity (Look for max in NH, min in SH). +c vradius Contains the distance from the storm fix position to +c each of the various near-surface wind threshhold +c distances in each quadrant. +c (3,4) ==> (# of threshholds, # of quadrants) +c See subroutine getradii for further details. +c wfract_cov Fractional coverage (areal coverage) of winds +c exceeding a certain threshold (34, 50, 64 kts) in +c each quadrant. +c (5,5,3) ==> (# of quadrants + 1, # of distance bins, +c # of thresholds). +c The "extra" array size for quadrants (5, instead of 4) +c is there to hold the total (i.e., "whole disc") +c statistics. +c See subroutine get_fract_wind_cov for further details +c +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c isastorm Character array used in the call to is_it_a_storm, +c tells whether the minimum requirement for an MSLP +c gradient was met (isastorm(1)), whether for the midlat +c and tcgen cases if a closed mslp contour was found +c (isastorm(2)), and if a circulation exists at 850 mb +c (isastorm(3)). Can have a value of 'Y' (requirement +c met), 'N' (requirement not met) or 'U' (requirement +c undetermined, due to the fact that no center location +c was found for this parameter). +c maxmini These 2 arrays contain the i and j indeces for the +c maxminj max/min centers that are found using the rough check +c in first_ges_ctr and subsequent routines. Only needed +c for a midlatitude or a genesis run, NOT needed for a +c TC tracker run. +c stormct Integer: keeps and increments a running tab of the +c number of storms that have been tracked at any time +c across all forecast hours. Used only for midlat or +c tcgen runs. +c gridprs This contains the actual value of the minimum pressure +c at a gridpoint. The barnes analysis will return an +c area-averaged value of pressure; this variable will +c contain the actual minimum value at a gridpoint near +c the lat/lon found by the barnes analysis. +c closed_mslp_ctr_flag This flag keeps track of the value of the +c closed contour flag returned from subroutine +c check_closed_contour. +c vt850_flag This flag keeps track of the value of the flag for +c the 850 mb Vt check. +c----- +c + USE def_vitals; USE inparms; USE tracked_parms; USE error_parms + USE set_max_parms; USE level_parms; USE grid_bounds; USE trkrparms + USE contours; USE atcf; USE radii; USE trig_vals; USE phase + USE gen_vitals; USE structure; USE verbose_output + USE waitfor_parms; USE module_waitfor +c + implicit none +c + type (datecard) inp + type (trackstuff) trkrinfo + type (cint_stuff) contour_info +c + character, allocatable :: closed_mslp_ctr_flag(:,:)*1 + character, allocatable :: vt850_flag(:,:)*1 + character :: r34_check_okay*1 + character :: need_to_expand_r34(4)*1 + integer, parameter :: nparms=14 + real, allocatable :: prstemp(:),iwork(:) + integer, parameter :: numdist=14,numquad=4,lout=51 + integer, allocatable :: prsindex(:) + integer imax,jmax,ifh,ist,irf,jj,istmp,ifhtemp,itret,ivpa + integer isiret1,isiret2,isiret3,idum,m,iix,jjx,imode,numtcv + integer iha,isa,iua,iva,iza,maxstorm,ivort,ifix,jfix,issret + integer imoa,imoca,iksa,isda,ileadtime,leadtime_check + integer ioaret,ioaxret,ifgcret,ifmret,igugret,isoiret,icccret + integer igrret,igmwret,iorret,ignret,iovret,icbret,igucret,ita + integer ifilret,ifret,iaret,isret,iotmret,iwa,iisa,sl_counter + integer iicret,igcret,pfcret + logical(1), allocatable :: valid_pt(:,:) + logical(1), allocatable :: masked_outc(:,:),masked_out(:,:) + logical(1) readflag(nparms),calcparm(maxtp,maxstorm) + logical(1) tracking_previously_known_storms + logical(1) need_to_flip_lats,need_to_flip_lons + logical(1) file_open + character cvort_maxmin*3,isastorm(3)*1,ccflag*1,gotten_avg_value*1 + character cmaxmin*3,get_last_isobar_flag*1,wcore_flag*1 + character gfilename*120,ifilename*120,gridmove_status*7 + integer vradius(3,4),igridzeta(nlevm1),imeanzeta(nlevm1) + integer maxmini(maxstorm),maxminj(maxstorm),pdf_ct_bin(16) + integer ifcsthour,stormct,prevstormct,kf,istmspd,istmdir,iggret + integer igiret,iuret,jdum,icount,ilonfix,jlatfix,igpret,ifhmax + integer ibeg,jbeg,iend,jend,ix1,ix2,n,ilev,npts,icpsa,igzvret + integer igfwret,ioiret,igisret,iofwret,iowsret,igwsret,igscret + integer pdf_ct_tot,lugb,lugi,iret,icmcf,iccfh,ivt8f + integer waitfor_gfile_status,waitfor_ifile_status + integer wait_max_ifile_wait,ivr,r34_good_ct + integer date_time(8) + character (len=10) big_ben(3) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridprs(maxstorm,maxtime) + real wfract_cov(5,5,3) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real ike(max_ike_cats) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmaxwind(maxstorm,maxtime),xmeanzeta + real stderr(maxstorm,maxtime),xval(maxtp),cps_vals(3) + real gridpoint_maxmin,dist,distnm,xknots,xmaxspeed + real uvgeslon,uvgeslat,xavg,stdv,search_cutoff,re,ri,dx,dy + real xinp_fixlat,xinp_fixlon,degrees,plastbar,rlastbar + real xinterval_fhr,cc_time_sum_tot,cc_time_sum_yes + real max_mslp_850,rmax,sdp,wdp,paramb,vtl_slope,vtu_slope + real xsfclon,xsfclat,cc_time_pct,radmax,r34_dist_thresh + real prev_latmax,prev_latmin,prev_lonmax,prev_lonmin + real vradius_km,hold_old_contint,tcv_max_wind_ms + real tcv_mslp_pa,r34_from_tcv,roci_from_tcv + real proci_from_tcv + + character(pfc_cmd_len) :: pfc_final +c + prev_latmax = -999.0 + prev_latmin = -999.0 + prev_lonmax = -999.0 + prev_lonmin = -999.0 + + icmcf = 0 + ivt8f = 0 + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + allocate (closed_mslp_ctr_flag(maxstorm,ifhmax),stat=icmcf) + allocate (vt850_flag(maxstorm,ifhmax),stat=ivt8f) + ! Initialize flags to 'u', not 'n'. That way, + ! when we are evaluating its value back over recent past hours, + ! we can distinguish a "no" value from an initialized value of + ! 'u' for which a storm hadn't yet been detected. + closed_mslp_ctr_flag = 'u' + vt850_flag = 'u' + endif + + allocate (prsindex(maxstorm),stat=iisa) + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iisa /= 0 .or. iva /= 0 .or. iwa /= 0 .or. icmcf /= 0 .or. + & ivt8f /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating prsindex,' + print *,'!!! prstemp or iwork array for storms: iisa = ',iisa + print *,'!!! iva= ',iva,' iwa= ',iwa,' icmcf= ',icmcf + print *,'!!! ivt8f= ',ivt8f + endif + itret = 94 + return + endif + + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + clon = 0.0 + clat = 0.0 + stderr = stermn ! initialize stderr to 0.1 (error_parms) + itret = 0 + xmaxwind = 0.0 + stormct = 0 + + ! It is critical to initialize the gridprs array to something + ! greater than normal atmospheric pressures (I've chosen 9999.99 + ! mb). This is so that in the sort on pressure before stormloop, + ! the top of the sorting index array will be filled with pressure + ! values from active storms, while those inactive 9999 storms + ! will fill the bottom of the sorting index array (prsindex). + + gridprs = 999999.0 + fixlon = -999.0 + fixlat = -999.0 + + if (inp%file_seq == 'multi') then + ! Each tau will have a separate file, starting with unit + ! number 300 (GRIB data) and 800 (GRIB index file) and + ! incrementing upwards from there for each tau. + lugb = 300 + lugi = 800 + else + ! All lead times are included in one big file. These values + ! for lugb and lugi will remain static for all taus. + lugb = 11 + lugi = 31 + endif + + ifh = 1 + + if ( verb .ge. 3 ) then + print *,'top of tracker, ifh= ',ifh,' ifhmax= ',ifhmax + endif + + ifhloop: do while (ifh <= ifhmax) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------*' + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* New forecast hour: ',i4,':',i2.2) + print *,'*-------------------------------------------*' + endif + + if (inp%file_seq == 'multi') then + lugb = lugb + 1 + lugi = lugi + 1 + + call get_grib_file_name (ifh,gfilename,ifilename) + + if (use_waitfor == 'y') then + + ! First check for existence of grib file.... + + call waitfor(trim(gfilename),waitfor_gfile_status,wait_min_age + & ,wait_min_size,wait_max_wait,wait_sleeptime) + if (waitfor_gfile_status /= 0) then + print *,' ' + write(6,405) + write(6,406) wait_max_wait,trim(gfilename) + 405 format('ERROR: TIMEOUT from waitfor for GRIB file.') + 406 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + ! Now check for existence of index file. Use a separate + ! max_wait time -- a much shorter one -- since once the + ! grib file is there, the index file should appear within + ! a matter of seconds. Also, the index file is much + ! smaller, so set the wait_min_size accordingly. + + wait_max_ifile_wait = 180 + wait_min_size = 500 + call waitfor(trim(ifilename),waitfor_ifile_status,wait_min_age + & ,wait_min_size,wait_max_ifile_wait,wait_sleeptime) + if (waitfor_ifile_status /= 0) then + print *,' ' + write(6,415) + write(6,416) wait_max_ifile_wait,trim(ifilename) + 415 format('ERROR: TIMEOUT from waitfor for INDEX file.') + 416 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + endif + + call open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: from open_grib_files, rc= ' + & ,iret + print *,'!!! Files after hour0 are missing, exiting normally' + stop 0 + endif + endif + + print *,'TEST before getgridinfo in sub tracker' + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is CLOSED' + endif + + call getgridinfo (imax,jmax,ifh,dx,dy,lugb,lugi,trkrinfo + & ,need_to_flip_lats,need_to_flip_lons,inp,iggret) + print *,'TEST after getgridinfo in sub tracker, iggret= ',iggret + + if (iggret /= 0) then + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in getgridinfo, rc= ' + & ,iggret + endif + stop 95 + endif + + if (inp%modtyp == 'regional' .and. inp%nesttyp == 'moveable') + & then + if (glatmax == prev_latmax .and. glatmin == prev_latmin .and. + & glonmax == prev_lonmax .and. glonmin == prev_lonmin) then + ! The moveable, nested regional grid has not moved since + ! the last lead time. This could be an indication that the + ! model lost the storm and so the grid has not moved to + ! stay with the cyclone center. Set a flag to indicate this. + gridmove_status = 'stopped' + else + gridmove_status = 'moving' + endif + else + gridmove_status = 'notappl' + endif + + prev_latmax = glatmax + prev_latmin = glatmin + prev_lonmax = glonmax + prev_lonmin = glonmin + + gotten_avg_value = 'n' + +c First, allocate the working data arrays.... + + if (allocated(valid_pt)) deallocate (valid_pt) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + + ! Allocate all of the allocatable arrays.... + + allocate (valid_pt(imax,jmax),stat=ivpa) + allocate (zeta(imax,jmax,nlevzeta),stat=iza) + allocate (u(imax,jmax,nlevs),stat=iua) + allocate (v(imax,jmax,nlevs),stat=iva) + allocate (hgt(imax,jmax,nlevm1),stat=iha) + allocate (slp(imax,jmax),stat=isa) + allocate (tmean(imax,jmax),stat=ita) + allocate (masked_out(imax,jmax),stat=imoa) + allocate (masked_outc(imax,jmax),stat=imoca) + + ita=0 + icpsa=0 + if (phaseflag == 'y') then + if (phasescheme == 'cps' .or. phasescheme == 'both') then + if (allocated(cpshgt)) deallocate (cpshgt) + allocate (cpshgt(imax,jmax,nlevs_cps),stat=icpsa) + endif + endif + + if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. + & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. + & imoa /= 0 .or. imoca /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating arrays.' + print *,'!!! iza = ',iza,' iua= ',iua,' iha= ',iha + print *,'!!! iva = ',iva,' isa= ',isa,' icpsa= ',icpsa + print *,'!!! iksa = ',iksa,' isda= ',isda,' ivpa= ',ivpa + print *,'!!! ita = ',ita,' imoa= ',imoa,' imoca= ',imoca + endif + itret = 94 + return + endif + + masked_out = .false. ! Initialize all pts to false at each hr + masked_outc = .false. ! Initialize all pts to false at each hr + + if ( verb .ge. 3 ) then + print *,'in beginning of tracker, imax= ',imax,' jmax= ',jmax + endif + +c Initialize all readflags to NOT FOUND for this forecast time, +c then call subroutine to read data for this forecast time. + + zeta = -9999.0 + u = -9999.0 + hgt = -9999.0 + v = -9999.0 + slp = -9999.0 + tmean = -9999.0 + + readflag = .FALSE. + + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: b4 getdata ... ',i2.2,':',i2.2,':',i2.2) + + call getdata (readflag,valid_pt,imax,jmax,ifh + & ,need_to_flip_lats,need_to_flip_lons,inp,lugb,lugi + & ,trkrinfo) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,32) date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: after getdata ... ',i2.2,':',i2.2,':',i2.2) + +c Count how many parms were successfully read for this fcst time. +c Also, for right now, put the value of readflag into all of the +c calcparms for parameters 3 through 9. Note that in getdata we +c read in 14 parms, but in this next loop we only check the +c readflags up to maxtp (= 11 as of 8/2009). That's because +c parms 12 & 13 are for 500 mb u & v, which are not used for +c tracking, only for calculating the deep layer mean wind for +c the next guess, and parm 14 is the 300-500 mb mean temperature, +c which is used for determining storm phase. Parms 10 & 11 are +c for the near-surface winds, which are used in estimating surface +c winds near the storm, and will now also be used as a secondary +c parameter for position estimates. + + idum = 0 + do irf = 1,maxtp + if (readflag(irf)) idum = idum + 1 + if (irf > 2) then + do jj=1,maxstorm + if (irf == 10 .or. irf == 11) then +ctpm6/14 revert +cJ.Peng---2014-10-03------------------------------ +c print *,'Setting calcparm to false for 10m stuff...' +c if (inp%model == 1) then + calcparm(irf,jj) = .false. +c else +c calcparm(irf,jj) = readflag(irf) +c endif + else + calcparm(irf,jj) = readflag(irf) + endif + enddo + endif + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Of ',maxtp,' trackable parms, you read in ',idum + print *,'parms for this fcst hour from the input grib file.' + endif + +c If not enough tracked parms were read in, exit the program.... + + if (idum == 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in subroutine tracker' + print *,'!!! Not enough tracked parms read in from getdata.' + print *,'!!! Check for a problem with the input GRIB file.' + print *,'!!! Model identifier = ',inp%model + print *,'!!! STOPPING EXECUTION FOR THIS MODEL' + endif + itret = 99 + ifhtemp = ifh + do while (ifhtemp <= ifhmax) + do istmp=1,maxstorm + fixlon (istmp,ifhtemp) = -999.0 + fixlat (istmp,ifhtemp) = -999.0 + enddo + ifhtemp = ifhtemp + 1 + enddo +cJ.P08042014 call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cJ.P08042014 call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +c & ,ioaret) + if (ifh == 1) then + ! Per Jim Gross (1/01), if the tracker ran but was unable + ! to get an initial fix (or, in this case, unable to get + ! the data needed to run), write out zeroes for the 00h + ! fixes to indicate that the tracker ran unsuccessfully, + ! but don't write out any subsequent forecast times + ! with zeroes.... + vradius = 0 + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initial value of 'undetermined' + do istmp = 1,maxstorm + if (stormswitch(istmp) /= 3) then + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0,-999.0,inp,istmp + & ,ifcsthour,0.0,0.0,vradius,maxstorm + & ,trkrinfo,-99.0,-99.0,-99.0,cps_vals + & ,wcore_flag,ioaxret) + call output_hfip (-999.0,-999.0,inp,istmp + & ,ifh,0.0,0.0,vradius,-99.0,ioaxret) + endif + enddo + endif + return + endif + +c Parameters 1 & 2 are abs vorticity at 850 & 700. If the data +c files had this parm at 850 & 700 (ECMWF & UKMET do NOT), then +c we don't need to re-calculate relative vorticity, we just need +c to subtract out the Coriolis component. If the files did not +c have vorticity, then we need to calculate relative vorticity. +c If we're able to read vorticity or calculate it, then set the +c vorticity calcparms to TRUE for all storms for now. + + do ivort=1,2 + + if (readflag(ivort)) then + + call subtract_cor (imax,jmax,dy,ivort) + + do jj=1,maxstorm + calcparm(ivort,jj) = .TRUE. + enddo + else + if (ivort == 1) then + if (readflag(3) .and. readflag(4)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(1,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + endif + else + if (readflag(5) .and. readflag(6)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(2,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + endif + endif + endif + + enddo + +c Compute the sfc vorticity if sfc_u and sfc_v have been read in. + + if (readflag(10) .and. readflag(11)) then + ! The 3 in the next call to rvcal is to indicate the 3rd level + ! for the zeta array, which is for the surface (or 10m) data. + call rvcal (imax,jmax,dx,dy,3,valid_pt) + do jj=1,maxstorm +ctpm6/14 calcparm(10,jj) = .TRUE. +ctpm6/14 calcparm(11,jj) = .TRUE. +cJ.Peng--------2014-10-27-------------------------- +c calcparm(10,jj) = .TRUE. +c calcparm(11,jj) = .TRUE. +c if (inp%model == 1) then + calcparm(10,jj) = .FALSE. ! Turned off for GFS hires 6/14 + calcparm(11,jj) = .FALSE. ! Turned off for GFS hires 6/14 +c endif + enddo + else + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + calcparm(11,jj) = .FALSE. + enddo + endif + +c --------------------------------------------------------------- +c Now call find_maxmin for the variables zeta, hgt and slp. Only +c process those storms for which stormswitch is set to 1. If a +c storm is selected to be processed, we still have to check the +c calcparm for each parameter, to make sure that the particular +c parm exists at that level and is able to be processed. +c +c The following commented-out data statements are just included +c as a reference so you can see the array positioning of the +c different parameters and levels: +c +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,sfc,sfc +c ,100,100,100/ +c data iglev /850,700,850,850,700,700,850,700,0,sfc,sfc +c ,500,500,400/ +c +c NOTE: For mid-latitude cases, we will track ONLY mslp, which +c is why we set all the other calcparms to 'false' just below. + + if (trkrinfo%type == 'midlat') then + do m = 1,maxstorm + calcparm(1,m) = .false. + calcparm(2,m) = .false. + calcparm(3,m) = .false. + calcparm(4,m) = .false. + calcparm(5,m) = .false. + calcparm(6,m) = .false. + calcparm(7,m) = .false. + calcparm(8,m) = .false. + calcparm(10,m) = .false. + calcparm(11,m) = .false. + enddo + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + call sort_storms_by_pressure (gridprs,ifh,maxstorm,prsindex + & ,issret) + if (ifh == 1) then + stormct = numtcv + endif + endif + + prevstormct = stormct + tracking_previously_known_storms = .true. + + stormloop: do sl_counter = 1,maxstorm + + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initialized value of 'undetermined' + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + ist = prsindex(sl_counter) + else + ist = sl_counter + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + + if (ist == (prevstormct + 1)) then + + ! For the mid-latitude and tropical cyclogenesis cases, we + ! need to scan the mslp field to find new storms. If we + ! are at this point inside the if statement in stormloop, + ! then that means we have looped through and attempted to + ! track all storms that have already been found up to this + ! point in the forecast, and we need to scan the field for + ! any new storms at this forecast hour. If this is for + ! forecast hour = 0, then right off the bat we may be + ! scanning the field (if there were no tcvitals records + ! read in for this forecast), since ist = 1 and + ! (prevstormct + 1) = 0 + 1 = 1. All that the call just + ! below to first_ges_center does is return a rough idea + ! of the location of new lows; more specific locations are + ! obtained through the barnes analysis tracking algorithm + ! further below. + + if (readflag(9)) then + if (ifh > 1) then + ! We need the use of 2 different masks. One + ! (masked_out) is to be used when looking for new lows, + ! so that after we find a new low, we mask out the + ! surrounding area so we don't find it on a subsequent + ! search for this forecast hour. The other + ! (masked_outc) is used in the routine to check for a + ! closed contour. If checking for a closed contour + ! at, say 70W/25N, this and surrounding points may have + ! already been masked out in first_ges_center, so "N" + ! would misleadingly/incorrectly be returned from + ! check_closed_contour, so that is why we need 2 masks. + ! But now after the first forecast hour (t=0), the way + ! we have this set up is that we track previously known + ! storms first, and once we're done with them, we + ! search for new storms at that same forecast hour. + ! But when looking for new storms, we need to know the + ! positions of the previously tracked storms at this + ! current forecast hour, so we copy the masked_outc + ! array to masked_out in this case.... + + masked_out = masked_outc + + endif + call first_ges_center (imax,jmax,dx,dy,'mslp',slp + & ,'min',trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) + tracking_previously_known_storms = .false. + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In subroutine tracker, readflag' + print *,'!!! for mslp indicates that the mslp data' + print *,'!!! is not available for this forecast ' + print *,'!!! hour, and it is needed for a "midlat"' + print *,'!!! or "tcgen" run of the tracker. ' + print *,'!!! We will exit....' + print *,'!!! readflag(9) = ',readflag(9) + print *,'!!! ifh= ',ifh + print *,' ' + endif + itret = 98 + return + endif + endif + endif + + xval = 0.0 ! initialize entire xval array to 0 + isastorm = 'U' ! re-initialize flag for each time, each storm + + select case (stormswitch(ist)) + + case (1) + + vradius = 0 + + if ( verb .ge. 2 ) then + print *,' ---------------------------------------------' + print *,' | *** TOP OF STORM LOOP *** ' + print *,' | Beginning of storm loop in tracker for' + print *,' | Storm number ',ist + write (6,418) ifhours(ifh),ifclockmins(ifh) + 418 format (1x,' | Forecast hour: ',i4,':',i2.2) + print *,' | Storm name = ',storm(ist)%tcv_storm_name + print *,' | Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,' ---------------------------------------------' + print *,' ' + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3 + & ,'_',i3.3,a1,'_',i4.4,a1,'_',a3) + + endif +c First, make sure storm is within the grid boundaries... + + call check_bounds (slonfg(ist,ifh),slatfg(ist,ifh),ist,ifh + & ,trkrinfo,icbret) + if (icbret == 95) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + + if (slatfg(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + if (calcparm(1,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,1),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(1,ist),clon(ist,ifh,1),clat(ist,ifh,1) + & ,xval(1),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(2,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,2),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(2,ist),clon(ist,ifh,2),clat(ist,ifh,2) + & ,xval(2),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(7,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,1),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(7,ist) + & ,clon(ist,ifh,7),clat(ist,ifh,7),xval(7) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(8,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,2),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(8,ist) + & ,clon(ist,ifh,8),clat(ist,ifh,8),xval(8) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(9,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for mslp' + endif + + call find_maxmin (imax,jmax,dx,dy,'slp' + & ,slp,'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(9,ist) + & ,clon(ist,ifh,9),clat(ist,ifh,9),xval(9) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(11,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for sfc zeta' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,3),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(11,ist),clon(ist,ifh,11),clat(ist,ifh,11) + & ,xval(11),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + +c Now get centers for V magnitude at 700 & 850 mb. First, +c get a modified guess lat/lon position for V magnitude. +c Do this because it's more crucial to have a better first +c guess position for the wind minimum than it is for the +c other parms, since in addition to the wind minimum at the +c center of the storm, you can also have many more wind +c minima outside the RMW (this is more of a concern in +c smaller and weaker storms). This modified guess position +c will be an average of the first guess position for this +c time and the fix positions for this time from some of the +c other parameters. + + if (calcparm(3,ist) .and. calcparm(4,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_uv_center for 850 mb ' + endif + + call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,850,valid_pt,calcparm(3,ist) + & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo + & ,igucret) + if (igucret /= 0) then + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + endif + else + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + clon(ist,ifh,3) = 0.0 + clat(ist,ifh,3) = 0.0 + endif + endif + + if (calcparm(5,ist).and. calcparm(6,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_uv_center for 700 mb ' + endif + + call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,700,valid_pt,calcparm(5,ist) + & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo + & ,igucret) + if (igucret /= 0) then + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + endif + else + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + clon(ist,ifh,5) = 0.0 + clat(ist,ifh,5) = 0.0 + endif + endif + + if (calcparm(10,ist) .and. igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_uv_center for the surface ' + endif + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,1020,valid_pt,calcparm(10,ist) + & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) + & ,trkrinfo,igucret) + if (igucret /= 0) then + calcparm(10,ist) = .FALSE. + endif + else + calcparm(10,ist) = .FALSE. + clon(ist,ifh,10) = 0.0 + clat(ist,ifh,10) = 0.0 + endif + +c ------------------------------------------------------ +c All of the parameter center fixes have been done. Now +c average those positions together to get the best guess +c fix position. If a center fix is able to be made, then +c call subroutine get_max_wind to get the maximum near- +c surface wind near the center, and then call get_next_ges +c to get a guess position for the next forecast hour. + + if (stormswitch(ist) == 1) then + + call fixcenter (clon,clat,ist,ifh,calcparm + & ,slonfg(ist,ifh),slatfg(ist,ifh),inp + & ,stderr,fixlon,fixlat,xval,maxstorm,ifret) + + if (ifret == 0) then + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'regional')then + if (fixlon(ist,ifh) > (trkrinfo%eastbd + 7.0) .or. + & fixlon(ist,ifh) < (trkrinfo%westbd - 7.0) .or. + & fixlat(ist,ifh) > (trkrinfo%northbd + 7.0) .or. + & fixlat(ist,ifh) < (trkrinfo%southbd - 7.0)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! will NOT be made for this time due' + print *,'!!! the storm being more than 7 degrees' + print *,'!!! outside the user-specified lat/lon' + print *,'!!! bounds for this run. We will stop' + print *,'!!! tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + 432 format (1x,'!!! Fcst hr = ',i4,':',i2.2) + print *,'!!! fixlat= ',fixlat(ist,ifh) + print *,'!!! fixlon= ',fixlon(ist,ifh) + print *,'!!! User East Bound = ',trkrinfo%eastbd + print *,'!!! User West Bound = ',trkrinfo%westbd + print *,'!!! User North Bound = ',trkrinfo%northbd + print *,'!!! User South Bound = ',trkrinfo%southbd + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + endif + cycle stormloop + endif + endif + else + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + +c Just because we've found a center doesn't mean there is +c actually a storm there. I noticed in the first year that +c for some decaying or just weak storms, the tracker would +c identify a center to follow, but it may have only been +c a weak trough passing by, or something else that's not +c our storm. This next subroutine checks to see that the +c surface pressure gradient and/or tangential winds at +c 850 mb resemble a storm. It is called twice; the first +c time for MSLP, the 2nd time for 850 mb winds. We will +c apply these storm-checking criteria if either the mslp +c or v850 check come back negative. Remember, there +c is the possibility that centers could not be found for +c 1 or both of these parameters, in which case the isastorm +c flag will have a value of 'U', for "undetermined". + + isiret1 = 0; isiret2 = 0; isiret3 = 0 + + if (ifret == 0) then + + if (calcparm(9,ist)) then + + ! Do a check of the mslp gradient.... + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,clon(ist,ifh,9),clat(ist,ifh,9) + & ,xval(9),trkrinfo,isastorm(1),isiret1) + + endif + + ! If we have found a valid mslp gradient, then make + ! a call to fix_latlon_to_ij to (1) get the actual + ! gridpoint value of the mslp (the value previously + ! stored in xval(9) is an area-averaged value coming + ! from the barnes analysis), and (2) to get the + ! (i,j) indeces for this gridpoint to be used in the + ! call to check_closed_contour below. + ! + ! NOTE: If a mslp fix was not made, or if the mslp + ! "isastorm" flag comes back as no, we make the same + ! call to fix_latlon_to_ij, but we use the mean fix + ! position as our input to search around, and then + ! basically we just find the lowest mslp near that + ! mean fix position. There is a check on the value + ! of xinp_fixlat and xinp_fixlon to make sure that + ! they contain valid values and not just the + ! initialized -999 values. + + if (isiret1 == 0 .and. isastorm(1) == 'Y') then + xinp_fixlat = clat(ist,ifh,9) + xinp_fixlon = clon(ist,ifh,9) + else + xinp_fixlat = fixlat(ist,ifh) + xinp_fixlon = fixlon(ist,ifh) + endif + + if (xinp_fixlat > -99.0 .and. xinp_fixlon > -990.0) + & then + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,xinp_fixlon,xinp_fixlat + & ,xval(9),ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + gridprs(ist,ifh) = gridpoint_maxmin + else + ! Search went out of regional grid bounds.... + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + ! For a "tracker" case, check to see if the user has + ! requested to compute and write out the ROCI. If + ! so, then we make a call to check_closed_contour, + ! being sure to specify 999 as the number of levels + ! to check.... + + if (isiret1 == 0 .and. isastorm(1) == 'Y' .and. + & trkrinfo%type == 'tracker') then + + if (trkrinfo%want_oci) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + if (trkrinfo%contint < 400.0) then + hold_old_contint = trkrinfo%contint + trkrinfo%contint = 400.0 + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before going into routine to diagnose' + print *,'the ROCI for a tracker run, the ' + print *,'requested contour interval is being ' + print *,'adjusted up (coarser) to avoid having' + print *,'the contour check routine break and ' + print *,'return an invalid value.' + print *,'User-requested contint value (Pa) = ' + & ,hold_old_contint + print *,'Modified contint value (Pa) = ' + & ,trkrinfo%contint + endif + endif + + masked_outc = .false. + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ' + & ,rlastbar,' nm' + print *,' ' + endif + + endif + + endif + + ! For the midlat & tcgen cases, do a check to see if + ! there is a closed mslp contour. The ifix and jfix + ! values passed into check_closed_contour are the + ! values for the (i,j) at the gridpoint minimum, + ! which was obtained just above from the call to + ! fix_latlon_to_ij. + + if (isastorm(1) == 'Y' .and. isiret1 == 0 .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ',rlastbar + & ,' nm' + print *,' ' + endif + + ! This next bit of code adds a second layer of closed + ! contour checking. This is to decrease the + ! occurrence of interrupted midlat and tcgen tracks, + ! which usually happens when the closed contour + ! criterion is not met for one time period. So in + ! this next code, we check to see if the ccflag was + ! 'y' for at least half the time over the last 24h. + ! For time periods shorter than 24h (e.g., the storm + ! was just detected at 144h and we are now at 156h), + ! the threshold is still that for at least half of + ! the time the system has been detected as a storm, + ! it must have a ccflag value of 'y'. + + if (ccflag == 'y') then + closed_mslp_ctr_flag(ist,ifh) = 'y' + else + closed_mslp_ctr_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & closed_mslp_ctr_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (closed_mslp_ctr_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.50) then + ccflag = 'y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE ON CLOSED CONTOUR CHECK: The' + print *,' ccflag returned for this hour was' + print *,' NO, but a check of recent ccflags' + print *,' indicates that more than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + ccflag = 'n' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!! NOTE ON CLOSED CONTOUR CHECK: The' + print *,'!! ccflag returned for this hour was' + print *,' NO, and a check of recent ccflags' + print *,' indicates that less than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + if (ccflag == 'y') then + isastorm(2) = 'Y' + else if (ccflag == 'n') then + isastorm(2) = 'N' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*---------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*---------------------------------------*' + print *,' ' + endif + + endif + + ! For tropical cyclones, check the avg 850 mb tangential + ! windspeed close to the storm center.... + + if (trkrinfo%type == 'tcgen' .or. + & trkrinfo%type == 'tracker') then + if (calcparm(3,ist)) then + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,clon(ist,ifh,3),clat(ist,ifh,3) + & ,xval(3),trkrinfo,isastorm(3),isiret3) + + if (trkrinfo%type == 'tcgen') then + ! This next bit of code adds a second layer of 850 + ! mb Vt magnitude checking. This is to decrease + ! the occurrence of interrupted tcgen tracks, + ! which occasionally happens for weak storms when + ! this criterion is not met for one time period. + ! So in this next code, we check to see if the + ! vt850_flag was 'y' for at least 75% of the time + ! over the last 24h. For time periods shorter + ! than 24h (e.g., the storm was just detected at + ! 144h and we are now at 156h), the threshold is + ! still that for at least 75% of the time the + ! system has been detected as a storm, it must + ! have a vt850_flag value of 'y'. + + if (isastorm(3) == 'Y') then + vt850_flag(ist,ifh) = 'y' + else + vt850_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & vt850_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - + & fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (vt850_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / + & cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.75) then + isastorm(3) = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ NOTE ON Vt_850 CHECK: The ' + print *,' isastorm flag returned for ' + print *,' this hour was NO, but a' + print *,' check of recent vt850_flags' + print *,' indicates that more than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE ON Vt_850 CHECK: The ' + print *,'!!! isastorm flag returned for ' + print *,' this hour was NO, and a' + print *,' check of recent vt850_flags ' + print *,' indicates that less than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + endif + + endif + endif + + else + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + isastorm(1) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! could not be made for mslp, ' + print *,'!!! therefore we will stop tracking ' + print *,'!!! for this storm.' + endif + + else + isastorm(1) = 'N' + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a TC tracker case, a fix could' + print *,'!!! not be made using any tracked parms,' + print *,'!!! therefore we will stop tracking for' + print *,'!!! this storm.' + endif + + endif + + if ( verb .ge. 3 ) then + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + + endif + + if (isiret1 /= 0 .or. isiret2 /= 0 .or. isiret3 /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: One of the calls to ' + print *,'!!! is_it_a_storm produced an error.' + print *,'!!! Chances are this is from a call to ' + print *,'!!! get_ij_bounds, meaning we are too close' + print *,'!!! to a regional grid boundary to do this ' + print *,'!!! analysis. Processing will continue....' + print *,'!!! isiret1= ',isiret1,' isiret2= ',isiret2 + print *,'!!! isiret3= ',isiret3 + endif + + endif + + if (isastorm(1) == 'N' .or. isastorm(2) == 'N' .or. + & isastorm(3) == 'N') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! At least one of the isastorm flags from' + print *,'!!! subroutine is_it_a_storm is "N", so ' + print *,'!!! either we were unable to find a good ' + print *,'!!! mslp gradient and/or a valid 850 mb ' + print *,'!!! circulation for the storm at this time,' + print *,'!!! or, for the cases of midlat or tcgen ' + print *,'!!! tracking, a closed mslp contour could ' + print *,'!!! not be found, thus we will stop tracking' + print *,'!!! this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! mslp gradient flag = ',isastorm(1) + print *,'!!! closed contour flag = ',isastorm(2) + print *,'!!! 850 mb winds flag = ',isastorm(3) + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + + ! Now do another check for the tracker and tcgen cases. + ! If the isastorm flags for mslp gradient and v850 BOTH + ! came back positive AND you have been able to locate an + ! 850 mb vort center, just do a check to make sure that + ! the distance between the 850 vort center and the mslp + ! center is not too great. + + if (trkrinfo%type == 'tracker' .or. + & trkrinfo%type == 'tcgen') then + if (isastorm(1) == 'Y' .and. isastorm(3) == 'Y' .and. + & calcparm(1,ist) .and. stormswitch(ist) == 1) then + +c if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) >= 25.0) then +c max_mslp_850 = 405.0 +c else if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) < 25.0) then +c max_mslp_850 = 405.0 +c else +c max_mslp_850 = 323.0 +c endif + + max_mslp_850 = 400.0 + + call calcdist (clon(ist,ifh,9),clat(ist,ifh,9) + & ,clon(ist,ifh,1),clat(ist,ifh,1),dist + & ,degrees) + + if (dist > max_mslp_850) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, the dist betw' + print *,'!!! the mslp center & the 850 zeta ' + print *,'!!! center is too great, thus we will' + print *,'!!! stop tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max dist allowed (km) = ' + & ,max_mslp_850 + print *,'!!! Actual distance (km) = ',dist + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Distance between the parm centers for' + print *,'850 zeta and mslp is ',dist,' (km)' + endif + + endif + endif + endif + + ! Do one final check. Check the new fix position and + ! the old fix position and calculate the speed that the + ! storm would have had to travel to get to this point. + ! If that speed exceeds a certain threshold (~60 kt), + ! assume you're tracking the wrong thing and quit. + ! Obviously, only do this for times > 00h. The check + ! in the if statement to see if the previous hour's + ! lats and lons were > -999 is for the midlat and + ! tcgen cases -- remember, they can have genesis at + ! any hour of the forecast, in which case the previous + ! forecast hour's lat & lon would be -999. + + if (ifh > 1 .and. stormswitch(ist) == 1) then + if (fixlon(ist,ifh-1) > -999.0 .and. + & fixlat(ist,ifh-1) > -999.0 ) then + + if (trkrinfo%type == 'midlat') then + xmaxspeed = maxspeed_ml + else + xmaxspeed = maxspeed_tc + endif + + call calcdist (fixlon(ist,ifh-1),fixlat(ist,ifh-1) + & ,fixlon(ist,ifh),fixlat(ist,ifh),dist + & ,degrees) + + ! convert distance from km to nm and get speed. + + distnm = dist * 0.539638 + xinterval_fhr = fhreal(ifh) - fhreal(ifh-1) + xknots = distnm / xinterval_fhr + + if (xknots > xmaxspeed) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, calculated spd' + print *,'!!! of the storm from the last position' + print *,'!!! to the current position is too high,' + print *,'!!! so we will stop tracking this storm' + print *,'!!! (For fear that we are not actually ' + print *,'!!! tracking our storm, but have instead' + print *,'!!! locked onto some other feature....)' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max speed allowed (kt) = ',xmaxspeed + print *,'!!! Actual speed (kt) = ',xknots + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'The average speed that the storm moved' + print *,'at since the previous forecast time is' + & ,xknots,' knots.' + endif + + endif + + endif + + endif + + endif + +c Now get the maximum near-surface wind speed near the storm +c center (get_max_wind). Also, call getradii to get the +c radii in each storm quadrant of gale-force, storm-force +c and hurricane force winds. + + if (readflag(10) .and. readflag(11) .and. ifret == 0 + & .and. stormswitch(ist) == 1) then + call get_max_wind (fixlon(ist,ifh),fixlat(ist,ifh) + & ,imax,jmax,dx,dy,valid_pt,levsfc + & ,xmaxwind(ist,ifh),trkrinfo,rmax,igmwret) +c if (igmwret /= 0 .and. gridmove_status == 'stopped') then + if (igmwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Return code from get_max_wind is /= 0. ' + print *,'!!! rcc= igmwret= ',igmwret + print *,'!!! Also, this is a moveable, regional grid' + print *,'!!! and the grid did not change from last' + print *,'!!! lead time to current one, so what has' + print *,'!!! likely happened is that the storm has ' + print *,'!!! moved close to the edge of the nested ' + print *,'!!! grid domain, but the nested grid itself' + print *,'!!! had stopped moving, probably because it' + print *,'!!! dropped or lost the storm.' + print *,'!!! ' + print *,'!!! TRACKING WILL STOP FOR THIS STORM' + print *,'!!! ' + endif + + stormswitch(ist) = 2 + cycle stormloop + endif + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + + ! For the radii, we encountered a problem with radmax + ! being too small. It was set at 650 km. Hurricane + ! Sandy exceeded this in the models, so the values + ! returned from getradii were close to the default + ! radmax value of 650 km (350 nm), instead of higher. + ! To fix it, we now use an iterative technique, where + ! we start with radmax as a small value (500 km). If + ! getradii returns a value for R34 in a quadrant that + ! does not exceed 0.97*radmax, then that value is ok. + ! If it does exceed 0.97*radmax, then we bump up radmax + ! by 50 km and call getradii again, looking to diagnose + ! radii only in those quadrants where the + ! need_to_expand_r34 flag = 'n'. + + vradius = 0 + r34_check_okay = 'n' + do ivr = 1,4 + need_to_expand_r34(ivr) = 'y' + enddo + radmax = 500.0 ! Initial radmax, in km + + getrad_iter_loop: do while + & (r34_check_okay == 'n' .and. radmax <= 1050.) + + call getradii (fixlon(ist,ifh),fixlat(ist,ifh),imax,jmax + & ,dx,dy,valid_pt,storm(ist)%tcv_storm_id + & ,ifcsthour,vradius,trkrinfo + & ,need_to_expand_r34,radmax,igrret) + r34_dist_thresh = 0.97 * radmax + r34_good_ct = 0 + do ivr = 1,4 + vradius_km = float(vradius(1,ivr)) / 0.5396 + if (vradius_km < r34_dist_thresh) then + r34_good_ct = r34_good_ct + 1 + need_to_expand_r34(ivr) = 'n' + endif + enddo + if (r34_good_ct == 4) then + r34_check_okay = 'y' + endif + radmax = radmax + 50.0 + enddo getrad_iter_loop + + endif + +c If the user has requested so, then call a routine to +c determine the type of cyclone, using Bob Hart's +c cyclone phase space (CPS) algorithms. It is only used +c for times after t=0, since for the first check (of the +c "parameter B" thickness asymmetry), we need to know +c in which direction the storm is moving. Pulling that +c storm movement data off of the tcvitals is not reliable +c since the model storm may not be moving in the same +c direction as the observed storm. However, we could do +c an upgrade later where this storm movement data is +c pulled from the "genesis vitals", which are derived +c from the model forecast data itself, not the obs. + + if (phaseflag == 'y' .and. stormswitch(ist) == 1) then + wcore_flag = 'u' ! 'u' = undetermined + call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag,igpret) + endif + + if (structflag == 'y' .or. ikeflag == 'y') then + call get_sfc_center (fixlon(ist,ifh),fixlat(ist,ifh) + & ,clon,clat,ist,ifh,calcparm,xsfclon + & ,xsfclat,maxstorm,igscret) + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,er_wind,sr_wind,er_vr,sr_vr + & ,er_vt,sr_vt,maxstorm,trkrinfo,igwsret) + if (igwsret == 0) then + call output_wind_structure (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),er_wind,sr_wind + & ,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iowsret) + endif + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,wfract_cov,pdf_ct_bin + & ,pdf_ct_tot,maxstorm,trkrinfo,igfwret) + if (igfwret == 0) then + call output_fract_wind (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),wfract_cov,'earth' + & ,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) + endif + endif + + if (ikeflag == 'y' .and. stormswitch(ist) == 1) then + call get_ike_stats (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,ike,sdp,wdp,maxstorm + & ,trkrinfo,igisret) + if (igisret == 0) then + call output_ike (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),ike,sdp,wdp,maxstorm + & ,ioiret) + endif + endif + +c Now print out the current fix position and intensity +c (in knots) to standard output. Conversion for m/s to +c knots (1.9427) is explained in output_atcf. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to fixcenter, fix positions at ' + write (6,442) ifhours(ifh),ifclockmins(ifh) + 442 format (1x,'forecast hour= ',i4,':',i2.2,' follow:') + print *,' ' + endif + + if (ifret == 0 .and. stormswitch(ist) == 1) then + + if ( verb .ge. 3 ) then + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + & ,int((xmaxwind(ist,ifh)*1.9427) + 0.5) + print *,' ' + endif + + ! Only call output routines every atcffreq/100 hours.... + + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + + if (leadtime_check == 0) then + + ifcsthour = ileadtime / 100 + + call output_atcfunix (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + + ! Get the storm motion vector and the speed of + ! motion so that we can output this in the + ! "atcf_sink" forecast text file. + + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'vitals',trkrinfo + & ,ignret) + else + istmdir = -999 + istmspd = -999 + ignret = 0 + endif + + if ( verb .ge. 3 ) then + write (6,617) istmspd,istmdir,ignret + 617 format (1x,'+++ RPT_STORM_MOTION: istmspd= ',i5 + & ,' istmdir= ',i5,' rcc= ',i3) + endif + + ! Call a routine to find the mean & max relative + ! vorticity near the storm at 850 & 700. These will + ! be written out to the "atcf_sink" fcst text file. + + imeanzeta = -99 + igridzeta = -99 + call get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo + & ,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + endif + + call output_atcf_sink (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta + & ,igridzeta,cps_vals,plastbar,rlastbar + & ,ioaxret) + + if (inp%model == 12 .and. ifcsthour == 0) then + ! Write vitals for GFS ens control analysis + call output_tcvitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,iovret) + + endif + endif + + ! The exception here is for the call to the output_hfip + ! routine, which will be called for every lead time + ! that is processed.... + + call output_hfip (fixlon(ist,ifh),fixlat(ist,ifh),inp,ist + & ,ifh,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,rmax,ioaxret) + else + + if ( verb .ge. 3 ) then + write (6,452) 'fixpos ',storm(ist)%tcv_storm_id + & ,' fhr= ',ifhours(ifh),ifclockmins(ifh) + & ,' Fix not made for this forecast hour' + 452 format (1x,a7,1x,a4,a6,i4,':',i2.2,a36) + + print *,' ' + print *,'!!! RETURN CODE from fixcenter not equal to 0,' + print *,'!!! or output from is_it_a_storm indicated the' + print *,'!!! system found was not our storm, or the ' + print *,'!!! speed calculated indicated we may have ' + print *,'!!! locked onto a different center, thus a fix' + print *,'!!! was not made for this storm at this ' + print *,'!!! forecast hour.' + print *,'!!! mslp gradient check = ',isastorm(1) + print *,'!!! mslp closed contour check = ',isastorm(2) + print *,'!!! 850 mb winds check = ',isastorm(3) + print *,'!!! fixcenter return code = ifret = ',ifret + print *,' ' + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + + ! For the vt=00h lead time, if the tracker failed to + ! locate a position, we are going to write out an + ! atcfunix that contains the position, intensity, mslp + ! and 34-kt wind radii from TC Vitals for this storm + ! and initial time.... + + tcv_max_wind_ms = float(storm(ist)%tcv_vmax) + tcv_mslp_pa = float(storm(ist)%tcv_pcen) * 100.0 + + ! Convert tcvitals NE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15ne) + if (r34_from_tcv > 0.0) then + vradius(1,1) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,1) = 0 + endif + + ! Convert tcvitals SE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15se) + if (r34_from_tcv > 0.0) then + vradius(1,2) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,2) = 0 + endif + + ! Convert tcvitals SW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15sw) + if (r34_from_tcv > 0.0) then + vradius(1,3) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,3) = 0 + endif + + ! Convert tcvitals NW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15nw) + if (r34_from_tcv > 0.0) then + vradius(1,4) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,4) = 0 + endif + + ! Convert tcvitals roci from km to nm + + if (storm(ist)%tcv_penvrad > 0) then + roci_from_tcv = float(storm(ist)%tcv_penvrad) + rlastbar = roci_from_tcv * 0.5396 + else + rlastbar = -99.0 + endif + + ! Convert tcvitals pressure at roci from km to nm + + if (storm(ist)%tcv_penv > 0) then + proci_from_tcv = float(storm(ist)%tcv_penv) + plastbar = proci_from_tcv * 100.0 + else + plastbar = -99.0 + endif + + write (6,291) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + & ,atcfymdh + 291 format (1x,'NOTE: TCVITALS_USED_FOR_ATCF_F00 ' + & ,' Storm ID: ',a4,' Storm name: ',a9 + & ,' YMDH: ',i10) + + call output_atcfunix (slonfg(ist,ifh) + & ,slatfg(ist,ifh),inp,ist + & ,ifcsthour,tcv_max_wind_ms + & ,tcv_mslp_pa,vradius,maxstorm,trkrinfo + & ,plastbar,rlastbar,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (trkrinfo%type == 'tracker') then + ! Update 11/11: For a 'tracker' run, i.e., one in + ! which we know that there is an observed storm in + ! the area, we will assume that there was some type + ! of problem in the initialization that prevented + ! the storm from being found. In this case, even + ! though we have written out zeroes for the 00h + ! time, we want to at least try tracking again at + ! the next lead time. Requested by HWRF folks.... + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',ist + write (6,301) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + 301 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + call get_next_ges (slonfg,slatfg,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + stormswitch(ist) = 1 + endif + + endif + cycle stormloop + endif + + +c Now get first guess for next forecast time's position. +c But first, if this is the first time level (ifh=1) and +c the user has requested that storm vitals be output (this +c is usually only done for model analyses in order to get +c an analysis position from one time to the next), we will +c write out a storm vitals record for this time level. +c Note that we have already gotten the next guess position +c info just above for the case of the repeated analysis +c data, so we'll just output the genesis vitals record. + + if (ifh <= ifhmax) then + if (ifh == 1 .and. trkrinfo%out_vit == 'y') then + call output_gen_vitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,istmspd,istmdir,iovret) + endif + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + else + istmdir = -999 + istmspd = -999 + endif + endif + + case (2) + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Case 2 in tracker for stormswitch' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + endif + + case (3) + continue + +c print *,' ' +c print *,'!!! Case 3 in tracker for stormswitch' +c print *,'!!! Storm name = ',storm(ist)%tcv_storm_name +c print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + + end select + + enddo stormloop + +c if (trkrinfo%type == 'midlat' .or. +c & trkrinfo%type == 'tcgen') then +c call output_tracker_mask (masked_outc,kpds,kgds,lb,ifh +c & ,imax,jmax,iotmret) +c endif + + if(use_per_fcst_command=='y') then +c User wants us to run a command per forecast time + +! Replace %[FHOUR] with forecast hour, %[FMIN] with forecast minute. + +! The %[] format is chosen to avoid shell syntax errors if someone +! includes unknown %[] constructs. A stray , for example, +! would generate syntax errors or unexpected results in some +! shells. + +! If an unrecognized %[xxx] sequence is used, it will be retained in +! the final command. This allows the underlying command to detect +! the unreplaced %[] and use suitable default values or abort, as +! appropriate. + + pfc_final=per_fcst_command + call argreplace(pfc_final,pfc_cmd_len,'%[FHOUR]', & + & ifhours(ifh)) + call argreplace(pfc_final,pfc_cmd_len,'%[FMIN]', & + & iftotalmins(ifh)) + + if(verb.ge.2) then + print *,' ' + print *,'!!! Running per-fcst command' + print *,'!!! Unparsed = ',trim(per_fcst_command) + print *,'!!! Parsed = ',trim(pfc_final) + endif + call run_command(trim(pfc_final),pfcret) + if(pfcret/=0 .and. verb.ge.1) then + print *,' ' + print *,'!!! Non-zero exit status from per-fcst command' + print *,'!!! Command = ',trim(pfc_final) + print *,'!!! Exit status = ',pfcret + print *,'!!! Continuing anyway...' + elseif(pfcret==0 .and. verb.ge.2) then + print *,' ' + print *,'!!! Per-fcst command returned success status (0)' + endif + endif + + ifh = ifh + 1 + if (ifh > ifhmax) exit ifhloop + + if (inp%file_seq == 'multi') then + call baclose(lugb,igcret) + call baclose(lugi,iicret) + if ( verb .ge. 3 ) then + print *,'baclose return code for unit ',lugb,' = igcret = ' + & ,igcret + print *,'baclose return code for unit ',lugi,' = iicret = ' + & ,iicret + endif + + endif + + enddo ifhloop +c +cJ.P08042014 call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cJ.P08042014 call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +c & ,ioaret) +c + 73 format ('fixpos ',a4,' fhr= ',i4,':',i2.2,' Fix position= ' + & ,f7.2,'E (',f6.2,'W)',2x,f7.2,' Max Wind= ',i3,' kts') + + if (allocated(prstemp)) deallocate (prstemp) + if (allocated(prsindex)) deallocate (prsindex) + if (allocated(iwork)) deallocate(iwork) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(vt850_flag)) deallocate (vt850_flag) + if (allocated(closed_mslp_ctr_flag)) + & deallocate (closed_mslp_ctr_flag) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine argreplace(arg,n,name,val) + ! This subroutine is used to generate the pre-forecast-command + ! It will edit the command (argument "arg") and replace string + ! name with value val. That is how the per-forecast-command + ! has these modifications: + + ! %[FHOUR] -> replace with -> last forecast hour + ! %[FMIN] -> replace with -> last forecast minute + + implicit none + + integer, intent(in) :: n + character(n), intent(inout) :: arg + character(*), intent(in) :: name + integer, intent(in) :: val + + integer found,namelen,i1,i2 + character(n) :: out + + found=index(arg,name) + namelen=len(name) + i1=found-1 ! last char that is before name + i2=found+namelen ! index of last char in name + + if(found==0) return + + out=' ' + + if(found>1 .and. i21) then +! special case: name is at end of string +! hope the value fits... + write(out,'(A,I0)') arg(1:i1),val + elseif(i2= 1.24 .and. dell < 2.49) then ! UKMET + ri = ritrk_most + radinf = 275.0 + npts = 2 + else ! ECMWF + ri = ritrk_coarse + radinf = 350.0 + npts = 1 + endif + + pthresh = trkrinfo%mslpthresh ! These are read in in + vthresh = trkrinfo%v850thresh ! subroutine read_nlists.... + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,parmlon,parmlat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij B, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij B, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print*,' ' + print*,'!!! ERROR in is_it_a_storm from call to' + print*,'!!! get_ij_bounds, stopping processing for ' + print*,'!!! storm number ',ist + endif + + isiret = 92 + return + endif + +c If the input cparm is slp, then check to see that the MSLP +c gradient in any direction from the MSLP center is at least +c 1mb / 200km, or 0.005mb/km. This is based on discussions with +c Morris & Bob, who have had good results using a 2mb/200km +c requirement. Since their model has a much finer resolution than +c all of the models we run the tracker on AND a much better +c depiction of the hurricane vortex, we do not use a requirement +c as strict as theirs, and so make the requirement only half as +c strong as theirs. +c +c If the input cparm is v850, then check to see that there is +c a circulation at 850 mb. We will do this by calculating the +c tangential wind of all points within a specified radius of +c the 850 minimum wind center, and seeing if there is a net +c average tangential wind speed of at least 5 m/s. +c +c UPDATE APRIL 2000: I've relaxed the thresholds slightly from +c 0.005 mb/km to 0.003 mb/km, and the wind threshold from +c 5 m/s to 3 m/s. Also, note that a special case for GDAS has +c been hardwired in that is weaker (0.002 mb/km and 2 m/s). +c That weaker GDAS requirement is for Qingfu's relocation stuff. +c +c UPDATE JULY 2001: The relaxed requirement put in place in +c April 2000 for the GDAS relocation has also been put in place +c for the GFS ensemble relocation. + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the loop. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, ilonfix= ',ilonfix + & ,' jlatfix= ',jlatfix + print *,'ibeg jbeg iend jend = ',ibeg,jbeg,iend,jend + print *,'cparm= ',cparm,' parmlon parmlat = ',parmlon,parmlat + print *,'parmval= ',parmval + print *,' ' + endif + + vtavg = 0.0 + ivt = 0 + + xmaxpgrad = -999.0 + + jloop: do jix = jbeg,jend,bskip + iloop: do iix = ibeg,iend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine is_it_a_storm' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + i = iix - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine ' + print *,'!!! is_it_a_storm for a non-global grid.' + print *,'!!! STOPPING....' + print *,'!!! i= ',i,' imax= ',imax + print *,' ' + endif + + stop 97 + endif + endif + + call calcdist(parmlon,parmlat,glon(i),glat(j),dist,degrees) + + if (dist > radinf .or. dist == 0.0) cycle + + if (defined_pt(i,j)) then + + if (cparm == 'slp') then + pgradient = (slp(i,j) - parmval) / dist + if (pgradient > xmaxpgrad) xmaxpgrad = pgradient + + if ( verb .ge. 3 ) then + write (6,93) i,j,glon(i),glat(j),dist,slp(i,j),pgradient + endif + + if (pgradient > pthresh) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, valid pgradient found.' + print '(a23,f8.5)',' pgradient threshold = ',pthresh + print '(a23,f8.5)',' pgradient found = ',pgradient + print *,'mslp center = ',parmlon,parmlat,parmval + print *,'pgrad loc = ',glon(i),glat(j),slp(i,j) + endif + + stormcheck = 'Y' + exit jloop + endif + endif + + if (cparm == 'v850') then + call getvrvt (parmlon,parmlat,glon(i),glat(j) + & ,u(i,j,nlev850),v(i,j,nlev850),vr,vt,igvtret) + if ( verb .ge. 3 ) then + write (6,91) i,j,glon(i),glat(j),u(i,j,nlev850) + & ,v(i,j,nlev850),vr,vt + endif + + vtavg = vtavg + vt + ivt = ivt + 1 + endif + + endif + + enddo iloop + enddo jloop + + 91 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' u= ',f8.4,' v= ',f8.4,' vr= ',f9.5,' vt= ',f9.5) + + 93 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' dist= ',f8.2,' slp= ',f10.2,' pgradient= ',f8.5) + + if (stormcheck /= 'Y' .and. cparm == 'slp') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, valid pgradient NOT FOUND.' + write (6,94) '!!! (Max pgradient less than ',pthresh,' mb/km)' + 94 format (1x,a29,5x,f8.5,a7) + write (6,95) '!!! Max pgradient (mb/km) found = ',xmaxpgrad + 95 format (1x,a34,f8.5) + print *,' ' + endif + + endif + + if (cparm == 'v850') then + + if (ivt > 0) then + vtavg = vtavg / float(ivt) + else + vtavg = 0.0 + endif + + if (parmlat > 0) then + if (vtavg >= vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (>= +',vthresh,' m/s for a NH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed +',vthresh + & ,' m/s (NH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + else + if (vtavg <= -vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (<= -',vthresh,' m/s for a SH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed -',vthresh + & ,' m/s (SH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + endif + + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag,igpret) +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure or phase of a cyclone. Initially, we +c will just have it use the Hart cyclone phase space (CPS) scheme. + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trkrparms; USE grid_bounds + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character wcore_flag*1 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real cps_vals(3) + real dx,dy,paramb,vtl_slope,vtu_slope + integer imax,jmax,igpret,igcpret,ist,ifh,maxstorm + integer igvpret,igcv1ret,igcv2ret + logical(1) valid_pt(imax,jmax) +c + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,611) + write (6,613) + write (6,615) + write (6,*) ' ' + + 611 format(1x,'#-----------------------------------------------#') + 613 format(1x,'# start of routine to determine cyclone phase...#') + 615 format(1x,'#-----------------------------------------------#') + endif + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + if (ifh > 1) then + + ! This condition that ifh > 1 is so that we *not* do the cps + ! stuff on fhour=0 stuff, since we don't know the storm motion + ! direction for the analysis. + + if (fixlon(ist,ifh-1) > -990.0 .and. + & fixlat(ist,ifh-1) > -990.0) then + + ! Similarly, these next two conditions (previous lat and + ! previous lon > -999) are in there in case we're doing a + ! tcgen or midlat case and this is the *first* time level + ! within a forecast that the storm has been detected (again, + ! we don't yet know the storm heading). + + call get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'lower',vtl_slope + & ,maxstorm,igcv1ret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'upper',vtu_slope + & ,maxstorm,igcv2ret) + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh) + & ,paramb,vtl_slope,vtu_slope + endif + + cps_vals(1) = paramb + cps_vals(2) = vtl_slope + cps_vals(3) = vtu_slope + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diagnostics were requested but will NOT' + print *,' >< be performed for this time level since we ' + print *,' >< are at the first time level for this newly' + print *,' >< found storm, therefore we cannot diagnose' + print *,' >< the model direction of storm movement.' + print *,' >< ifh= ',ifh + endif + + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diags were requested but will NOT be' + print *,' >< performed for this time level since we are at' + print *,' >< time level 1 and we cannot diagnose the model' + print *,' >< direction of storm movement. ' + print *,' >< ifh= ',ifh + endif + + endif + + endif + + 73 format ('cps_stats: ',a4,' lead time= ',i3,':',i2,' paramb= ' + & ,f8.2,' vtl= ',f9.2,' vtu= ',f9.2) + + + if (phasescheme == 'vtt' .or. phasescheme == 'both') then + call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + + 631 format(1x,'#-------------------------------------------------#') + 633 format(1x,'# End of routine to determine cyclone phase... #') + 635 format(1x,'#-------------------------------------------------#') + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines "Parameter B", which determines +c the degree of thermal symmetry between the "left" and "right" +c hemispheres of a storm, in the layer between 900 and 600 mb. +c We evaluate only those points that are within 500 km of the +c storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zthicksum(2) + real rlonc,rlatc,rlonb,rlatb,xdist,degrees,d,cosarg + real st_heading,st_heading_rad,ricps,dx,dy + real pt_dir,pt_dir_rad,zthick,hemval,paramb + real zthick_right_mean,zthick_left_mean + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer left_ct,right_ct,hemis,icount,maxstorm,ip + logical(1) valid_pt(imax,jmax) +c + ricps = 500.0 + +c ----------------------------------------------------------------- +c First, determine the angle that the storm took getting from the +c last position to the current one. +c ----------------------------------------------------------------- + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + if (d == 0.0) then + + ! Storm is stationary... + st_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print '(a43,f9.3)',' In get_cps_paramb, model storm heading = ' + & ,st_heading + print *,' ' + endif + +c ----------------------------------------------------------------- +c Now call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_paramb from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + igcpret = 92 + return + endif + +c ----------------------------------------------------------------- +c Now loop through all of the points of the subdomain. If the +c point is further than 500 km from the storm center, discard it. +c Otherwise, evaluate the angle from the storm center to this point +c to determine the hemisphere of the point, that is, if the point +c is to the left or the right of the storm track. +c ----------------------------------------------------------------- + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the + ! loop for the evaluation of parameter B. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + left_ct = 0 + right_ct = 0 + zthicksum = 0 + icount = 0 + +c print *,'CPS CORE: ibeg= ',ibeg,' iend= ',iend +c print *,'CPS CORE: jbeg= ',jbeg,' jend= ',jend + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + icount = icount + 1 + +c print *,'CPS CORE: ist= ',ist,' ifh= ',ifh,' j= ',j,' i= ',i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_paramb, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Parameter B will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_paramb' + print *,'!!! for a non-global grid.' + print *,'!!! Parameter B will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_PARAMB....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon= ',glon(ip),' glat= ',glat(j) + print *,'!!! Parameter B will not be computed.' + print *,'!!! EXITING GET_CPS_PARAMB....' + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + + !---------------------------------------------------------- + ! Calculate angle from storm center to point, in a 0-360 + ! framework, clockwise positive. + !---------------------------------------------------------- + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-fixlon(ist,ifh)) * dtr + rlatb = fixlat(ist,ifh) * dtr + d = degrees * dtr + + if (d > 0.) then + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_dir_rad = acos(cosarg) + else + pt_dir_rad = 2*pi - acos(cosarg) + endif + else + pt_dir_rad = 0.0 + endif + + pt_dir = pt_dir_rad / dtr + + !------------------------------------------------------------ + ! Based on the angle that the point is from the storm center, + ! determine if the point is to the left or the right of the + ! storm track. + !------------------------------------------------------------ + + if (st_heading >= 180.0) then + if ((st_heading - pt_dir) > 0.0 .and. + & (st_heading - pt_dir) <= 180) then + hemis = 2 + left_ct = left_ct + 1 + else + hemis = 1 + right_ct = right_ct + 1 + endif + else + if ((pt_dir - st_heading) > 0.0 .and. + & (pt_dir - st_heading) <= 180) then + hemis = 1 + right_ct = right_ct + 1 + else + hemis = 2 + left_ct = left_ct + 1 + endif + endif + + !------------------------------------------------------------ + ! Calculate the 600-900 mb thickness at this point and add + ! the thickness value to the array for the correct "storm + ! hemisphere". + !------------------------------------------------------------ + + zthick = cpshgt(ip,j,7) - cpshgt(ip,j,1) + zthicksum(hemis) = zthicksum(hemis) + zthick + + if ( verb .ge. 3 ) then + write (6,51) rlonb/dtr,rlatb/dtr,rlonc/dtr,rlatc/dtr + & ,st_heading,pt_dir,hemis,zthick + endif + + enddo iloop + enddo jloop + + 51 format (1x,'stlon stlat = ',2(f6.2,2x),' ptlon ptlat = ' + & ,2(f6.2,2x),' sthead= ',f6.2,' ptdir= ',f6.2,' hemis= ' + & ,i1,' zthick= ',f7.2) + +c ------------------------------------------------------------------ +c Now calculate parameter B. The hemval parameter = +1 for storms +c in the Northern Hemisphere and -1 for Southern Hemisphere storms. +c ------------------------------------------------------------------ + + zthick_right_mean = zthicksum(1) / float(right_ct) + zthick_left_mean = zthicksum(2) / float(left_ct) + + if (fixlat(ist,ifh) < 0.0) then + hemval = -1.0 + else + hemval = 1.0 + endif + + paramb = hemval * (zthick_right_mean - zthick_left_mean) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' right_ct= ',right_ct,' left_ct= ',left_ct + print *,' zthicksum(1)= ',zthicksum(1) + print *,' zthicksum(2)= ',zthicksum(2) + print *,' zthick_right_mean= ',zthick_right_mean + print *,' zthick_left_mean= ',zthick_left_mean + print *,' hemval= ',hemval + print *,' END of get_cps_paramb, paramb= ',paramb + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,clayer,vth_slope,maxstorm,igcvret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines the thermal wind profile for +c either the lower troposphere (i.e., between 600 and 900 mb) or the +c upper troposphere (i.e., between 300 and 600 mb). We evaluate +c only those points that are within 500 km of the storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character clayer*5 + real tmp1,tmp2,tmp3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zmax(7),zmin(7),zdiff(7),xlolevs(7),xhilevs(7),plev(7) + real dlnp(7),dzdlnp(7),dz(7),lnp(7) + real vth_slope,xdist,degrees,d,cosarg + real ricps,dx,dy,R2 + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j,k,kix + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igcvret,igiret + integer kbeg,kend,icount,maxstorm,ip + logical(1) valid_pt(imax,jmax) + + data xlolevs /900.,850.,800.,750.,700.,650.,600./ + data xhilevs /600.,550.,500.,450.,400.,350.,300./ +c data xlolevs /90000.,85000.,80000.,75000.,70000.,65000.,60000./ +c data xhilevs /60000.,55000.,50000.,45000.,40000.,35000.,30000./ +c + ricps = 500.0 + plev = 0.0 + + if (clayer == 'lower') then + kbeg = 1 + kend = 7 + plev = xlolevs + else + kbeg = 7 + kend = 13 + plev = xhilevs + endif + +c ----------------------------------------------------------------- +c First, call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_vtl from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + igcvret = 92 + return + endif + +c ------------------------------------------------------------------ +c Now loop through all of the points of the subdomain at each level. +c If a point is further than 500 km from the storm center, discard +c it. Otherwise, evaluate the gp height at the point to determine +c if it is a max or a min for the given level. Store the max and +c min height at each level in an array. +c ------------------------------------------------------------------ + +c ! We will want to speed things up for finer resolution grids. +c ! We can do this by skipping some of the points in the +c ! loop for the evaluation of parameter B. +c +c if ((dx+dy)/2. > 0.20) then +c bskip = 1 +c else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then +c bskip = 2 +c else if ((dx+dy)/2. <= 0.10) then +c bskip = 3 +c endif + + bskip = 1 ! Don't do any skipping for now.... + + zmax = -9999999.0 + zmin = 9999999.0 + zdiff = 0.0 + lnp = 0.0 + + levloop: do k = kbeg,kend + + if (kbeg == 7) then + ! processing upper layers (600-300 mb) + kix = k - 6 + else + ! processing lower layers (900-600 mb) + kix = k + endif + + lnp(kix) = log(plev(kix)) + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + icount = icount + 1 + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_vth, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_vth' + print *,'!!! for a non-global grid.' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_VTH....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j,' k= ',k + & ,' clayer= ',clayer + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon(ip)= ',glon(ip),' glat= ',glat(j) + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! EXITING GET_CPS_VTH....' + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + + tmp1 = zmax(kix) + tmp2 = cpshgt(ip,j,k) + tmp3 = zmin(kix) + + zmax(kix) = max(tmp1,tmp2) + zmin(kix) = min(tmp3,tmp2) + +c zmax(kix) = max(zmax(kix),cpshgt(ip,j,k)) +c zmin(kix) = min(zmin(kix),cpshgt(ip,j,k)) + + enddo iloop + enddo jloop + + zdiff(kix) = zmax(kix) - zmin(kix) + + enddo levloop + +c ------------------------------------------------------------------ +c Now calculate the vertical derivative of the gp height, that is, +c d(dz)/d(ln(p)). Here, zdiff is the gp height perturbation at a +c given level, calculated in the loop above; dz is the vertical +c change in that perturbation from one level to the next. +c ------------------------------------------------------------------ + + dz = 0.0 + dlnp = 0.0 + dzdlnp = 0.0 + + do k = 2,7 + dz(k) = zdiff(k) - zdiff(k-1) + dlnp(k) = log(plev(k)) - log(plev(k-1)) + dzdlnp(k) = dz(k) / dlnp(k) + enddo + +c ------------------------------------------------------------------ +c Now call a correlation routine to get the slope of a regression +c line. The independent variable that we input is dlnp, the change +c in log of pressure with height. The dependent variable is +c dzdlnp, the vertical change in the height perturbation with +c respect to the change in pressure. The slope that is returned +c defines whether we've got a cold core or warm core system. +c See Hart (MWR, April 2003, Vol 131, pp. 585-616) for more +c details, specifically his Fig. 3 and the discussion surrounding. +c Note that in the call to calccorr, we are sending only 6 of the +c 7 elements of the dlnp and dzdlnp arrays, beginning with the +c 2nd element of each. That's because the first array value for +c each of those arrays is empty, since in the loop just above, we +c start with kbeg+1, not kbeg. +c ------------------------------------------------------------------ + + call calccorr(lnp(2),zdiff(2),6,R2,vth_slope) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ In get_cps_vth, values for vth follow for ' + & ,'lead time= ',ifhours(ifh),':',ifclockmins(ifh),' ' + & ,storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' ... clayer = ',clayer + print *,' ' + endif + + do k = kbeg,kend + + if (kbeg == 7) then + kix = k - 6 + else + kix = k + endif + + if ( verb .ge. 3 ) then + print *,' ' + write (6,31) k,plev(kix),zmax(kix),zmin(kix),zdiff(kix) + if (kix > 1) then + write (6,32) plev(kix),log(plev(kix)) + & ,plev(kix-1),log(plev(kix-1)) + write (6,33) dz(kix),dlnp(kix),dzdlnp(kix) + else + write (6,34) + endif + endif + + enddo + + 31 format (1x,' +++ k= ',i2,' press= ',f8.1,' zmax= ',f7.2 + & ,' zmin= ',f7.2,' zdiff= ',f7.2) + 32 format (1x,' ln(',f7.1,')= ',f9.6,' ln(',f7.1,')= ',f9.6) + 33 format (1x,' dz= ',f7.2,' dlnp= ',f9.6,' dzdlnp= ',f9.3) + 34 format (1x,' --- First level... no derivatives done...') +c + return + end +c +C---------------------------------------------------- +C +C---------------------------------------------------- + subroutine calccorr(xdat,ydat,numpts,R2,slope) +c +c This subroutine is the main driver for a series of +c other subroutines below this that will calculate the +c correlation between two input arrays, xdat and ydat. +c +c INPUT: +c xdat array of x (independent) data points +c ydat array of y (dependent) data points +c numpts number of elements in each of xdat and ydat +c +c OUTPUT: +c R2 R-squared, the coefficient of determination +c slope Slope of regression line +c +c xdiff array of points for xdat - xmean +c ydiff array of points for ydat - ymean +c yestim array of regression-estimated points +c yresid array of residuals (ydat(i) - yestim(i)) + + USE verbose_output + + implicit none + + real xdat(numpts),ydat(numpts) + real xdiff(numpts),ydiff(numpts) + real yestim(numpts),yresid(numpts) + real xmean,ymean,slope,yint,R2 + integer numpts,i + +c + call getmean(xdat,numpts,xmean) + call getmean(ydat,numpts,ymean) +c + call getdiff(xdat,numpts,xmean,xdiff) + call getdiff(ydat,numpts,ymean,ydiff) +c + call getslope(xdiff,ydiff,numpts,slope) + yint = ymean - slope * xmean +c + call getyestim(xdat,slope,yint,numpts,yestim) + call getresid(ydat,yestim,numpts,yresid) +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * CPS Thermal wind regression details * ' + print *,' *--------------------------------------------------* ' + endif + + call getcorr(yresid,ydiff,numpts,R2) + + if ( verb .ge. 3 ) then + print *,' i ydat xdat ydiff xdiff e' + & ,' e2 ydiff2' + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + do i = 1,numpts + write(6,'(2x,i3,2x,f7.2,2x,f7.4,2x,f7.2,2x,f7.4,3(2x,f7.2))') + & i,ydat(i),xdat(i),ydiff(i) + & ,xdiff(i),yresid(i),yresid(i)*yresid(i) + & ,ydiff(i)*ydiff(i) + enddo + + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + print *,' ' + write (6,'(1x,a13,f9.3,3x,a5,f7.2)') ' means: y: ',ymean + & ,' x: ',xmean + + write (6,*) ' ' + write (6,30) 'slope= ',slope,' y-intercept = ',yint + 30 format (2x,a7,f10.3,a23,f10.3) + if (slope .gt. 0.0) then + write(6,40) 'Regression equation: Y = ',yint,' + ',slope + else + write(6,40) 'Regression equation: Y = ',yint,' - ' + & ,abs(slope) + endif + 40 format (2x,a27,f8.2,a3,f8.2,'X') +c + print *,' ' + write (6,'(1x,a17,f7.4,5x,a7,f7.4)') ' R2(r_squared) = ',R2 + & ,' r = ',sqrt(R2) + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * End of regression details * ' + print *,' *--------------------------------------------------* ' + endif + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getmean(xarr,inum,zmean) +c +c This subroutine is part of the correlation calculation, +c and it simply returns the mean of the input array, xarr. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c +c OUTPUT: +c zmean mean of data values in xarr + + implicit none + + real xarr(inum) + real xsum,zmean + integer i,inum +c + xsum = 0.0 + do i = 1,inum + xsum = xsum + xarr(i) + enddo +c + zmean = xsum / float(MAX(inum,1)) +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getdiff(xarr,inum,zmean,zdiff) +c +c This subroutine is part of the correlation calculation, +c and it returns in the array zdiff the difference values +c between each member of the input array xarr and the +c mean value, zmean. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c zmean mean of input array (xarr) +c +c OUTPUT: +c zdiff array containing xarr(i) - zmean + + implicit none + + real xarr(inum),zdiff(inum) + real zmean + integer i,inum +c + do i = 1,inum + zdiff(i) = xarr(i) - zmean + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + + subroutine getslope(xarr,yarr,inum,slope) +c +c This subroutine is part of the correlation calculation, +c and it returns the slope of the regression line. +c +c INPUT: +c xarr input array of xdiffs (x - xmean) +c yarr input array of ydiffs (y - ymean) +c inum number of points in x & y arrays +c +c OUTPUT: +c slope slope of regression line + + real xarr(inum),yarr(inum) + real slope,sumxy,sumx2 + integer i,inum + +c First sum up the xarr*yarr products.... + + sumxy = 0.0 + do i = 1,inum + sumxy = sumxy + xarr(i) * yarr(i) + enddo + +c Now sum up the x-squared terms.... + + sumx2 = 0.0 + do i = 1,inum + sumx2 = sumx2 + xarr(i) * xarr(i) + enddo + +c Now get the slope.... + + slope = sumxy / sumx2 + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getyestim(xarr,slope,yint,inum,yestim) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the predicted y-values using the +c regression equation that has been calculated. +c +c INPUT: +c xarr array of x data points +c slope slope of the calculated regression line +c yint y-intercept of the calculated regression line +c inum number of input points +c +c OUTPUT: +c yestim array of y pts estimated from regression eqn. + + implicit none + + real xarr(inum),yestim(inum) + real slope,yint + integer i,inum +c + do i = 1,inum + yestim(i) = yint + xarr(i) * slope + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getresid(yarr,yestim,inum,yresid) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the residual values between the +c input y data points and the y-estim predicted y values. +c +c INPUT: +c yarr array of y data points +c yestim array of y pts estimated from regression eqn. +c inum number of input points +c +c OUTPUT: +c yresid array of residuals (ydat(i) - yestim(i)) + + implicit none + + real yarr(inum),yestim(inum),yresid(inum) + integer i,inum +c + do i = 1,inum + yresid(i) = yarr(i) - yestim(i) + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getcorr(yresid,ydiff,inum,R2) +c +c This subroutine is part of the correlation calculation, +c and it does the actual correlation calculation. +c +c INPUT: +c yresid array of residuals (ydat(i) - yestim(i)) +c ydiff array of points for ydat - ymean +c inum number of points in the arrays +c +c OUTPUT: +c R2 R-squared, the coefficient of determination + + USE verbose_output + + implicit none + + real yresid(inum),ydiff(inum) + real R2,sumyresid,sumydiff + integer i,inum +c + sumyresid = 0.0 + sumydiff = 0.0 + + do i = 1,inum + sumyresid = sumyresid + yresid(i) * yresid(i) + sumydiff = sumydiff + ydiff(i) * ydiff(i) + enddo + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,30) 'Sum of y-residuals squared (e2) = ',sumyresid + write (6,30) 'Sum of y-diffs squared (ydiff2) = ',sumydiff + write (6,*) ' ' + 30 format (1x,a35,f10.2) + endif + +c if (sumydiff == 0.0) then +c R2=1.0 +c else +c R2 = 1 - sumyresid / sumydiff +c endif +c PENG 05/14/2018 Bug-fixed for R2 calculation with FENS job crashed. + if (sumyresid .lt. sumydiff) then + if (sumydiff .le. 0.000001) then + R2 = 1.0 + else + R2 = 1 - sumyresid / sumydiff + endif + else + R2=0.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. Here, we are only looking +c at the mid-to-upper tropospheric warm anomaly at the center of +c the storm. The temperature data that we are searching through in +c the tmean array should be the 300-500 mb mean temperature data. +c The criteria in this algorithm are based loosely on Vitart's +c criteria for warm core checking, but the nuts & bolts of the +c subroutine use algorithms from this tracker, including the barnes +c analysis. First, we locate the warm core with the find_maxmin +c routine. Then we use the check_closed_contour routine to see if +c there is a closed temperature contour surrounding the warm core. +c +c INPUT: +c inp +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c inp contains input date and model number information +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c ist integer storm number (internal to the tracker) +c ifh integer index for lead time +c trkrinfo derived type containing grid info on user boundaries +c fixlon array containing found fix longitudes +c fixlat array containing found fix latitudes +c valid_pt Logical; bitmap indicating if valid data at that pt. +c maxstorm maximum # of storms to be handled +c +c OUTPUT: +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c igvpret Return code for this subroutine. +c +c LOCAL: +c wcore_mean_val barnes-averaged value of the temperature at the +c location where the tracker found the warm core. +c wcore_point_max max temperature found at a gridpoint near the +c location where the tracker found the warm core using +c barnes analysis. + + USE set_max_parms; USE grid_bounds; USE trkrparms; USE contours + USE tracked_parms; USE gen_vitals; USE def_vitals; USE inparms + USE phase + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo,wcore_trkrinfo + type (cint_stuff) wcore_contour_info + type (datecard) inp + + character*1 get_last_contour_flag,wcore_flag + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dx,dy,wcore_mean_val,wcore_mean_lon,wcore_mean_lat + real wcore_point_max,tlastcont,rlastcont,tlastout,rlastout + integer imax,jmax,igvpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer icount,maxstorm,ip,ifmret,ifilret,ifix,jfix,icccret + integer num_check_conts + logical(1) valid_pt(imax,jmax),compflag,wcore_mask(imax,jmax) + logical(1) output_file_open +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of get_vtt_phase *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for warm core at hour ',i4,':',i2.2) + write (6,103) wcore_depth + 103 format (1x,'* Warm core depth threshold (wcore_depth) = ',f7.2) + print *,'*-------------------------------------------------*' + endif + +c ------------------------------------------------------------ + wcore_mask = .false. + wcore_mean_lon = -999.0 + wcore_mean_lat = -999.0 + wcore_trkrinfo = trkrinfo ! set equal to values from trkrinfo... + wcore_trkrinfo%contint = wcore_depth ! ...except use the warm + ! core contour interval specified by + ! the user in the extrkr.sh script. + +c ------------------------------------------------------------ +c First, call find_maxmin to locate the warm core + + call find_maxmin (imax,jmax,dx,dy,'tmp' + & ,tmean,'max',ist,fixlon(ist,ifh),fixlat(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,compflag + & ,wcore_mean_lon,wcore_mean_lat,wcore_mean_val + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + +c ------------------------------------------------------------ +c Once find_maxmin returns a value and a location for the +c barnes-averaged value of a warm core, then make a call to +c fix_latlon_to_ij to (1) get the actual gridpoint value of the +c temperature (the value stored in wcore_mean_val is an +c area-averaged value coming from the barnes analysis), and +c (2) to get the (i,j) indeces for this gridpoint to be used in +c the call to check_closed_contour below. + + if (wcore_mean_lat > -99.0 .and. wcore_mean_lon > -990.0) then + call fix_latlon_to_ij (imax,jmax,dx,dy,tmean,'max' + & ,valid_pt,wcore_mean_lon,wcore_mean_lat + & ,wcore_mean_val,ifix,jfix,wcore_point_max,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Warm core stats: ' + write (6,105) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_mean_lon,360.-wcore_mean_lon + & ,wcore_mean_lat,wcore_mean_val + write (6,106) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,ifix,jfix,wcore_point_max + endif + + else + ! Search went out of regional grid bounds.... + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN get_vtt_phase. The call to ' + print *,'!!! fix_latlon_to_ij returned a non-zero return ' + print *,'!!! code, which means that the search for the fix' + print *,'!!! i and j went out of bounds for a regional ' + print *,'!!! grid. This should have been caught in a ' + print *,'!!! previous call to find_maxmin for one of the ' + print *,'!!! various fix parms. In any event, we will not' + print *,'!!! search for a warm core for this storm and ' + print *,'!!! lead time.' + print *,' ' + write (6,115) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,'U',-999.99,-9999.99 + endif + + igvpret = 95 + wcore_flag = 'u' + return + endif + endif + + 105 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' mean_lon: ',f7.2,'E' + & ,1x,'(',f7.2,'W)',2x,'mean_lat: ',f7.2,2x + & ,'wcore_mean_val(K): ',f7.3) + 106 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' ifix: ',i5,2x + & ,' jfix: ',i5,2x,'wcore_point_max(K): ',f7.3) + + +c ------------------------------------------------------------ +c The Vitart scheme specifies that the temperature must decrease +c by at least 1.0C in all directions from the warm core center +c within a distance of 8 deg. A rigorous check of this criterion +c is performed here by utilizing the check_closed_contour routine. +c If we have a closed contour in the temperature field +c surrounding the warm core (using a 1 deg K interval), that +c criterion is satisfied. For diagnostic purposes, we set the +c value of num_check_conts to 999 in order to keep searching for +c all contours surrounding the warm core, and this allows us to +c get an idea of the "depth" or magnitude of the warm core when +c the tlastcont and rlastcont values are returned. + + wcore_contour_info%numcont = maxconts + num_check_conts = 999 + + get_last_contour_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,tmean + & ,valid_pt,wcore_mask,wcore_flag,'max',wcore_trkrinfo + & ,num_check_conts,wcore_contour_info,get_last_contour_flag + & ,tlastcont,rlastcont,icccret) + + if (wcore_flag == 'y') then + tlastout = tlastcont + rlastout = rlastcont/0.539638 + else + tlastout = -999.0 + rlastout = -9999.0 + endif + + if ( verb .ge. 3 ) then + write (6,115) storm(ist)%tcv_storm_id,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_flag,tlastout,rlastout + + 115 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2 + & ,' wcore_flag= ',a1,2x,' Temp of last contour(K) = ' + & ,f7.2,2x,'Radius of last contour(km) = ',f8.2) + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_sfc_center (xmeanlon,xmeanlat,clon + & ,clat,ist,ifh,calcparm,xsfclon,xsfclat + & ,maxstorm,igscret) +c +c ABSTRACT: This subroutine computes a modified lat/lon fix position +c to use as the input center position for the subroutines that +c follow which calculate surface-wind related values. The reason +c for this is that since we are concerned with the positioning of +c low-level wind features (e.g., rmax), we want the center position +c to be based solely on low-level features. We'll use mslp and the +c min in the sfc wind speed. If a center fix was unable to be made +c at this forecast hour for mslp and low-level winds, then we will +c stick with just using the mean position we got using all the other +c parameters. +c +c INPUT: +c xmeanlon The mean center longitude computed from all the various +c parameter fixes found in array clon +c xmeanlat The mean center latitude computed from all the various +c parameter fixes found in array clat +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Index for storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c (if a parameter fix could not be made at this forecast +c hour, then calcparm is set to false for this time for +c that parameter). +c maxstorm Maximum number of storms that can be tracked +c +c OUTPUT: +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c igscret Return code from this subroutine + + USE set_max_parms + USE verbose_output + + implicit none + + integer ist,ifh,ipct,igscret,maxstorm + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmeanlon,xmeanlat + real xsfclon,xsfclat,xlonsum,xlatsum + logical(1) calcparm(maxtp,maxstorm) + + ipct = 0 + xlonsum = 0.0 + xlatsum = 0.0 + + ! Do NOT include MSLP for the surface center at this time. +c if (calcparm(9,ist)) then +c ipct = ipct + 1 +c xlonsum = xlonsum + clon(ist,ifh,9) +c xlatsum = xlatsum + clat(ist,ifh,9) +c endif + + if (calcparm(10,ist)) then + ! NOTE: Put double weighting on surface wind center if + ! the tracker was able to find a center for it.... + ipct = ipct + 2 + xlonsum = xlonsum + 2.*clon(ist,ifh,10) + xlatsum = xlatsum + 2.*clat(ist,ifh,10) + endif + + if (calcparm(11,ist)) then + ! This is for the sfc vorticity center.... + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,11) + xlatsum = xlatsum + clat(ist,ifh,11) + endif + + if (ipct > 0) then + xsfclon = xlonsum / float(ipct) + xsfclat = xlatsum / float(ipct) + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In get_fract_wind_cov, CANNOT get modified fix ' + print *,'!!! position because the parameter fixes for mslp' + print *,'!!! and the sfc winds could not be obtained at this' + print *,'!!! forecast hour. ist= ',ist,' ifh= ',ifh + print *,'!!! We will use the fixlon and fixlat values for' + print *,'!!! this forecast hour.' + endif + + xsfclon = xmeanlon + xsfclat = xmeanlat + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ In get_sfc_center, modified fix (mslp + sfc_winds)' + print *,'+++ position follows: ' + print *,'+++ ' + print *,'+++ mslp: lon: ',clon(ist,ifh,9),' lat: ' + & ,clat(ist,ifh,9) + print *,'+++ sfc_winds: lon: ',clon(ist,ifh,10),' lat: ' + & ,clat(ist,ifh,10) + print *,'+++ sfc_vorticity: lon: ',clon(ist,ifh,11),' lat: ' + & ,clat(ist,ifh,11) + print *,'+++ multi-parm mean: lon: ',xmeanlon,' lat: ' + & ,xmeanlat + print *,'+++ sfc-only mean: lon: ',xsfclon,' lat: ',xsfclat + endif + + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,er_wind,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm + & ,trkrinfo,igwsret) +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure of the low level winds of a cyclone. +c The algorithm will search out at specified distances from the +c storm center along 45-degree radials and bilinearly interpolate +c the winds to points along those radials. This will be done +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 radials: NE, SE, +c SW and NW (45,135,225,315). For the storm-relative estimates, +c these radials will be computed at the same relative angles (i.e., +c 45,135,225,315), but with respect (positive clockwise) to the +c direction of storm motion. For example, for a storm moving with +c a heading of 280, the wind structure is evaluated at these +c radials: 325 (front-right; 45 deg CW from heading), 55 (back- +c right; 135 deg CW from heading), 145 (back-left; 225 deg CW from +c heading), 235 (front-left; 315 deg CW from heading). +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated +c er_wind: Quadrant winds in earth-relative framework +c sr_wind: Quadrant winds in storm-relative framework +c er_vr: Quadrant radial winds in earth-relative framework +c sr_vr: Quadrant radial winds in storm-relative framework +c er_vt: Quadrant tangential winds in earth-relative framework +c sr_vt: Quadrant tangential winds in storm-relative framework + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4 + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igvtret,ipct,maxstorm + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,xsfclon,xsfclat + logical(1) valid_pt(imax,jmax) +c + data rdist/10.,25.,50.,75.,100.,125.,150.,200.,250.,300.,350. + & ,400.,450.,500./ + + igwsret = 0 + + er_wind = 0.0 + sr_wind = 0.0 + er_vr = 0.0 + er_vt = 0.0 + sr_vr = 0.0 + sr_vt = 0.0 + +c ----------------------------------------------------------------- +c Now determine the angle that the storm took getting from the +c last position to the current one. If this is the initial time, +c use the observed direction of motion from the TC Vitals. This +c may not match up with the model storm's initial direction of +c motion, but it is all we have available to us in order to get +c a heading estimate for the initial time. This storm heading +c information will be used for the storm-relative profiles. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_wind_structure, fhr= ',fhreal(ifh) + & ,' ',storm(ist)%tcv_storm_id + & ,' ',storm(ist)%tcv_storm_name + print '(a25,a23,f9.3)',' In get_wind_structure, ' + & ,' model storm heading = ',st_heading + print *,' ' + endif + + endif + +c ----------------------------------------------------------------- +c Get the profiles for the earth-relative coordinate system. +c Start with NE, then SE, SW, and NW. +c ----------------------------------------------------------------- + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *****************************************************' + print *,' Wind Structure: distbear bilin interp starts here.' + print *,' *****************************************************' + print *,' ' + endif + + do iquad = 1,4 + + bear = ((iquad-1) * 90.) + 45. + + if ( verb .ge. 3 ) then + print *,'structure iquad= ',iquad,' earth-relative bear= ' + & ,bear + endif + + do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,' ' + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,' ' + print '(5(a10,f7.2))',' sfclat= ',xsfclat + & ,' sfclon= ',xsfclon + & ,' rdist= ',rdist(idist),' targlat= ',targlat + & ,' targlon= ',targlon + print '(19x,a8,f7.2,35x,a9,f7.2)','sfclon= ',360.-xsfclon + & ,'targlon= ',360.-targlon + endif + + call bilin_int_uneven (targlat,targlon,rdist(idist) + & ,dx,dy,imax,jmax,trkrinfo,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon,rdist(idist) + & ,dx,dy,imax,jmax,trkrinfo,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + er_wind(iquad,idist) = sqrt (xintrp_u**2 + xintrp_v**2) + + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,er_vr(iquad,idist) + & ,er_vt(iquad,idist),igvtret) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + er_wind(iquad,idist) = -999.0 + er_vr(iquad,idist) = -999.0 + er_vt(iquad,idist) = -999.0 + else + igwsret = 95 + return + endif + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2)',' intrp wind speed= ' + & ,er_wind(iquad,idist),' (in kts)= ' + & ,er_wind(iquad,idist)*1.9427 + endif + + enddo + + enddo + +c ----------------------------------------------------------------- +c Get the profiles for the storm-relative coordinate system. +c Start with the front-right quadrant and go clockwise through +c back-right, back-left and front-left. +c ----------------------------------------------------------------- + + if ( verb .ge. 3 ) then + print *,' ' + endif + + do iquad = 1,4 + + temp_bear = st_heading + ((iquad-1) * 90.) + 45. + bear = mod(temp_bear,360.) + + if ( verb .ge. 3 ) then + print *,'structure iquad= ',iquad,' storm-relative bear= ' + & ,bear + endif + + do idist = 1,numdist + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + call bilin_int_uneven (targlat,targlon,rdist(idist) + & ,dx,dy,imax,jmax,trkrinfo,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon,rdist(idist) + & ,dx,dy,imax,jmax,trkrinfo,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + sr_wind(iquad,idist) = sqrt (xintrp_u**2 + xintrp_v**2) + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,sr_vr(iquad,idist) + & ,sr_vt(iquad,idist),igvtret) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + sr_wind(iquad,idist) = -999.0 + sr_vr(iquad,idist) = -999.0 + sr_vt(iquad,idist) = -999.0 + else + igwsret = 95 + return + endif + + enddo + + enddo +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,calcparm,wfract_cov,pdf_ct_bin,pdf_ct_tot,maxstorm + & ,trkrinfo,igfwret) +c +c ABSTRACT: This subroutine determines the fractional areal coverage +c of winds exceeding various thresholds within specified arcs +c (e.g., 200 km, 400 km, etc) in each quadrant of a storm. The bins +c that are used go as follows: (1) 0-100; (2) 0-200; (3) 0-300; +c (4) 0-400; (5) 0-500. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real wfract_cov(numquad+1,numbin,numthresh) + real area_total_quad_bin(numquad,numbin) + real area_exceed_quad_bin(numquad,numbin,numthresh) + real xintlon,xintlat + real :: windthresh(numthresh) = (/17.5,25.74,32.94/) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,conv_ms_knots,vmagkts + real rads,ri,dell,vmag,xarea,grdintincr,xsfclon,xsfclat + real sum_exceed_area(numbin,numthresh) + real sum_total_area(numbin,numthresh) + integer pdf_ct_bin(16) + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igfwret,ipct,i,j,numinterp,ixoa,ixaa,iq,ib,it,ii + integer jlatfix,ilonfix,npts,ibeg,iend,jbeg,jend,ngridint,ni,nj + integer itret,igiret,idistbin,ipdfbin,pdf_ct_tot,maxstorm + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) + character got_pdf*6 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*5 :: cbin(5) = + & (/'0-100','0-200','0-300','0-400','0-500'/) + character*2 :: cthresh(3) = (/'34','50','64'/) +c + igfwret = 0 + conv_ms_knots = 1.9427 + rads = 500.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + + wfract_cov = 0.0 + area_total_quad_bin = 0.0 + area_exceed_quad_bin = 0.0 + sum_exceed_area = 0.0 + sum_total_area = 0.0 + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_fract_wind_cov from call to ' + print *,'!!! get_ij_bounds, stopping processing for storm' + print *,'!!! number ',ist + endif + + igfwret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_fract_wind_cov calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igfwret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + +c When evaluating the winds at a gridpoint, keep in mind that each +c gridpoint represents area around it. There are 2 special cases +c we need to watch out for. The first is for cases in which the +c area of a gridpoint straddles across a distance threshold, so +c that some of the gridpoint's area is in the "<200" bin, while +c some is in the "<100" bin. The other is for the case in which +c the area of a gridpoint straddles between 2 adjacent quadrants +c (e.g., a gridpoint exactly to the north of the center would have +c half its area in the NW quadrant and half in the NE quadrant). +c +c To properly "partition" and assign gridpoint areas, we need to +c interpolate the current grid down to a fine resolution. +c +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the guidelines that +c will be used, keeping in mind that we want the final grid spacing +c to be on the order of between 0.05 and 0.10 degree (finer than +c 0.05 deg is superfluous, and coarser than 0.10 deg is too coarse). +c +c Original grid size (deg) # of interps +c ------------------------- ------------ +c 0.8 <= g 4 +c 0.4 <= g < 0.8 3 +c 0.2 <= g < 0.4 2 +c 0.1 <= g < 0.2 1 +c g < 0.1 0 + + + if ((dx+dy)/2. >= 0.8) then + numinterp = 4 + else if ((dx+dy)/2. < 0.8 .and. (dx+dy)/2. >= 0.4) then + numinterp = 3 + else if ((dx+dy)/2. < 0.4 .and. (dx+dy)/2. >= 0.2) then + numinterp = 2 + else if ((dx+dy)/2. < 0.2 .and. (dx+dy)/2. >= 0.1) then + numinterp = 1 + else + numinterp = 0 + endif + + grdintincr = (dx+dy)/2. + do i = 1,numinterp + grdintincr = 0.5 * grdintincr + enddo + +c Now loop through the points in this subdomain, determine if any +c are within 500 km of the center, and then determine what quadrant +c the point is in relative to the center, and then calculate the +c fractional area coverage for winds. + + pdf_ct_tot = 0 + pdf_ct_bin = 0 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_fract_wind_cov, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_fract_wind_cov' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle iloop ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > (rads+(0.75*((dx+dy)/2.)*dtk*cos(glat(j)*dtr)))) + & then + + ! If the distance is greater than "rads" (500 km at initial + ! writing) plus another 3/4 of a gridpoint, then cycle. + ! The extra 3/4 of a gridpoint is to allow for the case of + ! some portion of the area around a gridpoint (whose + ! center point > 500 km) being within the 500 km arc... + ! although that is only factored in for grids with spacing + ! >= 0.1 deg. For smaller grids, where no interpolation is + ! done in this subroutine, then the distance to that point + ! is considered representative and the point is ignored if + ! it is not less than 500 km from the center. + + cycle iloop + + else + + ! First interpolate the area surrounding each grid point to + ! get fine resolution of lats & lons for determining how to + ! partition the area of a gridpoint among quadrants as well + ! as among distance thresholds. + + vmag = sqrt (u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + vmagkts = vmag * conv_ms_knots + + if (numinterp > 0) then + + grdintincr = ((dx+dy)/2.) / 2**numinterp ! "grid spacing" + ! of interpolated grid + ngridint = (2**numinterp) / 2 + + got_pdf = 'notyet' + + njloop: do nj= ngridint,-ngridint,-1 + + xintlat = glat(j) + float(nj) * grdintincr + + niloop: do ni= -ngridint,ngridint + + xintlon = glon(ii) + float(ni) * grdintincr + + call calcdist (xintlon,xintlat,xsfclon + & ,xsfclat,xdist,degrees) + + if (xdist <= 350. .and. got_pdf == 'notyet') then + ! The got_pdf flag is needed because in these loops + ! for niloop & njloop, we are actually looking at + ! tiny areas around the same grid point. So we + ! want to make sure we only count each gridpoint + ! once. + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + got_pdf = 'got_it' + endif + + if (xdist < 500.) then + + ! Compute area of this fraction of a grid box + xarea = (grdintincr * 111195) * + & (grdintincr * 111195 + & * cos(xintlat * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Go through a loop of the bins. The purpose of + ! this is that these "bins" all go from the + ! the center out to a specified radius, they are + ! NOT 100-km wide bins. So if we are dealing with + ! a point at r = 250 km, then that falls in the + ! 0-300 km bin, but it also falls in the 0-400 and + ! 0-500 km bins as well. So we need to run through + ! this binloop multiple times to get the area data + ! into multiple bins. Here are the bins & indices: + ! 1: 0-100 km + ! 2: 0-200 km + ! 3: 0-300 km + ! 4: 0-400 km + ! 5: 0-500 km + + binloop: do ib = idistbin,numbin + + if (xintlon >= xsfclon .and. + & xintlat >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (xintlon >= xsfclon .and. + & xintlat < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop + + endif + + enddo niloop + + enddo njloop + + else + + ! In this else statement is the case for a grid whose + ! resolution is already fine enough that we don't need + ! to interpolate any further. For example, we will have + ! the H*Wind data on a 0.05 degree grid, so that's already + ! fine enough. + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat + & ,xdist,degrees) + + if (xdist <= 350.) then + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + endif + + if (xdist < 500.) then + + ! Compute area of this grid box + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Why the binloop2? See explanation above in the "if" + ! part of this if-then block, where binloop is. + + binloop2: do ib = idistbin,numbin + + if (glon(ii) >= xsfclon .and. + & glat(j) >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (glon(ii) >= xsfclon .and. + & glat(j) < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop2 + + endif + + endif + + endif + + enddo iloop + + enddo jloop + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different quadrants, bins and thresholds... +c ------------------------------------------------- + + if ( verb .ge. 3 ) then + write (6,109) ' ' + & ,' ' + & ,' ' + write (6,109) ' Quadrant Bin Wind_Thresh ' + & ,'Fract_coverage (%) Area_exceeded' + & ,' Area_total' + write (6,109) ' -------- --- ----------- ' + & ,'------------------ -------------' + & ,' ----------' + write (6,109) ' ' + & ,' ' + & ,' ' + + do iq = 1,numquad + do ib = 1,numbin + do it = 1,numthresh + wfract_cov(iq,ib,it) = area_exceed_quad_bin(iq,ib,it) / + & area_total_quad_bin(iq,ib) + write (6,117) cquad(iq),cbin(ib),cthresh(it) + & ,wfract_cov(iq,ib,it)*100.0 + & ,area_exceed_quad_bin(iq,ib,it) + & ,area_total_quad_bin(iq,ib) + enddo + enddo + enddo + endif + + + 109 format (1x,a33,a37,a16) + 117 format (5x,a2,5x,a5,7x,a2,13x,f6.2,10x,f16.1,2x,f16.1) + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different bins and thresholds, but for the +c entire "disc" of the storm, that is, summing all +c quadrants together. +c ------------------------------------------------- + + do it = 1,numthresh + do ib = 1,numbin + do iq = 1,numquad + sum_total_area(ib,it) = sum_total_area(ib,it) + & + area_total_quad_bin(iq,ib) + sum_exceed_area(ib,it) = sum_exceed_area(ib,it) + & + area_exceed_quad_bin(iq,ib,it) + enddo + wfract_cov(5,ib,it) = sum_exceed_area(ib,it) + & / sum_total_area(ib,it) + enddo + enddo + + if ( verb .ge. 3 ) then + do ib = 1,numbin + do it = 1,numthresh + write (6,117) 'TT',cbin(ib),cthresh(it) + & ,wfract_cov(5,ib,it)*100.0 + & ,sum_exceed_area(ib,it) + & ,sum_total_area(ib,it) + enddo + enddo + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_ike_stats (imax,jmax,inp,dx,dy,ist,ifh + & ,fixlon,fixlat,xsfclon,xsfclat,valid_pt,calcparm + & ,ike,sdp,wdp,maxstorm,trkrinfo,igisret) +c +c ABSTRACT: This subroutine computes the Integrated Kinetic Energy +c (IKE) and Storm Surge Damage Potential (SDP) values, based on +c Powell (BAMS, 2007). At this time, we are only computing the IKE +c values for TS threshold (17.5 m/s) and above. We are not yet +c computing wind damage potential (WDP) since, per Mark Powell +c (4/2008), he is currently re-formulating an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c sdp Storm surge damage potential + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4 + integer npts,ipct,igisret,imax,jmax,ist,ifh,ilonfix,jlatfix + integer ibeg,jbeg,iend,jend,igiret,i,j,maxstorm,ii + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real ike(max_ike_cats) + real dx,dy,degrees,rads,ri,dell,xdist,vmag,xarea + real xsfclon,xsfclat,sdp,wdp + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) +c + igisret = 0 + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + rads = 400.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_ike_stats from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for storm ' + print *,'!!! number ',ist + endif + + igisret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_ike_stats calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igisret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + +c Search a grid of points near the storm center, evaluate if the +c storm is within the "rads" distance threshold. If so, compute +c the IKE values for all applicable thresholds (10, 18, 33 m/s). + + do j = jbeg,jend + do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ike_stats, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_ike_stats' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > rads) then + cycle + else + + vmag = sqrt(u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + + if (vmag > 10.0) then + ! Add gridpoint to IKE_10. Compute area first... + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + ike(1) = ike(1) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 18.0) then + ! Add gridpoint to IKE_ts. Area already computed for 10 + ike(2) = ike(2) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 33.0) then + ! Add gridpoint to IKE_h. Area already computed for 10 + ike(3) = ike(3) + (0.5 * (vmag**2) * xarea) + endif + + endif + + enddo + enddo + + ike(1) = ike(1) * 1.e-12 ! Convert from J to TJ + ike(2) = ike(2) * 1.e-12 ! Convert from J to TJ + ike(3) = ike(3) * 1.e-12 ! Convert from J to TJ + +c Compute the storm surge damage potential (sdp) + + if (ike(2) >= 0.0) then + sdp = 0.676 + (0.43 * sqrt(ike(2))) + & - (0.0176 * ((sqrt(ike(2)) - 6.5)**2) ) + else + sdp = -99.0 + endif + +c Print out the IKE and SDP statistics... + + if ( verb .ge. 3 ) then + print *,' IKE_10 (storm energy) = ',ike(1) + print *,' IKE_TS (tropical storm) = ',ike(2) + print *,' IKE_H (hurricane) = ',ike(3) + print *,' SDP = ',sdp + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine distbear (xlatin,xlonin,dist,bear,xlatt,xlont) +c +c ABSTRACT: Given an origin at latitude, longitude=xlato,xlono, +c this subroutine will locate a target point at a distance dist in +c km or nautical miles (depends on what you use for "rad_earth..." +c below), at bearing bear (degrees clockwise from north). +c Returns latitude xlatt and longitude xlont of target point. +c +c *** NOTE *** +c This subroutine was written to handle input lats & lons as this: +c All latitudes are in degrees, north positive and south negative. +c All longitudes are in degrees, west positive and east negative. +c *** **** *** +c +c However, for the longitudes, the rest of the tracker uses all +c 0-360 longitudes. Therefore, we need to convert the input lons +c and then once again convert the lons that are returned back to +c the calling routine. +c +c NOTE-- When origin is at north or south pole, bearing is no +c longer measured from north. Instead, bearing is measured +c clockwise from the longitude opposite that specified in xlono. +c Example-- if xlato=90., xlono=80., the opposite longitude is +c -100 (100 East), and a target at bearing 30. will lie on the +c -70. (70 East) meridian. +c +c AUTHOR: The core of this subroutine was written by Albion +c Taylor, another NOAA employee, in 1981. +c + USE trig_vals + + implicit none +c + real, parameter :: rad_earth_nm = 3440.170 ! radius of earth + real, parameter :: rad_earth_km = 6372.797 ! radius of earth + real xlato,xlono,dist,bear,xlatt,xlont,xlatin,xlonin + real cdist,sdist,clato,slato,clono,slono,cbear,sbear + real z,y,x,r,xlattz,xlontz,ddist,dbear,dxlato,dxlono +c + xlato = xlatin + xlono = xlonin + +cstr print *,' ' +cstr print *,'+++ At top of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlon= ',xlono,'E ',360.-xlono +cstr & ,'W xlat=',xlato +cstr print '(a6,f7.2,a8,f7.2)','dist= ',dist,' bear= ',bear + + if (xlono > 180.) then + ! Longitude input for this subroutine must be positive west + xlono = 360. - xlono + else + ! Longitude input for this subroutine must be negative east + xlono = -1. * xlono + endif + +cstr print '(a31,a8,f8.2)','After conversion for distbear, ' +cstr & ,' xlono= ',xlono + + ddist = dist + dbear = bear + dxlato = xlato + dxlono = xlono + + cdist = cos(ddist/rad_earth_km) + sdist = sin(ddist/rad_earth_km) + clato = cos(dtr*dxlato) + slato = sin(dtr*dxlato) + +cstr print *,'cdist= ',cdist,' sdist= ',sdist,' clato= ',clato +cstr & ,' slato= ',slato + + clono = cos(dtr*dxlono) + slono = sin(dtr*dxlono) + +cstr print *,'dxlono= ',dxlono,' clono= ',clono +cstr & ,' slono= ',slono + + cbear = cos(dtr*dbear) + sbear = sin(dtr*dbear) + +cstr print *,'cbear= ',cbear,' sbear= ',sbear + + z=cdist*slato + clato*sdist*cbear + y=clato*clono*cdist + sdist*(slono*sbear - slato*clono*cbear) + x=clato*slono*cdist - sdist*(clono*sbear + slato*slono*cbear) + +cstr print *,'z= ',z,' y= ',y,' x= ',x + + r = sqrt(x**2 + y**2) + +cstr print *,'r = sqrt(x**2 + y**2) = ',r + + xlattz = atan2(z,r)/dtr + +cstr print *,'xlattz = datan2(z,r)/dtr = ',xlattz + + xlatt = xlattz + + if (r <= 0.) go to 20 + + xlontz = atan2(x,y)/dtr + +cstr print *,'xlontz = atan2(x,y)/dtr = ',xlontz + +c xlont = xlontz + + ! Return the target longitude back to the calling routine + ! as a 0-360 positive east longitude.... + + xlont = mod(360.-xlontz,360.) + +c xlont = mod(360.+xlontz,360.) + +cstr print *,' ' +cstr print *,'At end of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlont= ',xlont,'E ' +cstr ,360.-xlont,'W xlatt=',xlatt + + return + 20 xlont=0. +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_uneven (targlat,targlon,targdist,dx,dy + & ,imax,jmax,trkrinfo,cparm,xintrp_val,ibiret) +c +c ABSTRACT: This subroutine performs a bilinear interpolation to get +c a data value at a given lat/lon that may be anywhere within a box +c defined by the four surrouding grid points. In the diagram below, +c remember that for our grids we are using in the tracker, the +c latitude index starts at the north pole and increases southward. +c The point "X" indicates the target lat/lon location of the value +c for which we are bilinearly interpolating. The values to and ta +c below are ratios that determine how geographically close the +c target location is to the point of origin (pt.1 (i,j)) in terms +c of both longitude (to) and latitude (ta). +c +c +c pt.1 pt.2 +c (i,j) (i+1,j) +c +c +c +c X +c +c pt.4 pt.3 +c (i,j+1) (i+1,j+1) +c + + USE grid_bounds; USE tracked_parms; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + character cparm*1 + real targlat,targlon,targdist,xintrp_val,dx,dy + real to,ta,d1,d2,d3,d4,z,eastlon + integer ie,iw,jn,js,ibiret,imax,jmax + + ibiret = 0 + +c -------------------------------------------------------------- +c For the latitudes and longitudes surrounding our target +c lat/lon location, convert the lat/lon values into i- and +c j-indices. +c -------------------------------------------------------------- + +c Find the j-indices for the points just to the north and the +c south of targlat.... + + if (targlat >= 0.0) then + ! For a northern hemisphere storm, jn is the j-index for the + ! point just to the *NORTH* (poleward) of targlat. + jn = int((glatmax - targlat)/dy + 1.) + js = jn + 1 + else + ! For a southern hemisphere storm, js is the j-index for the + ! point just to the *SOUTH* (poleward) of targlat. + js = ceiling((glatmax - targlat)/dy + 1.) + jn = js - 1 + endif + + ! Check to make sure that points are not being requested beyond + ! the northern or southern boundaries of the grid. This is most + ! likely to happen for a smaller, regional grid. + + if (jn > jmax .or. js > jmax) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jmax exceeded in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + + if (jn < 1 .or. js < 1) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jn < 0 or js < 0 in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + +c Find the i-indices for the points just to the east and the +c west of targlon.... + + ie = int((targlon - glonmin)/dx + 2.) + iw = ie - 1 + + ! Check for GM wrapping. Check ie to see if it is between the + ! most eastward gridpoint and the GM (i.e., on a 1-deg global + ! grid (360x181), it would be if targlon was between 359.0 (i=360) + ! and the GM (i=1, not i=361)). Similarly then, if we adjust ie + ! to then be 1, then we have a problem with iw, + ! since iw = 1 - 1 = 0. + + if (ie > imax) then + if (trkrinfo%gridtype == 'global') then + ie = ie - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ie > imax in subroutine ' + print *,'!!! bilin_int_uneven for a non-global grid. ' + print *,'!!! Returning to calling routine after ' + print *,'!!! assigning missing wind value of -99.' + print *,'!!! ie= ',ie,' imax= ',imax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + + if (iw < 1) then + if (trkrinfo%gridtype == 'global') then + iw = iw + imax + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: iw < 1 in subroutine bilin_int_uneven' + print *,'!!! for a non-global grid. Returning to calling ' + print *,'!!! routine after assigning missing wind value ' + print *,'!!! of -99. iw= ',iw + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + + if ( verb .ge. 3 ) then + print *,' +++ Interpolating winds for cparm= ',cparm + print '(6x,4(a4,i3))','jn= ',jn,' js= ',js,' iw= ',iw,' ie= ',ie + endif + +c ---------------------------------------------------------------- +c Calculate the longitude (to) and latitude (ta) location ratios. +c Check for GM wrapping, as we can run into a problem here if +c interpolating for points that are just west of the GM, since we +c would be interpolating using values of longitude just west of +c GM (say, glon(iw)=359.5) and the GM (glon(ie) = 0.0). This +c makes for an incorrect "to" ratio below, with 0-359.5 in the +c denominator. We have to account for this.... +c ---------------------------------------------------------------- + + if (glon(iw) > 300.0 .and. + & (glon(ie) < 10. .and. glon(ie) >= 0.)) then + eastlon = 360. - glon(ie) + else + eastlon = glon(ie) + endif + + if ( verb .ge. 3 ) then + print *,'glat(js)= ',glat(js),' glat(jn)= ',glat(jn) + endif + + to = (targlon - glon(iw)) / (eastlon - glon(iw)) + ta = (targlat - glat(jn)) / (glat(js) - glat(jn)) + +c -------------------------------------------------------------- +c Copy the data values at the 4 known points into simple scalar +c variables +c -------------------------------------------------------------- + + if (cparm == 'u') then + d1 = u(iw,jn,levsfc) + d2 = u(ie,jn,levsfc) + d3 = u(ie,js,levsfc) + d4 = u(iw,js,levsfc) + else if (cparm == 'v') then + d1 = v(iw,jn,levsfc) + d2 = v(ie,jn,levsfc) + d3 = v(ie,js,levsfc) + d4 = v(iw,js,levsfc) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in bilin_int_uneven.' + print *,'!!! Input cparm not recognized.' + print *,'!!! cparm= ',cparm + print *,'!!! EXITING....' + endif + + stop 95 + endif + + z = 1.9427 + +cstr print '(2x,4(a4,f8.2))',' d1= ',d1*z,' d2= ',d2*z +cstr & ,' d3= ',d3*z,' d4= ',d4*z + +c ------------------------------------------------------------- +c Compute the interpolated value +c ------------------------------------------------------------- + + xintrp_val = (1.-to) * (1.-ta) * d1 + & + to * (1.-ta) * d2 + & + to * ta * d3 + & + (1.-to) * ta * d4 + +cstr print '(2x,2(a11,f8.2))',' xintrp= ',xintrp_val,' (in kts)= ' +cstr & ,xintrp_val*z +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine sort_storms_by_pressure (gridprs,ifh,maxstorm,sortindex + & ,issret) +c +c ABSTRACT: This subroutine sorts storms by mslp. It is called by +c subroutine tracker just before the loop for "stormloop" is done +c for all the storms at a particular forecast hour. It is only +c called for the "midlat" and "tcgen" cases. The end result of +c this sort is an array (prsindex) that contains the indeces of +c the storms, arranged from lowest pressure to highest (and note +c that the "undefined" storms have a pressure of 9999.99 mb and +c thus get sorted to the bottom of the array). The purpose of +c doing this is so that we track the most intense storms first. +c Why go to the trouble? Imagine a scenario in which we are +c tracking a complex system in which there are 2 low pressure +c centers. Let's say that one is becoming dominant and +c intensifying, while the other is weakening. Now, let's assume +c that the weakening one eventually gets absorbed into the +c stronger, more dominant low. Now we only have 1 low, but if in +c the tracker stormloop, we first process the data for the +c weakening low, we will attribute the track to that storm, and +c then when we get to the point in the loop where we are trying +c to get the track for the stronger storm, we will (erroneously) +c stop the tracking for that storm since the storm center has +c already been attributed to the weaker storm. But by using this +c subroutine, we will track the stronger storm first, and thus +c avoid this problem. +c +c NOTE: The pressures used in the sort are those obtained at the +c previous forecast hour. At forecast hour = 0, just use the +c values as they were input to this routine, since they were +c found in first_ges_center from strongest to weakest already. +c +c INPUT: +c gridprs real array of storm mslp values +c ifh integer index for the current forecast hour +c maxstorm max num of storms that can be handled in this run +c +c OUTPUT: +c sortindex contains a sorted array of indeces. The orders +c sort routine does NOT rearrange the data. Rather, it +c returns this array of sorted indeces which point to +c the correct order of data values in the data array. +c issret return code from this subroutine +c + USE set_max_parms + USE verbose_output + + real, allocatable :: iwork(:) + real gridprs(maxstorm,maxtime) + integer ifh,maxstorm + integer sortindex(maxstorm) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: prstemp(:) +c + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iva /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub sort_storms_by_pressure allocating' + print *,'!!! prstemp or iwork arrays: ' + print *,'!!! iva= ',iva,' iwa= ',iwa + endif + + STOP 94 + return + endif + + if (ifh > 1) then + +c print *,' ' +c print *,'--- Before sort, original prs values follow:' +c print *,' ' + + do ist = 1,maxstorm + prstemp(ist) = gridprs(ist,ifh-1) +c write (6,81) ist,prstemp(ist)/100.0 + enddo + + imode = 2 + sortindex = 0 + call qsort (prstemp,sortindex,maxstorm) + +ccccc call orders (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) +ccccc call orders_4byte (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Pressure-sorted storm list:' + print *,' ' + + do ist = 1,maxstorm + if (prstemp(sortindex(ist))/100.0 < 9999.0) then + write (6,82) ist,sortindex(ist) + & ,prstemp(sortindex(ist))/100.0 + endif + enddo + + 81 format (1x,'ist= ',i5,' Original (unsorted) prstemp= ',f7.2) + 82 format (1x,'ist= ',i5,' sortindex(ist)= ',i5 + & ,' prstemp= ',f7.2) + endif + + else + do ist = 1,maxstorm + sortindex(ist) = ist + enddo + endif + + deallocate (prstemp); deallocate (iwork) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getvrvt (centlon,centlat,xlon,xlat + & ,udat,vdat,vr,vt,igvtret) +c +c ABSTRACT: This subroutine takes as input a u-wind and v-wind value +c at an input (xlon,xlat) location and returns the tangential and +c radial wind components relative to the input center lat/lon +c position (centlon,centlat). The only trick to this whole +c subroutine is figuring out the angle from the center point to the +c data point, and we do this by creating a triangle with the leg +c from the center point to the data point being the hypotenuse. +c +c NOTE: All longitudes must be in positive degrees east (0-360) !!! +c +c INPUT: +c centlon Longitude of center point +c centlat Latitude of center point +c xlon Longitude of pt at which vr & vt will be computed +c xlat Latitude of pt at which vr & vt will be computed +c udat u-value of wind at the point (xlon,xlat) +c vdat v-value of wind at the point (xlon,xlat) +c +c OUTPUT: +c vr Radial wind component at (xlon,xlat) wrt (centlon,centlat) +c vt Tang wind component at (xlon,xlat) wrt (centlon,centlat) +c igvtret Return code from this subroutine + + USE trig_vals + USE verbose_output + + real centlon,centlat,xlon,xlat,udat,vdat,vr,vt,degrees +c + call calcdist(centlon,centlat,xlon,xlat,hyp_dist,degrees) + + xlatdiff = abs(centlat - xlat) + xlondiff = abs(centlon - xlon) + + if (xlondiff == 0 .and. xlatdiff > 0) then + + if (centlat > xlat) angle = 180 ! pt directly south of ctr + if (centlat < xlat) angle = 0 ! pt directly north of ctr + + else if (xlondiff > 0 .and. xlatdiff == 0) then + + if (centlon > xlon) angle = 270 ! pt directly west of ctr + if (centlon < xlon) angle = 90 ! pt directly east of ctr + + else + + ! This next part figures out the angle from the center point + ! (centlon,centlat) to the data point (xlon,xlat). It does + ! this by setting up a triangle and then using inverse trig + ! functions to get the angle. Since this is a kludgy way to + ! do it that doesn't account for the curvature of the earth, + ! we'll do it 2 ways, using asin and then acos, then take the + ! average of those 2 for the angle. hyp_dist, calculated just + ! above, is the distance from the center pt to the data pt. + + opp_dist = xlatdiff/360. * ecircum + sin_value = opp_dist / hyp_dist + if (sin_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, sin_value > 1, setting to 1.' + print *,'!!! opp_dist= ',opp_dist,' hyp_dist= ',hyp_dist + print *,'!!! sin_value = ',sin_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! xlon= ',xlon,' xlat= ',xlat + print *,' ' + endif + + sin_value = 0.99999 + endif + sin_angle = asin(sin_value) / dtr + + call calcdist(centlon,centlat,xlon,centlat,adj_dist,degrees) + cos_value = adj_dist / hyp_dist + if (cos_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, cos_value > 1, setting to 1.' + print *,'!!! adj_dist= ',adj_dist,' hyp_dist= ',hyp_dist + print *,'!!! cos_value = ',cos_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! xlon= ',xlon,' xlat= ',xlat + print *,' ' + endif + + cos_value = 0.99999 + endif + cos_angle = acos(cos_value) / dtr + + tmpangle = 0.5 * (sin_angle + cos_angle) + + ! The previous lines of code just calculated an angle between + ! 0 and 90. This next if structure adjusts that angle to + ! instead be between 0 and 360. + + if (centlat <= xlat .and. centlon <= xlon) then + angle = 90 - tmpangle + else if (centlat > xlat .and. centlon <= xlon) then + angle = 90 + tmpangle + else if (centlat >= xlat .and. centlon >= xlon) then + angle = 270 - tmpangle + else if (centlat < xlat .and. centlon >= xlon) then + angle = 270 + tmpangle + endif + + endif + + uvrcomp = udat * sin(angle * dtr) + vvrcomp = vdat * cos(angle * dtr) + vr = uvrcomp + vvrcomp + + uvtcomp = (-udat) * cos(angle * dtr) + vvtcomp = vdat * sin(angle * dtr) + vt = uvtcomp + vvtcomp + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcfunix (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in the new ATCF UNIX format. +c Unlike the old atcf DOS format in which you waited until the +c whole tracking was over to write the output for all forecast +c hours, with this atcfunix format, each time we are calling this +c subroutine, it is to only write out 1 record, which will be the +c fix info for a particular storm at a given time. Also, even +c though we have some data (GFS, NAM) at 6-hour intervals, Jim +c Gross informed me that TPC does not need the positions at such +c frequency, and keeping the reporting at 12 hour intervals is fine. +c +c While this new atcfunix format contains much more information than +c the old 1-line atcf dos message, for our purposes we will use the +c slots for mslp and wind radii. An example set of output records +c will look like the following: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c plastbar pressure of the outermost closed isobar +c rlastbar radius (nm) of the outermost closed isobar +c rmax radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c cps_vals real array with the values for the 3 cyclone phase +c space parameters: (1) is for Parameter B (thermal +c asymmetry); (2) is for lower level (600-900 mb) thermal +c wind; (3) is for upper level (300-600 mb) thermal wind. +c wcore_flag character for value of 300-500 mb warm core: y, n, or +c 'u' for undetermined. +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE phase + USE verbose_output + + type (datecard) inp + type (trackstuff) trkrinfo + + real cps_vals(3) + real outlon,outlat,rmax + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,irmax,output_fhr,ic,iplastbar,irlastbar + integer vradius(3,4),icps_vals(3) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + character comma_fill1*48,comma_fill2*31,comma_filler*79 + + if ( verb .ge. 3 ) then + print *,'TTT top of atcfunix, ist= ',ist,' ifh= ',ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'in output_atcfunix, tcv_storm_id= ' + & ,storm(ist)%tcv_storm_id + print *,'in output_atcfunix, tcv_storm_id(3:3)= ' + & ,storm(ist)%tcv_storm_id(3:3) + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' +!zhang case ('A','a'); basinid = 'NA' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + if (trkrinfo%want_oci) then + if (plastbar > 0.0) then + iplastbar = int(plastbar/100 + 0.5) + else + iplastbar = -99 + endif + if (rlastbar > 0.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -99 + endif + else + iplastbar = -99 + irlastbar = -99 + endif + + if ( verb .ge. 3 ) then + print *, 'output: rlastbar=',rlastbar,' irlastbar=',irlastbar + print *, 'output: plastbar=',plastbar,' iplastbar=',iplastbar + endif + +c Now convert all of the cyclone phase space parameter values from +c real to integer. + + do ic = 1,3 + if (cps_vals(ic) > -9999.0) then + if (cps_vals(ic) >= 0.0) then + icps_vals(ic) = int(cps_vals(ic)*10. + 0.5) + else + icps_vals(ic) = int(cps_vals(ic)*10. - 0.5) + endif + else + icps_vals(ic) = -9999 + endif + enddo + + if (wcore_flag == 'y') wcore_flag = 'Y' + if (wcore_flag == 'n') wcore_flag = 'N' + if (wcore_flag == 'u') wcore_flag = 'U' + + comma_fill1 = ', 0, 0, , 0, , 0, 0, ,' + comma_fill2 = ' , , , 0, 0, 0, 0' + comma_filler = comma_fill1//comma_fill2 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + else + + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,'rmax= ',rmax,' irmax= ',irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,a79,', THERMO PARAMS' + & ,3(', ',i7),', ',a1,', ',i2,', DT, -999') + 91 format (a2,', ',a4,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,2(', ',i3),', ',a3) + +c bug fix for IBM: flush the output stream so it actually writes + flush(64) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each +c storm. This message contains the model identifier, the forecast +c initial date, and the positions for 0, 12, 24, 36, 48, 60 and 72 +c hours. In the case of the regional models (NGM, Eta), which +c only go out to 48h, zeroes are included for forecast hours +c 60 and 72. +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by Steve Lord for plotting purposes, and his +c plotting routines need the longitudes in 0 - 360, increasing +c westward. Thus, a necessary adjustment is made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + print *,'top of output_all' + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + intlon(ifh) = 0 + intlat(ifh) = 0 + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + endif + + enddo ifhloop + + print *,'before select case, atcfname= ' + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(5),intlon(5),intlat(9),intlon(9),intlat(13) + & ,intlon(13),intlat(17),intlon(17),intlat(21),intlon(21) + & ,0,0,storm(ist)%tcv_storm_id + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(3),intlon(3),intlat(5),intlon(5),intlat(7) + & ,intlon(7),intlat(9),intlon(9),intlat(11),intlon(11) + & ,intlat(13),intlon(13),storm(ist)%tcv_storm_id + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NOGAPS, ECMWF Ensemble + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3),intlat(4) + & ,intlon(4),intlat(5),intlon(5),intlat(6),intlon(6) + & ,intlat(7),intlon(7),storm(ist)%tcv_storm_id + + case ('GDA','HDA') ! GDAS, HDAS + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),0,0,0,0,0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case default + print *,' ' +! print *,'!!! Warning from subroutine output_all. ' + print *,'!!! Model name is not identified. ',atcfname +! print *,'!!! Model name = ',atcfname +! print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,'!!! Model is assumed to be parallel GDAS/GFS. ' + + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + 81 format (i2,a4,4i2.2,14i4,1x,a3) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm + & ,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each storm +c in ATCF format. This message contains the model identifier, the +c forecast initial date, and the positions for 12, 24, 36, 48 +c and 72 hours. This message also contains the intensity +c estimates (in knots) for those same hours. The conversion for +c m/s to knots is to multiply m/s by 1.9427 (3.281 ft/m, +c 1 naut mile/6080 ft, 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by the atcf system at TPC for plotting +c purposes, and the atcf plotting routines need the longitudes in +c 0 - 360, increasing westward. Thus, a necessary adjustment is +c made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real xmaxwind(maxstorm,maxtime) + real conv_ms_knots + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4,basinid*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + conv_ms_knots = 1.9427 + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + + intlon(ifh) = 0 + intlat(ifh) = 0 + + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + + endif + + enddo ifhloop + + basinid = ' ' + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid(1:2) = 'AL' + case ('E','e'); basinid(1:2) = 'EP' + case ('C','c'); basinid(1:2) = 'CP' + case ('W','w'); basinid(1:2) = 'WP' + case ('O','o'); basinid(1:2) = 'SC' + case ('T','t'); basinid(1:2) = 'EC' + case ('U','u'); basinid(1:2) = 'AU' + case ('P','p'); basinid(1:2) = 'SP' + case ('S','s'); basinid(1:2) = 'SI' + case ('B','b'); basinid(1:2) = 'BB' + case ('A','a'); basinid(1:2) = 'AA' + case default; basinid(1:2) = '**' + end select + basinid(3:4) = storm(ist)%tcv_storm_id(1:2) + + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(5),intlon(5) + & ,intlat(9),intlon(9),intlat(13),intlon(13),intlat(17) + & ,intlon(17),0,0 + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,17)*conv_ms_knots) + 0.5) + & ,0 + & ,basinid,inp%byy + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NOGAPS, ECMWF Ensemble, ECMWF hi-res + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4),intlat(5) + & ,intlon(5),intlat(7),intlon(7) + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('GDA','HDA') ! GDAS, HDAS + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4) + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,0,0,0,0 + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case default + print *,' ' + print *,'!!! ERROR in subroutine output_atcf. ' + print *,'!!! Model name is not identified.' + print *,'!!! Model name = ',atcfname + print *,'!!! ist = ',ist,' Model number = ',atcfnum + + end select + + enddo stormloop + + 82 format (i2,a4,4i2.2,10i4,5i3,1x,a4,i2.2) +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_hfip (outlon,outlat,inp,ist + & ,ifh,vmaxwind,xminmslp,vradius,rmax,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified ATCF UNIX format. +c The modification is to allow for sub-hourly output. That is, +c instead of just integer output hours, we can have output at +c 10, 15 or 20 past an hour. This necessitates a change in the +c "forecast hour" placeholder in the ATCF format. Instead of it +c being an I3, we'll make it an I5, with something like a lead time +c of 36.25h being rounded and truncated to 03625 for output. +c +c An example set of output records using the standard atcf format +c looks like the following: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c An example set of modified output records will look like the +c following, for the case of a lead time of 36:15 (36.25): +c +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifh index for the lead time array +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c rmax Radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms + USE verbose_output + + type (datecard) inp + + real outlon,outlat + real vmaxwind,conv_ms_knots,xminmslp,rmax + integer intlon,intlat,output_fhr,irmax,ileadtime + integer vradius(3,4) + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + ! ST: ifcsthour does not exist, so output_fhr is always + ! filled with invalid data here. However, output_fhr is + ! never used, so it is safe to remove. + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + ! output_fhr = ifcsthour + 3 + ileadtime = nint((fhreal(ifh) + 3.0) * 100.0) + else + ! output_fhr = ifcsthour + ileadtime = nint(fhreal(ifh) * 100.0) + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4),irmax + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4),irmax + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4),irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i5.5,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', 0, 0, ',i3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(69) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_fract_wind (outlon,outlat,xsfclon,xsfclat + & ,inp,ist,ifcsthour,vmaxwind,xminmslp,wfract_cov + & ,wfract_type,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values for the fractional areal coverage of various wind +c thresholds. In addition, this subroutine also writes out +c records to a file containing data on the PDF of wind magnitudes +c within r=350 km. +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with areal coverage thresholds. +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, NEE, 981, 857, 629, 810 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, NEE, 874, 732, 319, 610 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, NEE, 454, 327, 99, 270 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, AAE, 721, 721, 721, 721 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, AAE, 465, 465, 465, 465 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, AAE, 298, 298, 298, 298 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the pctgs for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind pctg info; all the +c other info is identical for each entry. +c +c Listed after the "XX" in each record is the radius from which +c the coverage is valid (000 km in this case); Next is the radius +c at which the coverage stops (100 km in this case). Next is the +c wind threshold (34, 50, 64). Next is an identifier for which +c quadrant the coverage starts in (first 2 characters are NE, SE, +c SW, NW); the last character indicates if the coverages are +c computed in the quadrants as earth-relative ("E") or +c storm-motion relative ("R"). The ones listed there as "AAE" +c are for the full disc (i.e., 4-quadrant average), earth-relative. +c Next are the wind coverage percentages, listed as percentage * 10 +c (e.g., 981 = 98.1%). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c wfract_cov percent areal coverage for various wind thresholds +c wfract_type 'earth' or 'storm' relative analysis +c pdf_ct_bin array for pdf of wind magnitudes within r=350 km +c pdf_ct_tot total count of pdf points for r < 350 km +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,pdfval + real wfract_cov(numquad+1,numbin,numthresh) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer :: windthresh(numthresh) = (/34,50,64/) + integer pdf_ct_bin(16) + integer intlon,intlat,output_fhr,intlon100,intlat100,pdf_ct_tot + integer maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (wfract_type == 'earth') then + wt = 'E' + else if (wfract_type == 'storm') then + wt = 'R' + else + wt = 'X' + endif + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'NE',wt + & ,int((1000.*wfract_cov(1,ib,it))+0.5) + & ,int((1000.*wfract_cov(2,ib,it))+0.5) + & ,int((1000.*wfract_cov(3,ib,it))+0.5) + & ,int((1000.*wfract_cov(4,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'AA',wt + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a6,i3.3,', ',i3.3,', ' + & ,i3,', ',a2,a1,4(', ',i4),', ',i4,a1,', ',i5,a1) + +c -------------------------------------------------- +c Now compute and write out the pdf values for the +c wind magnitude.... +c -------------------------------------------------- + + do ip = 1,16 + pdfval = float(pdf_ct_bin(ip)) / float(pdf_ct_tot) + write (76,85) atcfymdh,basinid,storm(ist)%tcv_storm_id(1:2) + & ,output_fhr,10*(ip-1),10*ip,pdf_ct_bin(ip) + & ,pdf_ct_tot,pdfval + enddo + + 85 format (1x,i10.10,3x,a2,a2,3x,i3,3x,i3.3,'_',i3.3,3x,i7,2x,i7 + & ,2x,f6.3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(73) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_wind_structure (outlon,outlat,xsfclon + & ,xsfclat,inp,ist,ifcsthour,vmaxwind,xminmslp,er_wind + & ,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values of the winds at specified distances along 45-degree +c radials in each storm quadrant. These are output +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 radials: NE, SE, +c SW and NW (45,135,225,315). For the storm-relative estimates, +c these radials will be computed at the same relative angles (i.e., +c 45,135,225,315), but with respect (positive clockwise) to the +c direction of storm motion. For example, for a storm moving with +c a heading of 280, the wind structure is evaluated at these +c radials: 325 (front-right; 45 deg CW from heading), 55 (back- +c right; 135 deg CW from heading), 145 (back-left; 225 deg CW from +c heading), 235 (front-left; 315 deg CW from heading). +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with wind values at the 13 specified distances +c (10, 25, 50, 75, 100, 150, 200, 250, 300, 350, 400, 450, 500 km) +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NEE, 1137, 1221, 854, 655, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SEE, 947, 982, 474, 396, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SWE, 645, 683, 328, 277, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NWE, 725, 753, 619, 429, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FRR, 1134, 1224, 852, 654, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BRR, 944, 984, 472, 393, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BLR, 649, 686, 321, 272, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FLR, 729, 756, 613, 421, etc., ... out to 500 km +c +c NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text. +c NOTE: These winds are in m/s coming into this routine and will +c be converted to knots*10 for output (e.g., 1221 = 122.1 kts) +c +c The "71" ID indicates earth-relative winds, the "72" ID indicates +c storm-relative winds. Here are the other IDs that will be used: +c 81: Tangential winds, earth-relative (m/s) +c 82: Tangential winds, storm-relative (m/s) +c 91: Radial winds, earth-relative (m/s) +c 92: Radial winds, storm-relative (m/s) +c +c Note that in this example, for this 36h forecast hour, there are +c 8 entries. This is so that we can include the wind values for +c the 4 different quadrants, for both the earth relative analyses +c (NEE, SEE, SWE, NWE) and the storm-relative analyses (FRR, BRR, +c BLR, FLR). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + integer ioutwind(numdist) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real outlon,outlat + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,id,intlon100,intlat100,ir + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*2 :: crel(4) = (/'FR','BR','BL','FL'/) + + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + +c Total wind (converted to knots*10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 71, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Total wind (converted to knots*10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 72, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 81, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 82, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 92, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a10,a2,a1,14(', ',i4) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(72) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_ike (outlon,outlat,xsfclon,xsfclat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,ike,sdp,wdp,maxstorm + & ,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the Integrated Kinetic Energy (IKE) and Storm Surge Damage +c Potential (SDP), based on Powell (BAMS, 2007). At this time, we +c are only computing the IKE values for TS threshold (17.5 m/s) and +c above. We are not yet computing wind damage potential (WDP) +c since, per Mark Powell (4/2008), he is currently re-formulating +c an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with WDP, SDP and IKE values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, 340, 560, 212, 174, 42, 93, 12, 0 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, WDP, SDP, I10, ITS, IH ,I2540,I4154, I55 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Values for WDP and SDP are multiplied by 10 in this routine +c before being written out. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c ike integrated kinetic energy, in units of TJ +c sdp storm surge damage potential +c wdp wind damage potential +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,sdp,wdp + real ike(max_ike_cats) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,intlon100,intlat100,maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (74,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, IKE',int((wdp*10)+0.5),int((sdp*10)+0.5) + & ,int(ike(1)+0.5),int(ike(2)+0.5),int(ike(3)+0.5) + & ,int(ike(4)+0.5),int(ike(5)+0.5),int(ike(6)+0.5) + & ,intlat100,clatns,intlon100,clonew +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a14,8(',',i5) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(74) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_phase (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,paramb,vtl_slope + & ,vtu_slope,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the three parameters that comprise Bob Hart's cyclone phase +c space (CPS). These parameters are his "parameter B", which +c assesses the left-right thermal asymmetry, and the upper +c troposphere (300-600 mb) and lower troposphere (900-600 mb) +c thermal wind values. +c +c LOCAL: +c +c Arrays: +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with paramb, vtl_slope and vtu_slope values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, 340, 560, 212 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, B, VTL, VTU +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c paramb thermal asymmetry +c vtl_slope thermal wind value for lower troposphere (900-600 mb) +c vtu_slope thermal wind value for upper troposphere (600-300 mb) +c +c OUTPUT: +c ioiret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + real outlon,outlat,paramb,vtl_slope,vtu_slope + real vmaxwind,conv_ms_knots,xminmslp + integer intlon,intlat,output_fhr + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (71,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 95, CPS',int(paramb+0.5),int(vtl_slope+0.5) + & ,int(vtu_slope+0.5) +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a14,3(',',i6)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(71) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf_gen (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different. Here's an example of the +c TPC standard atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c be the one that is pulled from the tcvitals or gen_vitals +c record: +c +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 50, NEQ, 155, 000, 000, 000 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 64, NEQ, 000, 000, 000, 000 +c +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd storm translation speed +c istmdir direction of storm movement +c plastbar pressure of last closed isobar +c rlastbar radius of last closed isobar +c rmax radius of max winds +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real outlon,outlat,plastbar,rlastbar,rmax + real vmaxwind,conv_ms_knots,xminmslp + real cps_vals(3) + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar,irmax + integer ivtl,ivtu,iparamb,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + + if ( verb .ge. 3) then + print *,'+++ Top of output_atcf_gen, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + + if (xminmslp == 999999.0) xminmslp = 0.0 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/100. + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (rmax > -90.0) then + irmax = int(rmax + 0.5) + else + irmax = -99 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(66) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf_sink (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta,igridzeta + & ,cps_vals,plastbar,rlastbar,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The "sink" in the subroutine name indicates that this output +c contains the whole kitchen sink of forecast storm info. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different, and the part after the radii +c data is different. Here's an example of the TPC standard +c atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c indicate the lat/lon at which the storm was *first* found in +c the model. The position may be either found within this run +c of the tracker, or that position may have been pulled from the +c tcvitals or gen_vitals record: +c +c 2000092500_F000_206N_0623W_13L, 2000092500, 03, GFSO, 036 +c , 243N, 675W, 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c , PLAS, RLAS, RMX, DIR, SPD, B, VTU, VTL +c , Z8MN, Z8MX, Z7MN, Z7MX +c +c As noted above, there is extra info at the end, after the +c "34, NEQ, 242, 163, 124, 208" radii info. Here is a key +c to indicate what these items are: +c +c PLAS: Pressure (mb) of last closed isobar +c RLAS: Radius of the last closed isobar in nm, 0 - 9999 nm. +c RMX: Radius of max winds, 0 - 999 nm. +c DIR: Direction of storm motion. +c SPD: Speed of storm motion (m/s * 10). +c B: Hart's CPS "Parameter B" thickness asymmetry value (m). +c VTL: Hart's CPS thermal wind (Lower, 900-600) value. +c VTU: Hart's CPS thermal wind (Upper, 600-300) value. +c Z8MN: Mean value of 850 mb zeta surrounding storm. +c Z8MX: Max value of 850 mb zeta near storm. +c Z7MN: Mean value of 700 mb zeta surrounding storm. +c Z7MX: Max value of 700 mb zeta near storm. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd speed of storm translation +c istmdir direction of storm motion +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c plastbar pressure of last closed isobar (pa) +c rlastbar radius of last closed isobar (nm) +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real cps_vals(3) + real outlon,outlat + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar + integer iparamb,ivtl,ivtu,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1 + + if ( verb .ge. 3 ) then + print *,'+++ Top of output_atcf_sink, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/100. + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4)) + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',2(i4,', '),4(i3,', '),2(i5,', '),4(i4,', '),a9) + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',2(i4,', '),i3,', ',2(i4,', '),3(i6,', '),4(i4,', ') + & ,a9) + +c write (68,87) gstm%gv_gen_date,gstm%gv_gen_lat +c & ,gstm%gv_gen_latns,gstm%gv_gen_lon +c & ,gstm%gv_gen_lonew,gstm%gv_gen_type +c & ,inp%bcc,inp%byy,inp%bmm,inp%bdd,inp%bhh +c & ,adjustr(atcfname),ifcsthour,intlat,clatns,intlon,clonew +c & ,int((vmaxwind*conv_ms_knots) + 0.5) +c & ,int(xminmslp/100.0 + 0.5) +c & ,'XX, 34, NEQ' +c & ,istmspd,istmdir,imeanzeta(1),igridzeta(1) +c & ,imeanzeta(2),igridzeta(2) +c +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,6(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(68) + + return + end + +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_tcvitals (xlon,xlat,inp,ist,iovret) +c +c ABSTRACT: This subroutine outputs a tcvitals record. The +c lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE inparms; USE set_max_parms + USE verbose_output + + type (tcvcard) stm + type (datecard) inp + real xlon,xlat +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "storm" +c components for this storm, then we will change the specific +c components that we need to. + + stm = storm(ist) + + stm%tcv_center = 'AEAR' + + stm%tcv_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + stm%tcv_latns = 'S' + else + stm%tcv_latns = 'N' + endif + + if (xlon >= 180.) then + stm%tcv_lon = 3600 - int(xlon * 10. + 0.5) + stm%tcv_lonew = 'W' + else + stm%tcv_lon = int(xlon * 10. + 0.5) + stm%tcv_lonew = 'E' + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) stm + endif + + write (65,21) stm + + 21 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + +c +c bug fix for IBM: flush the output stream so it actually writes + flush(65) + + return + end + +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_gen_vitals (xlon,xlat,inp,ist,istmspd,istmdir + & ,iovret) +c +c ABSTRACT: This subroutine outputs a modified vitals record. +c The lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c The storm identifier is different than that for a standard +c tcvitals. The storm identifier contains the date/time that +c the storm was first identified, and the lat/lon position at +c which it was first identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE gen_vitals; USE inparms; USE set_max_parms + USE verbose_output + + implicit none + + type (gencard) gstm + type (datecard) inp + real xlon,xlat + integer ist,iovret,istmspd,istmdir +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the vitals record. + + if (gstm%gv_gen_date /= 99999) then + + if (gstm%gv_gen_type /= 'FOF') then + ! If this is not a 'FOF' storm (found on the fly storm), then + ! it must be a TC vitals storm, or a tropical cyclone, and we + ! don't want to create a vitals record for a tropical cyclone, + ! since we will rely on reading them from the TC Vitals + ! database instead. + return + endif + + else + + ! This storm is new in this forecast/analysis and was found on + ! the fly in the first time level for this run and there was no + ! previous vitals record for this system + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = 0 + + gstm%gv_gen_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_gen_latns = 'S' + else + gstm%gv_gen_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_gen_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'W' + else + gstm%gv_gen_lon = int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'E' + endif + + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + gstm%gv_obs_ymd = inp%bcc * 1000000 + & + inp%byy * 10000 + & + inp%bmm * 100 + & + inp%bdd + + gstm%gv_obs_hhmm = inp%bhh * 100 + + gstm%gv_obs_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_obs_latns = 'S' + else + gstm%gv_obs_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_obs_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'W' + else + gstm%gv_obs_lon = int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'E' + endif + + gstm%gv_stdir = istmdir + gstm%gv_stspd = istmspd + + gstm%gv_depth = 'U' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) gstm + endif + + write (67,21) gstm + + 21 format (i10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1,'_',a3,1x,i8,1x + & ,i4.4,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x + & ,i3,4(1x,i4),1x,a1) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(67) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- +c subroutine output_tracker_mask (masked_outc,kpds,kgds,lb,ifh +c & ,imax,jmax,iotmret) +c +c ABSTRACT: This subroutine outputs a GRIB record that contains the +c "mask" used to mask out areas surrounding low pressure centers +c that have been found during the search at each forecast hour. This +c mask is written out purely for diagnostic purposes. The GRIB +c identifier given to the mask in the pds is 850 mb height (you can +c make it anything you want). This is only done for the "midlat" +c and "tcgen" cases, since the runs for those cases use a mask while +c the regular "tracker" run (that is, the run which strictly tracks +c only those storms in the TC vitals file) does not. +c +c INPUT: +c masked_outc logical array containing mask +c kpds GRIB pds array +c kgds GRIB gds array +c ifh integer counter index for current forecast hour +c imax num points is i-direction of input grid +c jmax num points is j-direction of input grid +c +c OUTPUT: +c iotmret return code from this subroutine + +c implicit none +cc +c integer ifh,imax,jmax,iotmret,kf,igoret,iix,jjx,ipret +c integer kpds(200),kgds(200) +c logical(1) masked_outc(imax,jmax),lb(imax,jmax) +c real xmask(imax,jmax) +cc +c if (ifh == 1) then +c call baopenw (77,"fort.77",igoret) +c print *,'baopenw: igoret= ',igoret +c +c if (igoret /= 0) then +c print *,' ' +c print *,'!!! ERROR in sub output_tracker_mask opening' +c print *,'!!! **OUTPUT** grib files. baopenw return codes:' +c print *,'!!! grib file 1 return code = igoret = ',igoret +c STOP 95 +c return +c endif +c endif +c +c xmask = 0.0 +c do jjx = 1,jmax +c do iix = 1,imax +c if (masked_outc(iix,jjx)) then +c xmask(iix,jjx) = 1.0 +c else +c xmask(iix,jjx) = 0.0 +c endif +c enddo +c enddo +c +c kf = imax * jmax +c +cc kpds(1) = 7 ; kpds(2) = 80 +cc kpds(3) = 2 ; kpds(4) = 192 +c +c kpds(5) = 7 +c kpds(6) = 100 +c kpds(7) = 850 +c kpds(22) = 0 +c +ccc kpds(7) = 850 ; kpds(8) = 02 +ccc kpds(9) = 11 ; kpds(10) = 4 +ccc kpds(11) = 0 ; kpds(12) = 0 +ccc kpds(13) = 1 ; kpds(14) = ifcsthour +ccc kpds(15) = 0 ; kpds(16) = 10 +ccc kpds(17) = 0 ; kpds(18) = 1 +ccc kpds(19) = 2 ; kpds(20) = 0 +ccc kpds(21) = 21 ; kpds(22) = 0 +ccc kpds(23) = 0 ; kpds(24) = 0 +ccc kpds(25) = 0 +ccc kgds(1) = 0 ; kgds(2) = imax +ccc kgds(3) = jmax ; kgds(4) = 90000 +ccc kgds(5) = 0 ; kgds(6) = 128 +ccc kgds(7) = -90000 ; kgds(8) = -1000 +ccc kgds(9) = 1000 ; kgds(10) = 1000 +ccc kgds(11) = 0 ; kgds(12) = 0 +ccc kgds(13) = 0 ; kgds(14) = 0 +ccc kgds(15) = 0 ; kgds(16) = 0 +ccc kgds(17) = 0 ; kgds(18) = 0 +ccc kgds(19) = 0 ; kgds(20) = 255 +cc +cc write(*,980) kpds(1),kpds(2) +cc write(*,981) kpds(3),kpds(4) +cc write(*,982) kpds(5),kpds(6) +cc write(*,983) kpds(7),kpds(8) +cc write(*,984) kpds(9),kpds(10) +cc write(*,985) kpds(11),kpds(12) +cc write(*,986) kpds(13),kpds(14) +cc write(*,987) kpds(15),kpds(16) +cc write(*,988) kpds(17),kpds(18) +cc write(*,989) kpds(19),kpds(20) +cc write(*,990) kpds(21),kpds(22) +cc write(*,991) kpds(23),kpds(24) +cc write(*,992) kpds(25) +cc write(*,880) kgds(1),kgds(2) +cc write(*,881) kgds(3),kgds(4) +cc write(*,882) kgds(5),kgds(6) +cc write(*,883) kgds(7),kgds(8) +cc write(*,884) kgds(9),kgds(10) +cc write(*,885) kgds(11),kgds(12) +cc write(*,886) kgds(13),kgds(14) +cc write(*,887) kgds(15),kgds(16) +cc write(*,888) kgds(17),kgds(18) +cc write(*,889) kgds(19),kgds(20) +cc write(*,890) kgds(21),kgds(22) +ccc +cc 980 format(' kpds(1) = ',i7,' kpds(2) = ',i7) +cc 981 format(' kpds(3) = ',i7,' kpds(4) = ',i7) +ccc 982 format(' kpds(5) = ',i7,' kpds(6) = ',i7) +cc 983 format(' kpds(7) = ',i7,' kpds(8) = ',i7) +cc 984 format(' kpds(9) = ',i7,' kpds(10) = ',i7) +cc 985 format(' kpds(11) = ',i7,' kpds(12) = ',i7) +cc 986 format(' kpds(13) = ',i7,' kpds(14) = ',i7) +cc 987 format(' kpds(15) = ',i7,' kpds(16) = ',i7) +cc 988 format(' kpds(17) = ',i7,' kpds(18) = ',i7) +cc 989 format(' kpds(19) = ',i7,' kpds(20) = ',i7) +cc 990 format(' kpds(21) = ',i7,' kpds(22) = ',i7) +cc 991 format(' kpds(23) = ',i7,' kpds(24) = ',i7) +cc 992 format(' kpds(25) = ',i7) +cc 880 format(' kgds(1) = ',i7,' kgds(2) = ',i7) +cc 881 format(' kgds(3) = ',i7,' kgds(4) = ',i7) +cc 882 format(' kgds(5) = ',i7,' kgds(6) = ',i7) +cc 883 format(' kgds(7) = ',i7,' kgds(8) = ',i7) +cc 884 format(' kgds(9) = ',i7,' kgds(10) = ',i7) +cc 885 format(' kgds(11) = ',i7,' kgds(12) = ',i7) +cc 886 format(' kgds(13) = ',i7,' kgds(14) = ',i7) +cc 887 format(' kgds(15) = ',i7,' kgds(16) = ',i7) +cc 888 format(' kgds(17) = ',i7,' kgds(18) = ',i7) +cc 889 format(' kgds(19) = ',i7,' kgds(20) = ',i7) +cc 890 format(' kgds(20) = ',i7,' kgds(22) = ',i7) +c +c print *,'just before call to putgb, kf= ',kf +c call putgb (77,kf,kpds,kgds,lb,xmask,ipret) +c print *,'just after call to putgb, kf= ',kf +c if (ipret == 0) then +c print *,' ' +c print *,'+++ IPRET = 0 after call to putgb' +c print *,' ' +c else +c print *,' ' +c print *,'!!!!!! ERROR: IPRET NE 0 AFTER CALL TO PUTGB !!!' +c print *,' ' +c endif +cc +cc bug fix for IBM: flush the output stream so it actually writes +c flush(6) +c +c return +c end +c +cc +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_next_ges (fixlon,fixlat,ist,ifh,imax,jmax + & ,dx,dy,modelid,valid_pt,readflag,maxstorm,istmspd + & ,istmdir,ctype,trkrinfo,ignret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. It does this by using two different +c methods and averaging the results from those two. The +c first method is a simple linear extrapolation made by +c basically drawing a line from the previous position +c through the current fix position and assuming straight +c line motion. The second method is to do a barnes +c smoothing of u & v in the vicinity of the storm at 850, +c 700 & 500 mb to get an average environmental wind +c vector at each level, and then move the storm according +c to the vector at each level. Then a weighted average is +c taken of all these positions from methods 1 & 2 to get +c the consensus for the guess position. NOTE: For a +c regional model and a storm that is relatively close to +c the model boundary, there is a strong possibility that +c the barnes analysis subroutine will fail due to trying +c to access grid points beyond the model's lateral +c boundary. In this case, the redlm & ridlm are halved +c and barnes is called again. If it still fails, then +c just use the result from method 1 as a default. +c +c INPUT: +c fixlon Array with longitudes of fix positions +c fixlat Array with latitudes of fix positions +c ist Storm number currently being processed +c ifh Forecast hour currently being processed +c imax Max number of pts in x-direction for this grid +c jmax Max number of pts in y-direction for this grid +c dx grid-spacing of the model in the i-direction +c dy grid-spacing of the model in the j-direction +c modelid Integer indicating what model's data is being processed +c valid_pt Logical; bitmap indicating if valid data at that pt. +c readflag Logical; Tells whether or not a variable was read in +c for this model +c maxstorm Max # of storms that can be handled in this run +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, eventually +c in the barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c istmspd The speed that the storm would have to move to get from +c the current position to the next guess position +c istmdir The direction in which the storm would have to move to +c get from the current position to the next guess position +c +c LOCAL: +c dt Number of seconds between successive forecast times +c for this particular model. +c dtkm Distance in meters of 1 degree latitude +c icutmax Max number of times to cut the ridlm and redlm in half, +c for use in calling barnes. If you're using a regional +c model and on the first call to barnes you try to access +c a point that's outside the model grid boundary, we'll +c call barnes again, but not before cutting the redlm and +c ridlm in half. icutmax says how many times to allow +c this cutting in half before giving up and just going +c with the extrapolation method. At first writing, we'll +c set icutmax to 2, so that it will allow the ridlm to +c get down to 500 km (originally 2000 km) and the redlm +c to 125 km (originally 500 km). +c *** NOTE: After testing the system, it was found that if +c we cut these radii, the u and v values that are +c calculated from barnes are too strongly influenced by +c the near-storm environment and, especially for asymmetric +c systems, resulted in u and v values being much too strong. +c As such, we will not allow these values to be cut, and if +c we hit the boundaries in barnes, we'll just use the +c extrapolation method, which has seemed to work just fine. +c +c OTHER: (slonfg, slatfg & storm defined in module def_vitals) +c slonfg Array containing first guess longitude positions +c slatfg Array containing first guess latitude positions +c storm Contains tcvitals information +c + USE radii; USE def_vitals; USE set_max_parms; USE grid_bounds + USE tracked_parms; USE level_parms; USE trig_vals; USE trkrparms + USE gen_vitals + USE verbose_output + + type (trackstuff) trkrinfo + integer icutmax,istmspd,istmdir,bskip + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + character*1 :: in_grid, extrap_flag, barnes_flag + character(*) ctype + logical(1) valid_pt(imax,jmax),readflag(14) +c + in_grid = 'n' + extrap_flag = 'y' +c +c For updating the first guess, if Method 1 and Method 2 are both +c able to be done, give the following weights to the 2 methods. +c + data barneswt /0.50/, extrapwt /0.50/ +c +c ------------------------------- +c METHOD 1: LINEAR EXTRAPOLATION +c ------------------------------- +c First, just do a simple linear extrapolation from the previous +c fix position through the current fix position. If it's the +c first time (vt=0), then use the storm motion vector and storm +c speed information from the TC Vitals card. +c + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(ist)%tcv_stdir == -99 .or. + & storm(ist)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GET_NEXT_GES, at fcst hour = 0, either ' + print *,'!!! storm motion or storm speed = -99 on TCV card.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(ist)%tcv_stdir + print *,'!!! storm motion speed= ',storm(ist)%tcv_stspd + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + extrap_flag = 'n' + else + ucomp = sin(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + vcomp = cos(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(ist,ifh) + ydeg + avglat = 0.5 * (extraplat + fixlat(ist,ifh)) + if (avglat > 89.5) avglat = 89.0 + if (avglat < -89.5) avglat = -89.0 + cosfac = cos(avglat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(ist,ifh) + xdeg + endif + else + +c Do a simple linear extrapolation of the current motion of the +c storm. Follow a line from the fix position from the last fix +c through the current fix and extrapolate out. To figure out the +c new latitude, just see how many deg lat the storm moved since +c last time and add it to the current fix latitude. To calculate +c the new fix longitude, though, we need to see how many deg lon +c the storm moved since the last time, convert that to the +c distance (km) the storm travelled in the x-direction (at an +c average latitude between the current and previous latitudes), +c and then add that distance on to the current longitude and +c convert that distance to the num of degrees the storm has +c travelled in the x-direction (at an average latitude between +c the current and next(extrap) latitudes). +c +c UPDATE Feb 2009: To account for the possibility of using +c irregularly spaced forecast hours (e.g., 6,10,10.5,...etc), +c I had to modify this linear extrapolation. + + + if (fixlat(ist,ifh-1) > -900.0 .and. + & fixlon(ist,ifh-1) > -900.0) then + + ylatdegmove_last = fixlat(ist,ifh) - fixlat(ist,ifh-1) + xlondegmove_last = fixlon(ist,ifh) - fixlon(ist,ifh-1) + + xnumh_last = fhreal(ifh) - fhreal(ifh-1) + + ylatdegmove_last_perhour = ylatdegmove_last / xnumh_last + xlondegmove_last_perhour = xlondegmove_last / xnumh_last + + xnumh_next = fhreal(ifh+1) - fhreal(ifh) + + extraplat = fixlat(ist,ifh) + & + (ylatdegmove_last_perhour * xnumh_next) + + yoldavglat = 0.5 * (fixlat(ist,ifh) + fixlat(ist,ifh-1)) + yoldcosfac = cos (dtr * yoldavglat) + xdistmove_last = xlondegmove_last * dtk * yoldcosfac + + xdistmove_last_perhour = xdistmove_last / xnumh_last + + ynewavglat = 0.5 * (extraplat + fixlat(ist,ifh)) + ynewcosfac = cos(dtr * ynewavglat) + xdegnew = (xdistmove_last_perhour * xnumh_next) + & / (dtk * ynewcosfac) + extraplon = fixlon(ist,ifh) + xdegnew + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GET_NEXT_GES, at fcst hour = ',ifcsthr + print *,'!!! the lon and lat positions for the previous' + print *,'!!! forecast hour are -999, meaning that this is a' + print *,'!!! new storm, so we cannot use the extrap method.' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + extrap_flag = 'n' + + endif + + endif + +c ------------------------------- +c METHOD 2: Barnes analysis +c ------------------------------- +c Do a barnes analysis on the u & v components of the wind near the +c storm to get an average u & v, then advect the storm according to +c the average wind vector obtained. The call to get_ij_bounds is +c needed in order to restrict the number of grid points that are +c searched in the barnes subroutine. See Abstract from this +c subroutine for further details. + + npts = ceiling(ridlm/(dtk*((dx+dy)/2))) + + call get_ij_bounds (npts,0,ridlm,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for ' + print *,'!!! storm number ',ist + endif + + ignret = 92 + return + endif + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if ((dx+dy)/2 > 0.20) then + bskip = 1 + else if ((dx+dy)/2 > 0.10 .and. (dx+dy)/2 <= 0.20) then + bskip = 2 + else if ((dx+dy)/2 > 0.05 .and. (dx+dy)/2 <= 0.10) then + bskip = 3 + else if ((dx+dy)/2 > 0.03 .and. (dx+dy)/2 <= 0.05) then + bskip = 5 + else if ((dx+dy)/2 <= 0.03) then + bskip = 10 + endif + +c Calculate average wind at each level (currently: 850, 700 & 500) + + re = redlm + ri = ridlm + icut = 0 + + if (trkrinfo%type == 'midlat') then + icutmax = 2 + else + icutmax = 1 + endif + + radmaxloop: do while (icut <= icutmax .and. in_grid == 'n') + + ubar = 0.0; vbar = 0.0 + iuret = 0; ivret = 0 + wgttot = 0.0 + ibarnct = 0 + barnes_flag = 'n' + + levelloop: do n=1,nlevg + + select case (n) + case (1); ix1=3; ix2=4 ! For 850 mb readflags + case (2); ix1=5; ix2=6 ! For 700 mb readflags + case (3); ix1=12; ix2=13 ! For 500 mb readflags + end select + + if (readflag(ix1) .and. readflag(ix2)) then + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,u(1,1,n),valid_pt + & ,bskip,re,ri,uavg,icount,ctype,trkrinfo,iuret) + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,v(1,1,n),valid_pt + & ,bskip,re,ri,vavg,icount,ctype,trkrinfo,ivret) + + if (iuret /= 0 .or. ivret /= 0) then + +c ...barnes probably tried to access a pt outside the grid +c domain. So, reduce by half the distance from the center +c of the farthest pt that barnes tries to access, exit this +c loop, and try it again with the smaller re and ri. + + iuret = 96; ivret = 96 + reold = re + riold = ri + re = 0.5 * re + ri = 0.5 * ri + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: While attempting to use the barnes ' + print *,'method to update the first guess, the ' + print *,'algorithm tried to access a grid point that ' + print *,'does not have valid data, meaning that too ' + print *,'large a radius is being searched. So, the 2 ' + print *,'radii, re and ri, are being halved and, if the' + print *,'value of icutmax > 0, the algorithm will be ' + print *,'run again. Otherwise, if icutmax = 0, only ' + print *,'the extrapolation method will be used.' + print *,'iuret= ',iuret,' ivret= ',ivret,' icut= ',icut + print *,'Old re = ',reold,' New re = ',re + print *,'Old ri = ',riold,' New ri = ',ri + endif + + exit levelloop + + else + ubar = ubar + wgts(n) * uavg + vbar = vbar + wgts(n) * vavg + wgttot = wgttot + wgts(n) + ibarnct = ibarnct + 1 + endif + + endif + + enddo levelloop + + if (ibarnct > 0 .and. wgttot > 0.0) then + barnes_flag = 'y' + in_grid = 'y' + ubar = ubar / wgttot + vbar = vbar / wgttot + barnlat = fixlat(ist,ifh) + (vbar * dt)/dtkm + cosfac = cos (dtr * 0.5 * (fixlat(ist,ifh) + barnlat)) + barnlon = fixlon(ist,ifh) + (ubar * dt)/(dtkm * cosfac) + +c This next if statement says that if we've had to reduce the +c size of the barnes analysis domain twice already, then we've +c only done the analysis on a much smaller area, and this +c doesn't give us as good a picture of the average winds in the +c area of the storm, so reduce the emphasis we place on the +c barnes method. + + if (icut >= 2) barneswt = barneswt / 2. + + else + barnes_flag = 'n' + endif + + icut = icut + 1 + + enddo radmaxloop + +c --------------------- +c Average the results +c --------------------- +c Now do a weighted average of the positions obtained from the +c linear extrapolation and the barnes analysis methods. + + if (extrap_flag == 'y' .and. barnes_flag == 'y') then + wt_total = barneswt + extrapwt + slatfg(ist,ifh+1) = (barneswt * barnlat + extrapwt * extraplat) + & / wt_total + + ! Note that in any of these statements just below, in order for + ! any of these to be > 360, the original fixlon must be close + ! to 360, i.e., in the far eastern part of the grid, as opposed + ! to being in the far western part (e.g., 0-2 deg East or so). + ! Conversely, for any of these to be < 0, the original fixlon + ! must be close to 0, i.e., in the far *western* part of the + ! grid. + + if (fixlon(ist,ifh) > 330.0) then + + if (extraplon > 360. .or. barnlon > 360.) then + + if (trkrinfo%gridtype == 'global') then + + continue ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon > 360 or' + print *,'!!! barnlon > 360 for a non-global grid. We ' + print *,'!!! only do GM wrapping for global grids.' + print *,'!!! extraplon= ',extraplon,' barnlon= ',barnlon + endif + + ignret = 95 + return + + endif + + endif + + elseif (fixlon(ist,ifh) < 30.0) then + + if (extraplon < 0. .or. barnlon < 0.) then + + if (trkrinfo%gridtype == 'global') then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon < 0 or ' + print *,'!!! barnlon < 0 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + + endif + + endif + + else + + continue ! extraplon and barnlon do not need to be modified + ! since there should be no way that a storm + ! currently east of 30E and west of 30W could make + ! it to the Greenwich Mer in one forecast interval + + endif + + slonfg(ist,ifh+1) = (barneswt * barnlon + extrapwt * extraplon) + & / wt_total + + if (slonfg(ist,ifh+1) > 360.) then + ! If we've GM-wrapped past 360, adjust it to be 0-360... + slonfg(ist,ifh+1) = slonfg(ist,ifh+1) - 360. + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 360.-barnlon,barnlat + write (6,43) 360.-extraplon,extraplat + endif + + ignret = 0 + else if (extrap_flag == 'y' .and. barnes_flag == 'n') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_next_ges, barnes method was not ' + print *,'!!! done for updating the first guess for this ' + print *,'!!! storm. Only the linear extrapolation method ' + print *,'!!! was used.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + slatfg(ist,ifh+1) = extraplat + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 0.0,0.0 + write (6,43) 360.-extraplon,extraplat + endif + + ignret = 0 + else if (extrap_flag == 'n' .and. barnes_flag == 'y') then + slatfg(ist,ifh+1) = barnlat + if (barnlon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (barnlon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = barnlon + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 360.-barnlon,barnlat + write (6,43) 0.0,0.0 + endif + + ignret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges, new position guess not' + print *,'!!! made. Could not get guess using either barnes' + print *,'!!! method or extrapolation method.' + print *,'!!! extrap_flag = ',extrap_flag + print *,'!!! barnes_flag = ',barnes_flag + print *,'!!! Storm number = ',ist,' ifh = ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + write (6,41) 0.0,0.0 + write (6,43) 0.0,0.0 + endif + + ignret = 95 + endif + + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| Current fix & updated fix positions |' + print *,'-------------------------------------------------- ' + print *,'| In get_next_ges, current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',ist + print *,'| Return code from get_next_ges = ',ignret + print *,'| Storm Name = ',storm(ist)%tcv_storm_name + print *,'| Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + write (6,21) fixlat(ist,ifh) + write (6,23) 360.-fixlon(ist,ifh),fixlon(ist,ifh) + write (6,25) slatfg(ist,ifh+1) + write (6,27) 360.-slonfg(ist,ifh+1),slonfg(ist,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current fix lat is ',f7.2) + 23 format (' | Current fix lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') + 41 format (' --- barnlon= ',f7.2,'W barnlat= ',f7.2) + 43 format (' --- extraplon= ',f7.2,'W extraplat= ',f7.2) + +c Now calculate the speed that the storm would have to move at in +c order to make it to the next forecast position. We will use +c this information in writing out the "gen_vitals" record, if this +c is requested. + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh) + & ,slonfg(ist,ifh+1),slatfg(ist,ifh+1),dist,degrees) + + ! convert distance from km to meters, then get speed in m/s. + + distm = dist * 1000. + stmspd = distm / dt + istmspd = int ((stmspd * 10) + 0.5) + + xincr = slonfg(ist,ifh+1) - fixlon(ist,ifh) + yincr = slatfg(ist,ifh+1) - fixlat(ist,ifh) + + if ( verb .ge. 3 ) then + print *,'iocheck, dist= ',dist,' distm= ',distm + print *,'iocheck, stmspd= ',stmspd,' istmspd= ',istmspd + print *,'iocheck, xincr= ',xincr,' yincr= ',yincr + endif + + if (xincr < 0.0 .and. slonfg(ist,ifh+1) < 30.0 .and. + & fixlon(ist,ifh) > 300.0) then + ! This means we have a storm moving east across the GM, and + ! so we are subtracting, for example, something like + ! 0.5 - 359.5, so redo xincr, but add 360 to slonfg first... + xincr = (slonfg(ist,ifh+1) + 360.0) - fixlon(ist,ifh) + else if (xincr > 300.0) then + ! This means we have a storm moving west across the GM, and + ! so we are subtracting, for example, something like + ! 359.5 - 0.5, so redo xincr, but add 360 to fixlon first... + xincr = slonfg(ist,ifh+1) - (fixlon(ist,ifh) + 360.0) + endif + + if (xincr == 0.0) then + if (yincr == 0.0) then + stmdir = 0.0 + else if (yincr > 0) then + stmdir = 360.0 + else if (yincr < 0) then + stmdir = 180.0 + endif + else if (xincr > 0.0) then + if (yincr == 0.0) then + stmdir = 90.0 + else + arct = atan(yincr/xincr) + stmdir = 90. - arct / dtr + endif + else if (xincr < 0.0) then + if (yincr == 0.0) then + stmdir = 270.0 + else + arct = atan(yincr/xincr) + stmdir = 270. - arct / dtr + endif + endif + + istmdir = int (stmdir + 0.5) + if (istmdir > 360) then + istmdir = 360 + else if (istmdir < 0) then + istmdir = 0 + endif + + if ( verb .ge. 3 ) then + print *,'iocheck, stmdir= ',stmdir,' istmdir= ',istmdir + endif + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getradii (xcenlon,xcenlat,imax,jmax,dx,dy,valid_pt + & ,cstormid,ifcsthr,vradius,trkrinfo + & ,need_to_expand_r34,radmax,igrret) +c +c ABSTRACT: This subroutine looks through the wind data near an +c input storm center (fixlon,fixlat) and gets the radii of various +c surface winds in each of the 4 storm quadrants (NE,NW,SE,SW). +c The wind thresholds that are sought are gale force (34kt|17.5m/s), +c storm force (50kt|25.7m/s), and hurricane force (64kt|32.9m/s). +c This subroutine calls the Cray subroutine orders, which is a +c Cray-optimized sort routine. +c +c UPDATE (AUG 2001): The Cray subroutine orders was ported to the +c SP by NCEP personnel. On the SP version, some changes were +c apparently made so that the size of the arrays for calling +c arguments 2, 3 and 4 (iwork, dtemp and isortix in my calling +c routine) must be the same. This was not the case on the Crays, +c and this was causing the tracker to crash for cases far north +c on fine grids (GFDL 1/3 grid). +c +c UPDATE (AUG 2012): The call to the Cray subroutine orders was +c replaced with a call to qsort, which uses a quicksort sorting +c algorithm. While this is not the fastest sorting routine out +c there, we don't do a lot of sorting here, and qsort is simple +c and it is portable. +c +c UPDATE (April 2013): For the radii, we encountered a problem with +c radmax being too small. It was set at 650 km. Hurricane Sandy +c exceeded this in the models, so the values returned from getradii +c were close to the default radmax value of 650 km (350 nm), instead +c of much higher as they should have been. To fix it, we now use an +c iterative technique, where we start with radmax as a small value +c (450 km). If getradii returns a value for R34 in a quadrant that +c does not exceed 0.97*radmax, then that value is ok. If it does +c exceed 0.97*radmax, then we bump up radmax by 50 km and call +c getradii again, looking to diagnose radii only in those quadrants +c where the need_to_expand_r34 flag = 'n'. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c cstormid 3-character storm ATCF ID (e.g., 03L, 11E, etc) +c ifcsthr integer value for current forecast hour +c trkrinfo derived type containing various info on the storm +c need_to_expand_r34 1-character array that specifies which of the +c 4 quadrants still need to be expanded on this time +c through getradii in order to get an R34 value that is +c not right at the outermost boundary. +c radmax input max radius (km) that will be used for this +c iteration of getradii. +c +c OUTPUT: +c +c igrret return code from this subroutine +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c +c LOCAL: +c +c radmax the maximum radius to look for winds for the various +c thresholds. +c quadinfo This array contains the magnitude of the near-surface +c winds and the distance from the gridpoint to the fix +c position for each point in each quadrant that is within +c the maximum allowed radius, radmax. quadinfo is +c allocated within this subroutine, and is allocated as +c (quadrant, num_pts_in_quadrant, data_type), where +c data_type is either windspeed(1) or distance(2) from +c storm center to grid point. +c quadmax This array contains the max surface wind in each +c quadrant, plus the location of it and the distance from +c the storm center. This information is critical to +c identifying when this subroutine is malfunctioning. + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE level_parms + USE trkrparms + USE verbose_output + +c + type (trackstuff) trkrinfo +c + logical(1) valid_pt(imax,jmax) +c dimension iwork(257) + real, allocatable :: quadinfo(:,:,:),iwork(:) + real quadmax(4,4) + real exactdistnm,exactdistkm,radmax,degrees,cosarg + real rlonb,rlonc,rlatb,rlatc + real pt_heading_rad,pt_heading,d + integer, allocatable :: isortix(:) + integer iwindix,ipoint,ifcsthr + integer quadct(4),vradius(3,4) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: dtemp(:) + real :: windthresh(3) = (/17.5,25.7,32.9/) + character cstormid*3 + character :: need_to_expand_r34(4)*1 + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *************************************************** ' + print *,' AT BEGINNING OF GETRADII, input radmax= ',radmax + print *,' *************************************************** ' + print *,' ' + print *,'xcenlon= ',xcenlon,' xcenlat= ',xcenlat + print *,'imax= ',imax,' jmax= ',jmax,' dx= ',dx,' dy= ',dy + endif + + igrret = 0 + +c ----------------------------------------------------------- +c PART 1: Define the maximum radius for which you'll search +c for the wind values, and then get the beginning and ending +c i and j points for that sub-region to search. Define this +c maximum radius (radmax) in terms of km. +c ----------------------------------------------------------- + +c radmax = 650.0 ! This value is in units of km. With April 2013 +c ! update, this is now defined in calling routine + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + igrret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine getradii' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + igrret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmax is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmax/(dtk*dx))/cosfac) + numjpts = ceiling(radmax/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (ibeg < 1) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + igrret = 99 + return + endif + + endif + + if (jbeg < 1) jbeg = 1 + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in getradii calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Wind radii will not be calculated for this time.' + endif + + igrret = 99 + return + endif + + if (iend > imax) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + igrret = 99 + return + endif + + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getradii, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + +c ----------------------------------------------------------- +c PART 2: Within the area of grid points defined by jbeg, +c jend, ibeg and iend, (1) calculate all the wind speeds at +c each grid point, (2) calculate all of the distances from +c each grid point to the storm center, (3) assign each grid +c point to one of the 4 quadrants (NE,NW,SE,SW), (4) in each +c quadrant, sort the points, based on windspeed. +c ----------------------------------------------------------- + + jnum = jend - jbeg + 1 + inum = iend - ibeg + 1 +c numalloc = ((jnum * inum) / 2) + inum/2 + jnum/2 + numalloc = jnum * inum + inum/2 + jnum/2 + + if ( verb .ge. 3 ) then + print *,'in getradii, numalloc= ',numalloc,' radmax= ',radmax + endif + + allocate (quadinfo(4,numalloc,2),stat=iqa) + + if (iqa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub getradii allocating quadinfo array.' + print *,'!!! iqa = ',iqa + endif + + igrret = 94 + return + endif + + quadct = 0 + +c Calculate the distances and wind speeds at each grid point. If +c the distance is < radmax, include that wind info in the +c appropriate quadinfo array location for that quadrant. + + quadmax = 0.0 + + jloop: do j=jbeg,jend + iloop: do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point in question = ',i + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + igrret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub getradii' + print *,'!!! for a non-global grid. i= ',i + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + igrret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + if (dist > radmax) cycle iloop + + if (valid_pt(ip,j)) then + + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + +cc print *,'i= ',i,' j= ',j,' dist= ',dist,' vmag= ',vmag + + ! Calculate the angle from the center point to this point + ! and then assign this point to the appropriate quadrant bin + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-xcenlon) * dtr + rlatb = xcenlat * dtr + d = degrees * dtr + +c write (6,59) 360.-xcenlon,xcenlat,360.-glon(ip),glat +c +c write (6,61) d/dtr,rlatc/dtr,360.-(rlonc/dtr),rlatb/dtr +c & ,360.-(rlonb/dtr),sin(rlatc),sin(rlatb),cos(d) +c & ,sin(d),cos(rlatb) +c +c +c 59 format (1x,'+++ gr, xcenlon= ',f8.3,'W xcenlat= ' +c & ,f8.3,' glon= ',f8.3,'W glat= ',f8.3) +c +c 61 format (1x,'+++ gr, d rlatc rlonc rlatb rlonb= ',5f9.4 +c & ,' sin(rlatc)= ',f8.6,' sin(rlatb)= ',f8.6 +c & ,' cos(d)= ',f8.6,' sin(d)= ',f8.6 +c & ,' cos(rlatb)= ',f8.6) + + if (d == 0.0) then + + pt_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d)) / + & (sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_heading_rad = acos(cosarg) + else + pt_heading_rad = 2*pi - acos(cosarg) + endif + + pt_heading = pt_heading_rad / dtr + + endif + + if (pt_heading >= 0.0 .and. pt_heading < 90.) then + ! NE quadrant + iq = 1 + else if (pt_heading >= 90.0 .and. pt_heading < 180.) then + ! SE quadrant + iq = 2 + else if (pt_heading >= 180.0 .and. pt_heading < 270.) then + ! SW quadrant + iq = 3 + else if (pt_heading >= 270.0 .and. pt_heading <= 360.) then + ! NW quadrant + iq = 4 + endif + +c write (6,73) xcenlat,360.-xcenlon,j,i,ip,glat(j) +c & ,360.-glon(ip),pt_heading,iq + + 73 format (1x,'+++ getradii clat clon: ',f6.2,' ',f7.2,'W',3i4 + & ,' plat plon: ',f6.2,' ',f7.2,'W Dir: ',f7.2 + & ,' Quad: ',i2) + + quadct(iq) = quadct(iq) + 1 + quadinfo(iq,quadct(iq),1) = vmag + quadinfo(iq,quadct(iq),2) = dist + if (vmag > quadmax(iq,4)) then + quadmax(iq,1) = glon(ip) + quadmax(iq,2) = glat(j) + quadmax(iq,3) = dist + quadmax(iq,4) = vmag + endif + + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After loop, quadct(1)= ',quadct(1),' quadct(2)= ' + & ,quadct(2) + print *,' quadct(3)= ',quadct(3),' quadct(4)= ' + & ,quadct(4) + print *,' ' + + write (6,110) cstormid,ifcsthr,'NE',quadmax(1,1),quadmax(1,2) + & ,quadmax(1,3)*0.539638,quadmax(1,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SE',quadmax(2,1),quadmax(2,2) + & ,quadmax(2,3)*0.539638,quadmax(2,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SW',quadmax(3,1),quadmax(3,2) + & ,quadmax(3,3)*0.539638,quadmax(3,4)*1.9427 + write (6,110) cstormid,ifcsthr,'NW',quadmax(4,1),quadmax(4,2) + & ,quadmax(4,3)*0.539638,quadmax(4,4)*1.9427 + print *,' ' + + 110 format (' quadmax: ',a3,1x,i3.3,1x,a2,1x,' lon: ',f6.2,'E',1x + & ,' lat: ',f6.2,' radius: ',f7.2,' nm',2x,' vmag: ' + & ,f6.2,' kts') + endif + +c Now go through each quadrant and put the wind speed distance info +c into a temporary array (dtemp), sort that array, and then scan +c through that array to find the various thresholds. + + quadrantloop: do k=1,4 + + if (need_to_expand_r34(k) == 'y') then + print *,'---> R34 search underway for quadrant ',k + & ,' radmax= ',radmax + continue + else + print *,'+ R34 okay for quadrant ',k,'... skipping...' + cycle quadrantloop + endif + + if (allocated(isortix)) deallocate (isortix) + if (allocated(dtemp)) deallocate (dtemp) + if (allocated(iwork)) deallocate (iwork) + allocate (isortix(quadct(k)),stat=iisa) + allocate (dtemp(quadct(k)),stat=idta) + allocate (iwork(quadct(k)),stat=iwa) + if (iisa /= 0 .or. idta /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii allocating isortix or dtemp' + print *,'!!! array for quadrant= ',k,' iisa = ',iisa + print *,'!!! idta= ',idta,' iwa= ',iwa + endif + + itret = 94 + return + endif + +c ------------------- + + do m=1,quadct(k) + dtemp(m) = quadinfo(k,m,2) + enddo + + imode = 2 + isortix = 0 + + call qsort (dtemp,isortix,quadct(k)) + +ccccc call orders (imode,iwork,dtemp,isortix,quadct(k),1,8,1) +cccc call orders_4byte (imode,iwork,dtemp,isortix +cccc & ,quadct(k),1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' +c ************************************************************** +c--- mf 20100609 +c CAUSE OF SEG FAULT!!!!!!!! -- not sure still an issue if dtemp +c properly allocated +c + !print *,' dtemp(isortix(1)) = ',dtemp(isortix(1)) + print *,' dtemp(isortix(quadct(k)))= ' + & ,dtemp(isortix(quadct(k))) + print *,' isortix(1) = ',isortix(1) + print *,' isortix(quadct(k)) = ',isortix(quadct(k)) + endif + +c ! Uncomment these next lines to see a listing in the output of +c ! all wind values & distances in this quadrant less than radmax +c do iqq = 1,quadct(k) +c print *,' iqq= ',iqq,' vmag= ',quadinfo(k,isortix(iqq),1) +c & ,' dist= ',quadinfo(k,isortix(iqq),2) +c enddo + +c ------------------- + + if (quadct(k) < 2) then ! not enough members in array + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GETRADII, NOT ENOUGH MEMBERS IN ARRAY FOR' + print *,'!!! QUADRANT #',k,' .... # members = quadct(k)= ' + & ,quadct(k) + print *,'!!! SETTING ALL VRADII = 0 for quadrant = ',k + endif + + vradius(1,k) = 0 + vradius(2,k) = 0 + vradius(3,k) = 0 + cycle quadrantloop + endif + +c Within this quadrant, go through the sorted array of wind +c magnitudes and compare those wind values against the set +c wind thresholds to get the wind radii. The array has +c been sorted by distance from the storm center in order of +c closest (ipoint=1) to farthest (ipoint=quadct(k)). We +c analyze these wind values by starting at the farthest +c point and moving inward until we hit a point that has a +c wind value of at least 34-knot winds (17.5 m/s). When +c we find that point, we interpolate between that point and +c the next farthest out point to get the distance that would +c be for the exact 17.5 m/s value. We then continue searching +c through the wind values down closer to the storm center to +c see if we can find values for the 50- and 64-knot winds. + + iwindix = 1 + ipoint = quadct(k) + 1 + + threshloop: do while (iwindix <= 3 .and. ipoint > 1) + + ipoint = ipoint - 1 + + if (quadinfo(k,isortix(ipoint),1) < windthresh(iwindix)) then + cycle threshloop + else + if (ipoint == quadct(k)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In getradii, a max wind radius was' + print *,'!!! found at the maximum radius checked, so ' + print *,'!!! you may want to make sure that you are' + print *,'!!! checking at a far enough distance from ' + print *,'!!! the fix position, that is, you may want to' + print *,'!!! increase the value of radmax in subroutine' + print *,'!!! getradii. Currently, radmax (km) = ',radmax + print *,'!!! iwindix = ',iwindix,' quadrant= ',k + endif + + vradius(iwindix,k) = int( ((quadinfo(k,isortix(ipoint),2) + & * 0.5396) / 5.0) + 0.5) * 5 + else + +c Interpolate between the 2 closest distances to each wind +c threshold to get "exact" distance to that wind threshold +c radius, convert from km to nm, and then round to the +c nearest 5 nm (since TPC uses this precision). +c 7/23/98 UPDATE: Jim Gross has asked that values not be +c rounded to the nearest 5 nm, but rather only to the +c nearest 1 nm. + + exactdistkm = quadinfo(k,isortix(ipoint),2) + + & ( (quadinfo(k,isortix(ipoint),1) - windthresh(iwindix)) / + & (quadinfo(k,isortix(ipoint),1) - + & quadinfo(k,isortix(ipoint+1),1)) * + & ( (quadinfo(k,isortix(ipoint+1),2) - + & quadinfo(k,isortix(ipoint),2)) ) ) + + exactdistnm = exactdistkm * 0.5396 ! Convert km to nm + vradius(iwindix,k) = int(exactdistnm + 0.5) + +cc vradius(iwindix,k) = int( (exactdistnm / 5.0) + 0.5) * 5 + + + if ( verb .ge. 3 ) then + print *,'iwindix= ',iwindix,' exactdistnm = ' + & ,exactdistnm + print *,'vradius(iwindix,k) =',vradius(iwindix,k) + endif + + endif + +c The possibility exists, especially for coarse output +c grids, that there could be a jump over more than 1 wind- +c thresh category when going from 1 grid point to the next, so +c we need to account for this. For example, if 1 point has +c vmag = 15 m/s and the next point closer in has vmag = 28 +c m/s, then between those 2 points you have the thresholds +c for gale force AND storm force winds, so to be safe, we +c actually need to add 1 to ipoint and re-check the current +c point, if the wind value at that point is found to be +c greater than a wind threshold value (which it has if you've +c gotten to this point in threshloop). + + ipoint = ipoint + 1 + + iwindix = iwindix + 1 + + endif + + enddo threshloop + + deallocate (dtemp,stat=idta) + deallocate (isortix,stat=iisa) + deallocate (iwork,stat=iwa) + if (idta /= 0 .or. iisa /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating isortix or' + print *,'!!! dtemp or work for quadrant= ',k + print *,'!!! idta= ',idta,' iisa= ',iisa,' iwa= ',iwa + endif + + itret = 94 + return + endif + + enddo quadrantloop + + deallocate (quadinfo,stat=iqa) + if (iqa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating quadinfo array.' + print *,'!!! iqa= ',iqa + endif + + itret = 94 + return + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_max_wind (xcenlon,xcenlat,imax,jmax,dx,dy + & ,valid_pt,levsfc,vmax,trkrinfo,rmax,igmwret) +c +c ABSTRACT: This subroutine looks for the maximum near-surface wind +c near the storm center. Because different fcst Centers give us +c different parms, we will look at: 10m winds for GFS, MRF, GDAS, +c NGM and NAM; 10 m winds for NOGAPS; and surface winds for UKMET. +c ECMWF does not send us any near-surface wind parameters. By the +c way, this subroutine is only concerned with the value of the max +c wind, NOT where it's located radially with respect to the center. +c The value that's returned in vmax is the max wind speed in m/s, +c which are the units the data are stored in. However, when the +c max wind values are output in output_atcf, they will be +c converted from m/s to knots. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c levsfc integer holding the value of the array member that holds +c the near-surface winds in the u and v arrays (at orig +c writing, it's = 4). +c +c OUTPUT: +c +c vmax value of maximum near-surface wind near the storm ctr +c rmax radius of max winds +c igmwret return code from this subroutine +c +c LOCAL: +c +c radmaxwind the maximum radius to look for a max wind near the +c storm center. You have to allow this to be bigger for +c model grids with coarse resolution (ECMWF 2.5 degree). + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real radmaxwind,degrees,dx,dy,rmax + logical(1) valid_pt(imax,jmax) +c + igmwret = 0 + rmax = -99.0 + + if ((dx+dy)/2. <= 1.25) then + if ((dx+dy)/2. <= 0.25) then + radmaxwind = 300.0 + else + radmaxwind = 300.0 + endif + else + radmaxwind = 500.0 + endif + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmaxwind is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmaxwind/(dtk*dx))/cosfac) + numjpts = ceiling(radmaxwind/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_max_wind calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Value of vmax will be set to 0 for this time.' + endif + + vmax = 0.0 + igmwret = 99 + return + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + + print *,' ' + print *,'In get_max_wind, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + + vmax = 0.0 + do j=jbeg,jend + do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point = ',i + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + + if (dist > radmaxwind) cycle + + if (valid_pt(ip,j)) then + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + if (vmag > vmax) then + vmax = vmag + rmax = dist * 0.539638 ! convert from km to nm + endif + endif + + enddo + enddo + + if ( verb .ge. 3 ) then + print *,'At end of get_max_wind, vmax= ',vmax,' rmax= ',rmax + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine fixcenter (clon,clat,ist,ifh,calcparm,geslon,geslat + & ,inp,stderr,fixlon,fixlat,xvalues,maxstorm,ifret) +c +c ABSTRACT: This subroutine loops through the different parameters +c for the input storm number (ist) and calculates the +c center position of the storm by taking an average of +c the center positions obtained for those parameters. +c First we check to see which parameters are within a +c max error range (errmax), and we discard those that are +c not within that range. Of the remaining parms, we get +c a mean position, and then we re-calculate the position +c by giving more weight to those estimates that are closer +c to this mean first-guess position estimate. +c +c INPUT: +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c geslon Initial guess longitude for this storm at this fcst hour +c geslat Initial guess latitude for this storm at this fcst hour +c inp contains the input date and model number information +c xvalues The actual max or min data values for each parameter +c maxstorm max # of storms to be handled in this run +c +c INPUT/OUTPUT: +c stderr Standard deviation of the position "error" of the parms +c relative to the guess storm position. As long as the +c distance of a parm center to the guess center is <= +c errpmax, it is included in the std dev calculation. +c +c OUTPUT: +c fixlon Best approximation of storm center's longitude +c fixlat Best approximation of storm center's latitude +c ifret Return code from this subroutine +c +c LOCAL: +c storm Contains tcvitals info for the storms (def_vitals) +c trkerr_avg Sum/avg of the track errors for all parms for this +c fcst hour, regardless of whether or not the error was +c > errmax. It's used for getting the std deviation of +c the position error for this forecast time, to be used +c as part of the errmax calculation for the next fcst +c time. +c iclose Number of parameters whose position estimates are +c found to be within a distance errmax of the guess pos +c wtpos The weight given to each position estimate. It's +c based on the distance from the average position. +c errdist The "error" of the parameter center position relative +c to the storm's guess position. +c avgerr Average "error" of the parameter center positions +c relative to the storm's guess position. +c use4next Logical; If a parm center has been calculated but its +c distance from the guess position is > errmax, we don't +c use this center in calculating the new guess position, +c however we will use this position in calculating the +c standard deviation of the current time's guess +c positions, to be used in calculating the new errmax +c for the next forecast time. So in this subroutine, +c calcparm may be set to FALSE if errdist > errmax, but +c use4next will not be set to FALSE (Actually, it is +c only set to FALSE if errdist > errpmax, which is +c defined in error_parms and is roughly 600km). +c stderr_close Standard deviation of position errors for parms that +c have center estimates that are within a distance +c errmax of the guess position. +c clon_fguess These are the first-guess mean position estimates, +c clat_fguess which are the means of the position estimates that +c are within a distance errmax. These first-guess mean +c positions will be refined by giving more weight to +c individual parameter estimates that are closer to +c this first-guess mean position. +c dist_from_mean Contains the "error" distance of each parameter +c from the first-guess mean position (clon_fguess, +c clat_fguess). NOTE: If a parameter is not within +c a distance errmax of the guess position for this +c time (geslon,geslat), then there will be NO +c dist_from_mean calculated for that parm. +c + USE error_parms; USE set_max_parms; USE inparms; USE def_vitals + USE atcf; USE gen_vitals; USE tracked_parms + USE verbose_output + + type (datecard) inp + + real clon(maxstorm,maxtime,maxtp),temp_clon(maxtp) + real clat(maxstorm,maxtime,maxtp),temp_clat(maxtp) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real trkerr(maxtp),errdist(maxtp),xvalues(maxtp) + real stderr(maxstorm,maxtime),devia(maxtp),wtpos(maxtp) + real dist_from_mean(maxtp) + real degrees,errtmp + integer gt345_ct,lt15_ct + logical(1) calcparm(maxtp,maxstorm),use4next(maxtp) + character charparm(11)*8,charmaxmin(11)*8 +c + data charparm /'zeta 850','zeta 700','vmag 850','NOT USED' + & ,'vmag 700','NOT USED',' gph 850',' gph 700',' MSLP' + & ,'vmag sfc','zeta sfc'/ + data charmaxmin /' Max ',' Max ',' Min ','NOT USED' + & ,' Min ','NOT USED',' Min ',' Min ',' Min ' + & ,' Min ',' Max '/ +c + ifret=0 +c +c We need to judge whether each parameter position is reasonable, +c so we'll check to make sure that the dist from each parameter's +c estimate to the guess position is less than a maximum allowable +c error. If it's the first forecast time, use the initial error max +c (defined as errinit in error_parms) as errmax. Otherwise, the +c max error criterion is that the distance error must not exceed 3 +c times the previous forecast time's standard deviation (after a +c small growth factor has been applied). +c UPDATE 3/5/98: During testing, it was found that just using the +c previous time's stdev made errmax too "jumpy" (i.e., at vt=48h, +c errmax could = 380, and then at vt=54h, errmax could jump down +c to 190, so we've changed it so that it uses an average of the +c stdev's from the 3 previous forecast times to maintain some +c continuity between successive forecast times). +c + if (ifh == 1) then + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR' ) then + errmax = err_gfs_init + errinit = err_gfs_init + else if (atcfname == 'EMX ') then + errmax = err_ecm_max + errinit = err_ecm_max + else + errmax = err_reg_init + errinit = err_reg_init + endif + else + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR') then + errinit = err_gfs_init + else if (atcfname == 'EMX ') then + errinit = err_ecm_max + else + errinit = err_reg_max + endif + + if (ifh >= 4) then + xavg_stderr = (stderr(ist,ifh-3) + stderr(ist,ifh-2) + & + stderr(ist,ifh-1)) / 3.0 + else if (ifh == 3) then + xavg_stderr = (stderr(ist,ifh-2) + stderr(ist,ifh-1)) / 2.0 + else if (ifh == 2) then + xavg_stderr = stderr(ist,ifh-1) + endif + +c Following errmax statement replaced by the ensuing 4 lines +c due to a compiler bug on some other platforms.... +c errmax = amin1(amax1(3.0*xavg_stderr*errpgro,errinit) +c & ,errpmax) + + errtmp = 3.0*xavg_stderr*errpgro + errmax = max(errtmp,errinit) + errtmp = errpmax + errmax = min(errmax,errtmp) + + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (ifh > 1) then + print '(a42,f8.2,a15,f8.2)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = ' + & ,stderr(ist,ifh-1),' xavg_stderr= ',xavg_stderr + else + print '(a45,a18)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = N/A' + & ,' xavg_stderr= N/A' + endif + print *,'At beg of fixcenter, errpgro = ',errpgro + print *,'At beg of fixcenter, errinit = ',errinit + print *,'At beg of fixcenter, errpmax = ',errpmax + print *,'At beg of fixcenter, ifh= ',ifh,' errmax= ',errmax + endif + + trkerr_avg = 0.0 + iclose = 0; itot4next = 0 + clonsum = 0.0; clatsum = 0.0 + errdist = 0.0 + use4next = .FALSE. + gt345_ct = 0 + lt15_ct = 0 + +c For each parm, check to see if the estimated center is within +c distance errmax of the guess center. If it's within errmax, +c then use that parm for locating the center. If it's NOT +c within errmax, but IS within errpmax, then we still use this +c in calculating the standard deviation of the parameters for +c helping to determine the errmax for the next forecast hour. +c NOTE: For calculating the std dev to be used for the next +c forecast hour, do NOT use vmag 850, vmag 700 or vmag sfc, since +c those parms are always guaranteed to be within a short range of +c the guess, due to the nature of the algorithm (see subroutine +c get_uv_center for further details on that). + + do ip=1,maxtp + + if (ip == 4 .or. ip == 6) then ! Parms 4 & 6 not defined. + calcparm(ip,ist) = .FALSE. + cycle + endif + if (calcparm(ip,ist)) then + call calcdist (geslon,geslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + errdist(ip) = dist + if (dist <= errpmax) then + if (ip == 3 .or. ip == 5 .or. ip == 10) then + use4next(ip) = .FALSE. + else + use4next(ip) = .TRUE. + trkerr_avg = trkerr_avg + dist + itot4next = itot4next + 1 + endif + endif + if (dist <= errmax) then + iclose = iclose + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 + endif + clonsum = clonsum + clon(ist,ifh,ip) + clatsum = clatsum + clat(ist,ifh,ip) + else + calcparm(ip,ist) = .FALSE. + endif + endif + + enddo + + if (iclose > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15ct) to the sum of the lons (clonsum) + clon_fguess = (clonsum + (360.*float(lt15ct)))/ iclose + else + clon_fguess = clonsum / float(iclose) + endif + if (clon_fguess >= 360.0) then + clon_fguess = clon_fguess - 360. + endif + clat_fguess = clatsum / float(iclose) + endif + +c Print out a table listing of the locations of the fixes for +c the individual parameters. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'--------------------------------------------------' + write (6,95) 'Individual fixes follow..., fhr= ',ifhours(ifh) + & ,ifclockmins(ifh),' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + write (6,97) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,'Model name = ',atcfname + print *,'Values of -99.99 indicate that a fix was unable to be' + print *,'made for that paramater. Parameters 4 & 6 are not' + print *,'used. Vorticity data values are scaled by 1e5.' + print *,'errdist is the distance that the position estimate is' + print *,'from the guess position for this time. MSLP value ' + print *,'here may differ from that in the atcfunix file since ' + print *,'the one here is that derived from the area-averaged ' + print *,'barnes analysis, while that in the atcfunix file is ' + print *,'from a specific gridpoint.' + write (6,21) geslon,360.-geslon,geslat + write (6,*) ' ' + write (6,23) + write (6,25) + endif + + if (geslat > 0.0) then + charmaxmin(1) = ' Max ' + charmaxmin(2) = ' Max ' + charmaxmin(11) = ' Max ' + else + charmaxmin(1) = ' Min ' + charmaxmin(2) = ' Min ' + charmaxmin(11) = ' Min ' + endif + + do ip=1,maxtp + if (ip == 1 .or. ip == 2 .or. ip == 11) then + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + else + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + endif + enddo + + 21 format (' Guess location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 23 format (' parm# parm Max/Min Lon_fix(E) Lon_fix(W)' + & ,' Lat_fix Max/Min_value calcparm errdist(km)') + 25 format (' ----- ---- ------- ---------- ----------' + & ,' ------- ------------- -------- ----------') + 27 format (2x,i2,4x,a8,2x,a8,3x,f7.2,5x,f7.2,4x,f7.2,7x,f9.2 + & ,6x,L2,7x,f7.2) + 95 format (1x,a33,1x,i4,':',i2.2,a2,a4,a1,a9) + 97 format (' Gen ID (if available): ',i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3) + + +c If number of parameter centers close enough (iclose) > 0, then +c calculate the center by taking an average of all the parameter +c center positions that are within distance errmax from the guess +c position (geslon,geslat). Get a first-guess mean position, and +c then re-calculate the position estimate by giving more weight +c to those positions that are closer to the first-guess mean +c position. + + dist_from_mean = 0.0 + + if (iclose > 0) then + +c Get distances from first-guess mean position.... + + do ip=1,maxtp + if (calcparm(ip,ist)) then + call calcdist (clon_fguess,clat_fguess,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + dist_from_mean(ip) = dist + endif + enddo + +c Get the mean distance of each parameter estimate from +c the first-guess mean position + + call avgcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,iaret) + + if (iaret == 0) then + + call stdevcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,stderr_close,isret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After stdevcalc, xmn_dist_from_mean= ' + & ,xmn_dist_from_mean,' stderr_close= ' + & ,stderr_close,' isret= ',isret + endif + + endif + if (iaret /= 0 .or. isret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER -- Error occurred in either' + print *,'!!! avgcalc or stdevcalc. Storm number = ',ist + print *,'!!! RCC from avgcalc = ',iaret + print *,'!!! RCC from stdevcalc = ',isret + print *,'!!! Center fix will NOT be made, and processing' + print *,'!!! for this storm is ending. The probable cause' + print *,'!!! is that no calcparms were valid for this storm' + print *,'!!! at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + if (calcparm(1,ist) .or. calcparm(2,ist) .or. calcparm(7,ist) + & .or. calcparm(8,ist) .or. calcparm(9,ist) + & .or. calcparm(11,ist)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In fixcenter, STOPPING PROCESSING for this' + print *,'!!! storm. The reason is that none of the fix' + print *,'!!! locations for parms z850, z700, zeta 850,' + print *,'!!! zeta 700, MSLP or sfc zeta were within a ' + print *,'!!! reasonable distance of the guess location. As' + print *,'!!! such, no attempt will be made to fix the vmag' + print *,'!!! 850 or vmag 700 minima since, by the nature of' + print *,'!!! the algorithm for these 2 parms, a fix ' + print *,'!!! location WILL ALWAYS be returned that is ' + print *,'!!! within a reasonable distance of the center' + print *,'!!! guess position (the other 5 parameters may' + print *,'!!! or may not do so). So if the other 5 parms' + print *,'!!! insist that the guess is too far away, we ' + print *,'!!! do not want to grab a false center with ' + print *,'!!! the vmag minima.' + print *,'!!! ist= ',ist,' ifh= ',ifh + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'!!! Forecast hour: ',i4,':',i2.2) + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now re-calculate the mean position by giving more weight +c to those position estimates that are closer to the first +c guess mean position. Note that if stderr_close < 5.0, we +c force it to be 5.0; we do this to avoid getting very +c large numbers for devia values, which could make the +c weights (wtpos) equal to 0. This occurred during testing +c when only 2 parameters were valid, and so, of course, the +c standard deviation from the mean of those 2 parameters +c was close to 0, which gave devia values around 6000, and +c then wtpos values of 0, leading to a divide by 0 crash +c later on in subroutine wtavrg. + + kprm=0 + + if (stderr_close > 0.0) then + if (stderr_close < 5.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Since stderr_close had a value less than' + print *,'5, stderr_close has been forced to be equal' + print *,'to 5 in order to avoid dividing by zero later' + print *,'on in subroutine wtavrg.' + endif + + stderr_close = 5.0 + endif + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + devia(kprm) = dist_from_mean(ip) / stderr_close + wtpos(kprm) = exp(-devia(kprm)/3.) + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + + if ( verb .ge. 3 ) then + write (6,113) ip,kprm,dist_from_mean(ip),devia(kprm) + & ,wtpos(kprm),temp_clon(kprm) + & ,360.-temp_clon(kprm),temp_clat(kprm) + endif + + endif + enddo + 113 format (1x,'ip= ',i2,' kprm= ',i2,' dist_from_mean= ',f7.3 + & ,' devia= ',f7.3,' wtpos= ',f8.5,2x,3(2x,f7.2)) + else +c +c This next if statement is for the case in which only 1 +c parameter is valid, for which the stderr_close will = 0 +c (obviously), but as long as we have 1 valid parameter, +c continue processing, and set the weight for that parm = 1. +c The else portion is for the case in which stderr_close +c = 0 with NO parms being close. +c + if (iclose == 1) then + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + wtpos(kprm) = 1 + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + endif + enddo + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, stderr_close not > 0' + print *,'!!! stderr_close = ',stderr_close + print *,'!!! The probable cause is that no calcparms were' + print *,'!!! valid for this storm at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + endif +c + if (kprm > 0) then + call wtavrg_lon (temp_clon,wtpos,kprm,fixlon(ist,ifh),iwtret1) + call wtavrg (temp_clat,wtpos,kprm,fixlat(ist,ifh),iwtret2) + if (iwtret1 > 0 .or. iwtret2 > 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER in call to wtavrg.' + print *,'!!! Return Codes from wtavrg calls follow: ' + print *,'!!! RCC from wtavrg for long fix: ',iwtret1 + print *,'!!! RCC from wtavrg for lat fix: ',iwtret2 + print *,'!!! This means a divide by zero would have ' + print *,'!!! been attempted, which means that the ' + print *,'!!! weights in wtpos are not > 0. Check in' + print *,'!!! subroutine fixcenter where devia values' + print *,'!!! are calculated to see if something is ' + print *,'!!! wrong there. Values of wtpos array follow:' + print *,'!!! ',wtpos + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + print *,' ' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, kprm NOT > 0' + print *,'!!! This means that, for whatever reason, the ' + print *,'!!! calcparm logical flag was set to .FALSE. for' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: IN FIXCENTER, No storms are within errmax ' + print *,'!!! OR the calcparm logical flag was set to .FALSE. ' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now calculate the average error of all the parms that are within +c a radius errpmax (defined in error_parms, ~600km), and the std +c dev of those errors. This standard deviation will be used in +c calculating the maximum allowable error for the next forecast +c time. + + if (itot4next > 0 .and. ifret /= 95) then + trkerr_avg = trkerr_avg / float(itot4next) + call stdevcalc (errdist,maxtp,use4next,trkerr_avg + & ,stderr(ist,ifh),isret) + if (isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in FIXCENTER calculating std deviation ' + print *,'!!! for use in next forecast hours errmax.' + print *,'!!! ist= ',ist,' ifh= ',ifh,' itot4next= ' + & ,itot4next + endif + + ifret = 95 + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine avgcalc (xdat,kmax,valid,xavg,iaret) +c +c ABSTRACT: This subroutine just calculates a straight average of +c the parameters in the input array (xdat). The logical array +c (valid) indicates whether or not to include a particular array +c member or not in the calculation. + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) +c + iaret = 0 +c + xsum = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + xsum = xsum + xdat(i) + ict = ict + 1 + endif + enddo +c + if (ict > 0) then + xavg = xsum / float(ict) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in avgcalc, ict NOT > 0' + endif + + xavg = xdat(1) + iaret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg (xdat,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xdat) using the input weights +c in the input array (wt). It is used to calculate the center lat +c and lon fix positions. +c + USE verbose_output + + real xdat(kmax),wt(kmax) +c + iwtret = 0 +c + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xdat(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg, wtot NOT > 0' + endif + + iwtret = 95 + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg_lon (xlon,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xlon) using the input weights +c in the input array (wt). This subroutine is specifically used +c to find the center lon fix positions. It contains code to +c account for wrapping around the Greenwich Meridian. +c + + USE verbose_output + + real xlon(kmax),wt(kmax) + integer gt345_ct,lt15_ct +c + iwtret = 0 + gt345_ct = 0 + lt15_ct = 0 + +c First check to see if we have lons that are both to the left +c and the right of the greenwich meridian + + do i = 1,kmax + if (xlon(i) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (xlon(i) < 15.) then + lt15_ct = lt15_ct + 1 + endif + enddo + + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some lons that are in the 300's (west of the GM), and + ! some that are in the 0's (east of the GM). We need to + ! standardize these if we want to get a meaningful average. + do i = 1,kmax + if (xlon(i) < 15.) then + xlon(i) = xlon(i) + 360.0 + endif + enddo + endif + + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xlon(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg_lon, wtot NOT > 0' + endif + + iwtret = 95 + endif + + if (xwtavg >= 360.0) then + xwtavg = xwtavg - 360.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine stdevcalc (xdat,kmax,valid,xavg,stdx,isret) + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) + + isret = 0 + + stdx = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + stdx = stdx + (xdat(i) - xavg)**2 + ict = ict + 1 + endif + enddo + + if (ict > 0) then + stdx = sqrt(stdx/float(ict)) + if (stdx == 0.0) then +c This can happen if you have just 2 points; The mean position +c will be exactly in the middle of the 2 points and so the +c standard deviation around that mean point will be 0. And +c since the calling routine will quit if the returned standard +c deviation is 0, we must force it to be 1 so the program +c continues running. Theoretically, it could also happen with +c 3 or more points, but the likelihood of the distances working +c out to exactly equidistant for 3 points is not that good. + stdx = 1.0 + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in stdevcalc, ict NOT > 0' + endif + + isret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,xval,trkrinfo,igucret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the minimum in the wind speed near the storm center. This center +c fix is done differently than for the other parms. With this fix, +c we severely limit the area that is searched, because we do not +c want to confuse a wind minimum out on the periphery of a storm +c with the center wind minimum. Therefore, this subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the guess +c position for this time and the 5 other parm fixes. That modified +c guess position is passed into this subroutine as uvgeslon and +c uvgeslat, and that's where the searching for the wind minimum +c is done. To get the wind minimum, the u and v data are first +c interpolated down to a fine grid (see details below for exact +c figures), and then a single-pass barnes analysis is done on that +c fine grid. The reason that we first interpolate the data (which +c is different from how we do the other parms) is that if we just +c use the original grid resolution, we may not be able to +c accurately pick out a minimum in the wind field at the center. +c + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real, allocatable :: uold(:,:),vold(:,:),unew(:,:),vnew(:,:) + real, allocatable :: rlonold(:),rlatold(:),rlonnew(:),rlatnew(:) + real, allocatable :: vmag(:,:) + real :: dx,dy + real :: grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + character*1 :: gotlat + logical(1) cflag, valid_pt(imax,jmax) + logical(1), allocatable :: lbi(:,:) +c + gotlat = 'n' +c +c ----------------------------------------------------------------- +c INTERPOLATE INPUT GRID TO SMALLER GRID +c ----------------------------------------------------------------- +c +c Get beginning and ending j points (on the input grid) for a +c smaller array that surrounds the storm. It is this smaller array +c that we will interpolate to a finer grid. +c +c Calculate number of pts to either side of this j to search +c + npts = ceiling(rads_vmag/(dtk*((dx+dy)/2.))) +c + call get_ij_bounds (npts,0,ritrk_vmag,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij D, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij D, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center from call to ' + print *,'!!! get_ij_bounds, stopping processing for ' + print *,'!!! storm number ',ist + endif + + igucret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, and our gridtype is NOT' + print *,'!!! global, so we are going to redefine ibeg to 1.' + print *,' ' + endif + + ibeg = 1 + endif + endif + + if (jbeg < 1) jbeg = 1 + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center calculating ibeg, iend, jbeg' + print *,'or jend. ibeg= ',ibeg,' iend= ',iend,' jbeg= ',jbeg + print *,'jend= ',jend + print *,'uv center will not be calculated for this time.' + endif + + igrret = 99 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, and our gridtype is' + print *,'!!! NOT global, so we will redefine iend to imax.' + print *,' ' + endif + + iend = imax + endif + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif +c + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the grid sizes for +c some of the typical grids that will be used: +c +c Original grid size # of interps Final grid size +c -------------------- ------------ --------------------- +c 1.00 deg (111.19 km) 3 0.125 deg (13.9 km) +c 1.25 deg (138.99 km) 3 0.156 deg (17.4 km) +c 2.50 deg (277.99 km) 4 0.156 deg (17.4 km) + + if ((dx+dy)/2. > 1.2) then + numinterp = 4 + else if ((dx+dy)/2. > 0.50 .and. (dx+dy)/2. <= 1.2) then + numinterp = 3 + else if ((dx+dy)/2. > 0.25 .and. (dx+dy)/2. <= 0.50) then +ctpm6/14 numinterp = 2 + numinterp = 3 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.25) then +ctpm6/14 numinterp = 1 + numinterp = 3 + else if ((dx+dy)/2. <= 0.10) then + numinterp = 0 + endif + + dell = (dx+dy)/2. + imxold = iend - ibeg + 1 + jmxold = jend - jbeg + 1 + +c -------------------------------------------------------------- +c Before interpolating, make sure that all the original +c points have valid data. If they don't then exit the +c subroutine. NOTE: This is NOT checking to see if ALL the pts +c on the complete & full input grid have valid data; it only +c checks those points that are within the box returned from +c get_ij_bounds. + + do i=ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_uv_center, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! PROCESSING WILL STOP. ' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + stop 94 + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_uv_center' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + do j=jbeg,jend + if (.not. valid_pt(ip,j)) goto 975 + enddo + + enddo + +c ------------------------------------ +c Now begin the interpolation process + + allocate (uold(imxold,jmxold),stat=iuo) + allocate (vold(imxold,jmxold),stat=ivo) + allocate (rlonold(imxold),stat=iloo) + allocate (rlatold(jmxold),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. iloo /= 0 .or. ilao /= 0) goto 970 + + do intnum = 1,numinterp + + if (intnum == 1) then + + do i=ibeg,iend + + ik = i + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ik = i + imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i < 1' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i + endif + + igucret = 92 + return + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ik = i - imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i > imax' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i,' imax= ',imax + endif + + igucret = 92 + return + endif + endif + + rlonold(i-ibeg+1) = glon(ik) + do j=jbeg,jend + uold(i-ibeg+1,j-jbeg+1) = u(ik,j,nlev) + vold(i-ibeg+1,j-jbeg+1) = v(ik,j,nlev) + if (gotlat == 'n') then + rlatold(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatold once + enddo + + else + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate (rlatold) + allocate (uold(imxnew,jmxnew),stat=iuo) + allocate (vold(imxnew,jmxnew),stat=ivo) + allocate (rlonold(imxnew),stat=iloo) + allocate (rlatold(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 970 + + gotlat = 'n' + do i=1,imxnew + rlonold(i) = rlonnew(i) + do j=1,jmxnew + uold(i,j) = unew(i,j) + vold(i,j) = vnew(i,j) + if (gotlat == 'n') then + rlatold(j) = rlatnew(j) + endif + enddo + gotlat = 'y' + enddo + + imxold = imxnew + jmxold = jmxnew + deallocate (unew); deallocate (vnew) + deallocate (rlonnew); deallocate (rlatnew) + + endif + + dell = 0.5 * dell + imxnew = 2 * imxold - 1 + jmxnew = 2 * jmxold - 1 + + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + + call bilin_int_even (imxold,jmxold,uold + & ,imxnew,jmxnew,unew,ibiret) + call bilin_int_even (imxold,jmxold,vold + & ,imxnew,jmxnew,vnew,ibiret) +c call lin_int (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int_lon (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int (jmxold,jmxnew,rlatold,rlatnew,iliret) + + chk_lonspc_old = rlonold(imxold) - rlonold(imxold - 1) + chk_latspc_old = rlatold(jmxold) - rlatold(jmxold - 1) + chk_lonspc_new = rlonnew(imxnew) - rlonnew(imxnew - 1) + chk_latspc_new = rlatnew(jmxnew) - rlatnew(jmxnew - 1) + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, intnum= ',intnum + print *,'imxold= ',imxold,' imxnew= ',imxnew + print *,'jmxold= ',jmxold,' jmxnew= ',jmxnew + print *,'Grid boundaries of modified uv grid: ' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ' + & ,grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ' + & ,grid_minlon + endif + + enddo + +c ------------------ + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate(rlatold) + + if (numinterp == 0) then + + ! No interpolations were done for this fine mesh grid, but we + ! need to fill some of these arrays and define variables for + ! subsequent subroutine calls just below here that require + ! the variables imxnew, jmxnew, and the arrays unew and vnew. + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional ' + print *,'grid; iend should not > imax here !!!' + endif + + igucret = 99 + return + endif + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional' + print *,'grid; ibeg should not < 1 here !!!' + endif + + igucret = 99 + return + endif + endif + + imxnew = iend - ibeg + 1 + jmxnew = jend - jbeg + 1 + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + gotlat = 'n' + + do i=ibeg,iend + + ip = i + + if (i > imax) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i - imax ! Wrapping past GM + endif + + if (i < 1) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i + imax ! Wrapping past GM + endif + + rlonnew(i-ibeg+1) = glon(ip) + do j=jbeg,jend + unew(i-ibeg+1,j-jbeg+1) = u(i,j,nlev) + vnew(i-ibeg+1,j-jbeg+1) = v(i,j,nlev) + if (gotlat == 'n') then + rlatnew(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatnew once + enddo + + endif + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,'Grid boundaries of modified uv grid in get_uv_center:' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ',grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ',grid_minlon + endif + + allocate (vmag(imxnew,jmxnew),stat=ivm) + allocate (lbi(imxnew,jmxnew),stat=ilb) + if (ivm /= 0 .or. ilb /= 0) goto 972 + call calc_vmag (unew,vnew,imxnew,jmxnew,vmag,icvret) + deallocate (unew); deallocate (vnew) + + lbi = .TRUE. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to find_maxmin, imxnew= ',imxnew + & ,'jmxnew= ',jmxnew,' ist= ',ist + write (6,171) dell,uvgeslon,360.-uvgeslon,uvgeslat + 171 format (' dell= ',f7.3,' uvgeslon= ',f8.3,'E (',f8.3,'W)' + & ,' uvgeslat= ',f8.3) + endif + +c Note that in the next call, I pass the 'global' argument to +c find_maxmin. This defines what type of grid it is, so that the +c proper grid_buffer can be chosen. This grid_buffer is designed +c to avoid having a center be chosen too close to the grid +c boundary. However, in the case of vmag here, we are only using +c a small subgrid, and we want to make sure we use *all* points +c in that subgrid for searching, and that will occur if we set that +c calling argument to 'global' as opposed to 'regional'. + + call find_maxmin (imxnew,jmxnew,dell,dell,'vmag' + & ,vmag,'min',ist,uvgeslon,uvgeslat,rlonnew,rlatnew,lbi + & ,trkrinfo,cflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,'global',ifmret) + deallocate (vmag); deallocate (lbi) + deallocate (rlonnew); deallocate (rlatnew) +c + if (ifmret == 0) then + goto 995 + else + igucret = ifmret + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center in call to find_maxmin' + print *,'!!! storm num = ',ist,' igucret = ',igucret + endif + + goto 998 + endif +c + 970 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either uold, vold,' + print *,'!!! rlonold or rlatold in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 971 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either unew, vnew,' + print *,'!!! rlonnew or rlatnew in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 972 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either vmag or lbi in ' + print *,'!!! subroutine get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! ivm= ',ivm,' ilb= ',ilb + endif + + igucret = 97 + goto 998 + + 975 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Inside get_uv_center, at least one of the points' + print *,'!!! is not a valid data point. This point may be ' + print *,'!!! outside the valid data bounds of a regional grid' + print *,'!!! i= ',i,' j= ',j + print *,'!!! Storm number = ',ist + endif + + igucret = 98 + goto 998 +c + 995 continue + igucret = 0 +c + 998 continue + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_guess (guesslon,guesslat,clon,clat + & ,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) +c +c ABSTRACT: The purpose of this subroutine is to get a modified +c first guess lat/lon position before searching for the +c minimum in the wind field. The reason for doing this is +c to better refine the guess and avoid picking up a wind +c wind minimum far away from the center. So, use the +c first guess position (and give it strong weighting), and +c then also use the fix positions for the current time +c (give the vorticity centers stronger weighting as well), +c and then take the average of these positions. +c +c INPUT: +c guesslon guess longitude for this forecast time +c guesslat guess latitude for this forecast time +c clon array with center longitude fixes for the various parms +c clat array with center latitude fixes for the various parms +c calcparm logical; tells whether or not a parm has a valid fix +c at this forecast hour +c ist index for current storm +c ifh index for current forecast hour +c maxstorm max # of storms that can be handled +c +c OUTPUT: +c uvgeslon contains modified guess longitude position at which to +c look for the wind minimum +c uvgeslat contains modified guess latitude position at which to +c look for the wind minimum +c igugret return code for this subroutine (0=normal) +c---- +c + USE set_max_parms; USE level_parms; USE error_parms + USE verbose_output + + logical(1) calcparm(maxtp,maxstorm) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real uvgeslon, uvgeslat + real guesslon,guesslat,degrees + integer gt345_ct,lt15_ct + + sumlon = 0.0 + sumlat = 0.0 + ict = 0 + gt345_ct = 0 + lt15_ct = 0 + +c NOTE: We need to be careful in this routine when averaging +c the longitudes together, in case we cross the greenwich +c meridian, because then we may be averaging 345+ lons with +c lons that are less than 15, giving incorrect results. +c Therefore, check for this, and if it occurs, add 360 onto +c any of the <15 lons (add it twice for those lons being +c counted twice (guesslon and the vorticity centers)). + +c Weight the uv guess position by counting the storm's guess +c position twice. + + sumlon = sumlon + 2.*guesslon + sumlat = sumlat + 2.*guesslat + ict = ict + 2 + + if (guesslon > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (guesslon < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct.... + endif + + do ip = 1,maxtp + if ((ip > 2 .and. ip < 7) .or. ip == 10) then + cycle ! because 3-6 are for 850 & 700 u & v and 10 is + ! for surface wind magnitude. + else + if (calcparm(ip,ist)) then + call calcdist (guesslon,guesslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + + if (dist < uverrmax) then +c +c Give the vorticity centers 2x weighting as well +c + if (ip == 1 .or. ip == 2 .or. ip == 11) then + sumlon = sumlon + 2.*clon(ist,ifh,ip) + sumlat = sumlat + 2.*clat(ist,ifh,ip) + ict = ict + 2 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct... + endif + else + sumlon = sumlon + clon(ist,ifh,ip) + sumlat = sumlat + clat(ist,ifh,ip) + ict = ict + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 ! Only 1 for non-zeta parms + endif + endif + + endif + + endif + endif + enddo +c + if (ict > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15ct) to the sum of the lons (sumlon) + uvgeslon = (sumlon + (360.*float(lt15ct)))/ ict + else + uvgeslon = sumlon / ict + endif + if (uvgeslon >= 360.0) then + uvgeslon = uvgeslon - 360. + endif + uvgeslat = sumlat / ict + igugret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_guess, ict not > 0, ict= ',ict + print *,'!!! vmag center will not be calculated for this' + print *,'!!! storm -- at least not at this level' + print *,'!!! Storm number = ',ist + endif + + igugret = 91 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calc_vmag (xu,xv,imx,jmx,wspeed,icvret) +c +c ABSTRACT: This subroutine calculates the magnitude of the wind +c speed for an array of points, given real u and real v arrays. +c + real xu(imx,jmx),xv(imx,jmx),wspeed(imx,jmx) +c + do i=1,imx + do j=1,jmx + wspeed(i,j) = sqrt( xu(i,j)*xu(i,j) + xv(i,j)*xv(i,j) ) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_even (imxold,jmxold,xold + & ,imxnew,jmxnew,xnew,ibiret) +c +c ABSTRACT: This subroutine does a bilinear interpolation on a +c grid of evenly spaced data. Do NOT attempt to use this subroutine +c with data that are not evenly spaced or you will get unpredictable +c results. +c + real xold(imxold,jmxold), xnew(imxnew,jmxnew) +c +c +c --------------------------------------------------------------------- +c Latitude ----> | +c | +c L O e O e O e O e O | O: original point from input array +c o | +c n e 1 2 1 2 1 2 1 e | 1: interpolated, primary inter. pt +c g | +c i O 2 O 2 O 2 O 2 O | e: interpolated edge point +c t | +c u e 1 2 1 2 1 2 1 e | 2: interpolated, secondary inter. pt +c d | +c e O 2 O 2 O 2 O 2 O | Interpolations are done in the order +c | as indicated above; First, the input +c | e 1 2 1 2 1 2 1 e | 'O' pts are placed onto the new, +c | | larger grid. From that, the '1' pts +c | O 2 O 2 O 2 O 2 O | can be interpolated. Next, the edge +c | | (e) pts are interpolated using an +c v e 1 2 1 2 1 2 1 e | interpolation of two 'O' pts and one +c | '1' pt. Finally, the '2' pts are +c O e O e O e O e O | done using the 2 surrounding '0' and +c | '1' pts. Bilinear interpolation is +c | made incredibly easier by the fact +c | that the grid is evenly spaced. +c --------------------------------------------------------------------- +c NOTE: Remember that the arrays that are read in are indexed as +c (lon,lat), so that in the diagram above, pt (1,1) is at the upper +c left and pt (imax,jmax) is at the lower right, and each column is +c a new latitude and each row is a new longitude. +c +c ----------------------------------------------------------------- +c Put original (O) values from input array into new, expanded array +c ----------------------------------------------------------------- +c + do i=1,imxold + do j=1,jmxold + xnew(2*i-1,2*j-1) = xold(i,j) + enddo + enddo +c +c ---------------------------------------------- +c Interpolate to get primary interior (1) points +c ---------------------------------------------- +c + do i=1,imxold-1 + do j=1,jmxold-1 + xnew(2*i,2*j) = 0.25 * (xnew(2*i-1,2*j-1) + xnew(2*i+1,2*j-1) + & + xnew(2*i+1,2*j+1) + xnew(2*i-1,2*j+1)) + enddo + enddo +c +c --------------------------- +c Interpolate edge (e) points +c --------------------------- +c +c ... Northernmost 'e' points ... +c + j=1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,2)) + enddo +c +c ... Southernmost 'e' points ... +c + j = 2*jmxold - 1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,j-1)) + enddo +c +c ... Westernmost 'e' points ... +c + i=1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(2,2*j)) + enddo +c +c ... Easternmost 'e' points ... +c + i = 2*imxold - 1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(i-1,2*j)) + enddo +c +c ------------------------------------------------ +c Interpolate to get secondary interior (2) points +c ------------------------------------------------ +c + do j=2,2*jmxold-2 + istep = mod(j+1,2) + do i=istep+2,2*imxold-2,2 + xnew(i,j) = 0.25 * (xnew(i-1,j) + xnew(i,j-1) + xnew(i+1,j) + & + xnew(i,j+1)) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points +c + do i=1,ioldmax-1 + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int_lon (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. This particular +c routine is specifically used for interpolating +c longitudes, and it factors in the possibility of +c interpolating across the greenwich meridian. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points, and make the +c necessary adjustment when interpolating a longitude between, +c for example, 359.5 and 0.0. +c + do i=1,ioldmax-1 + if (xnew(2*i-1) > 350. .and. xnew(2*i+1) < 10.) then + xnew(2*i) = 0.5 * (xnew(2*i-1) + (360. + xnew(2*i+1))) + else + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + endif + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) +c +c ABSTRACT: This subroutine finds the maximum and mean zeta values +c at 850 & 700 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms + USE trkrparms; USE level_parms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + + logical(1) readflag(14),valid_pt(imax,jmax),compflag + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanzeta,dx,dy,re,ri,parmlon,parmlat + integer igridzeta(nlevm1),imeanzeta(nlevm1) + integer n,ix1,ix2,ilev,npts,imax,jmax,igzvret,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_zeta_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_zeta_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max zeta values at 850 and 700 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_zeta_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_zeta_loop: do n=1,2 + + gridpoint_maxmin = -99.0 + xmeanzeta = -99.0 + compflag = .true. + + select case (n) + case (1); ilev=850 ! For 850 mb + case (2); ilev=700 ! For 700 mb + end select + + if (zeta(ilonfix,jlatfix,n) > -9990.0) then + + ! ------------------------------------------- + ! We have valid zeta data for this level, so + ! we first call barnes now to get the mean zeta + ! surrounding our found center position. + ! ------------------------------------------- + + if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,n),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanzeta + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds + imeanzeta(n) = int ((xmeanzeta * 1e6) + 0.5) + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_zeta_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for zeta values will not be done.') + exit report_zeta_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + exit report_zeta_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) n,ilev,xmeanzeta,imeanzeta(n) + 621 format (1x,'+++ RPT_MEAN_ZETA: n= ',i2,' lev= ',i4 + & ,' xmeanzeta= ',f9.6,' imeanzeta= ',i4) + write (6,*) ' --- mean zeta raw = ',xmeanzeta + endif + + ! ----------------------------------------------- + ! Call fix_latlon_to_ij to get the nearest actual + ! raw (grid) zeta data value, not the mean value. + ! ----------------------------------------------- + + call fix_latlon_to_ij (imax,jmax,dx,dy + & ,zeta(1,1,n),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanzeta,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + igridzeta(n) = int ((gridpoint_maxmin * 1e6) + 0.5) + else + igridzeta(n) = -99 + endif + + if ( verb .ge. 3 ) then + write (6,623) n,ilev,gridpoint_maxmin,igridzeta(n),ifilret + 623 format (1x,'+++ RPT_GRID_ZETA: n= ',i2,' lev= ',i4 + & ,' grid zeta= ',f9.6,' igrid zeta= ',i4,' ifilret= ',i3) + write (6,*) ' --- grid zeta raw= ',gridpoint_maxmin + endif + + enddo report_zeta_loop + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get 850 & 700 zeta for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine find_maxmin (imax,jmax,dx,dy,cparm,fxy,maxmin,ist + & ,guesslon,guesslat,rlonv,rlatv,valid_pt,trkrinfo + & ,compflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,cmodel_type,ifmret) +c +c This routine finds the location (clon,clat) of and value of the +c the max or min of fxy in the vicinity of slon,slat. The value of +c the input flag maxmin determines whether to look for a max or a +c min value. The max/min is determined by finding the point which +c gives the max/min value of a single point barnes analysis of fxy +c with e-folding radius re (km) and influence radius ri (km). The +c initial search is restricted to a radius rads around the point +c (slon,slat) on a grid with lon,lat spacing dx and dy. The location +c is refined by reducing the spacing of the search grid by a factor +c of two, nhalf times. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c maxmin Char string indicating whether to search for a max or min +c ist Number of the storm being processed +c guesslon Guess longitude of the storm +c guesslat Guess latitude of the storm +c rlonv Array containing longitude values of input grid points +c rlatv Array containing latitude values of input grid points +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c trkrinfo derived type detailing user-specified grid info +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c +c INPUT/OUTPUT: +c compflag Logical; continue processing this storm or not (would be +c set to FALSE if, for example, the guess position is +c outside the domain of a regional grid) +c +c OUTPUT: +c ctlon Center longitude of storm found for this parameter +c ctlat Center latitude of storm found for this parameter +c xval Max or Min value found at the (ctlon,ctlat) +c ifmret Return code from this subroutine +c +c UPDATE DEC 2009: For the HFIP HRH testing, it was found that +c due to the very limited domain size of some of the models, the +c barnes scheme was allowing points close to the grid boundaries +c to erroneously be selected as the center point. We add in a +c buffer (grid_buffer) here to prevent this from occurring. + + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + character(*) maxmin,cparm,cmodel_type + logical(1) compflag, valid_pt(imax,jmax) + real fxy(imax,jmax),rlonv(imax),rlatv(jmax) + real ctlon,ctlat,degrees,dx,dy,guesslon,guesslat,xval + real rads,re,ri,dell,fmax,fmin,rlatt,rlont,dist,ftemp,ritmp + real vmag_latmax,vmag_latmin,vmag_lonmax,vmag_lonmin,retmp + real tlon,tlat,grid_buffer,temp_grid_minlon,temp_guesslon + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + integer imax,jmax,ist,bskip1,bskip2,iskip,ifmret,npts,maxvgrid + integer ibeg,iend,jbeg,jend,ilonfix,jlatfix,igiret,icount,iret + integer ibct,ibarnes_loopct,i,j,k,iix,jix,jvlatfix,ivlonfix + integer nhalf,icvpret + integer date_time(8) + character (len=10) big_ben(3) +c + ifmret = 0 + nhalf = 5 +c +c ----------------------------------------------------------- +c Set initial parms for use in find_maxmin. +c Different radii used for V magnitude than for other parms, +c see discussion in module radii for more details. +c + if (cparm == 'vmag') then + +c NOTE: The maxvgrid variable determines what size grid to send +c to subroutine barnes. e.g., maxvgrid = 8 means send an +c 8x8 grid; maxvgrid = 12 means send a 12x12 grid. For +c ultra-fine mesh grids (finer than 0.04 deg, or 1/25 deg), +c we expand to 12 in order to sample a few more points +c around each grid point. + + if ((dx+dy)/2. > 0.04) then + maxvgrid = 8 + else + maxvgrid = 12 + endif + + rads = rads_vmag; re = retrk_vmag; ri = ritrk_vmag + re = (float(maxvgrid)/4) * ((dx+dy)/2. * dtk) ! Basically, this +c sets re equal to half the distance from the gridpoint +c in question to the farthest point that will be +c sampled when the (maxvgrid x maxvgrid) grid is passed +c on to subroutine barnes. Thus, just ignore the +c parameter retrk_vmag, and use this instead. + else if ((dx+dy)/2. < 1.26 .and. (dx+dy)/2. >= 0.40) then + rads = rads_most; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.40 .and. (dx+dy)/2. >= 0.10) then +ctpm6/14 rads = rads_fine; re = retrk_most; ri = ritrk_most + rads = rads_most; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.10) then + rads = rads_hres; re = retrk_hres; ri = ritrk_most + else + rads = rads_coarse; re = retrk_coarse; ri = ritrk_coarse + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of find_maxmin, rads= ',rads,' re= ',re + & ,' ri= ',ri,' cparm= ',cparm,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+15; fmin = 1.0e+15 + ctlon = 0.0; ctlat = 0.0 + + if (npts == 0) npts = 1 + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if (dell > 0.20) then +ctpm6/14 bskip1 = 2 + bskip1 = 1 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 4 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 6 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 10 + bskip2 = 5 + else if (dell <= 0.03) then + bskip1 = 15 + bskip2 = 5 + endif + + if (cparm == 'vmag') then + bskip1 = 1 + bskip2 = 1 + endif + +c If input parm is vmag, we've already done the minimizing by +c interpolating to the fine mesh grid, so we'll simply send the +c bounds that were input to this subroutine to barnes +c as boundaries for the array to search. For all other parms, +c however, no minimizing has been done yet, so we need to call +c get_ij_bounds to set the boundaries for a much smaller grid that +c surrounds the storm (as opposed to having subroutine barnes +c search the entire global grid). + + if (cparm == 'vmag') then + + if ( verb .ge. 3 ) then + print *,'In find_maxmin, jmax= ',jmax,' imax= ',imax + endif + + ibeg=1; jbeg=1; iend=imax; jend=jmax + vmag_latmax = rlatv(1) ! N-most lat of vmag subgrid + vmag_latmin = rlatv(jmax) ! S-most lat of vmag subgrid + vmag_lonmin = rlonv(1) ! W-most lon of vmag subgrid + vmag_lonmax = rlonv(imax) ! E-most lon of vmag subgrid + + if ( verb .ge. 3 ) then + write (6,44) vmag_latmax,vmag_lonmin,360.-vmag_lonmin + & ,imax,jmax + write (6,46) vmag_latmin,vmag_lonmax,360.-vmag_lonmax + endif + + 44 format (' vmag_latmax= ',f8.3,' vmag_lonmin= ',f8.3 + & ,'E (',f8.3,'W) imax= ',i4,' jmax= ',i4) + 46 format (' vmag_latmin= ',f8.3,' vmag_lonmax= ',f8.3 + & ,'E (',f8.3,'W)') + + if (vmag_lonmin > 330. .and. vmag_lonmax < 30.) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: For a case of find_maxmin, our vmag' + print *,'!!! subgrid is straddling the GM. The code should' + print *,'!!! be able to handle this, but if strange errors' + print *,'!!! are occurring, check into the code either here' + print *,'!!! in find_maxmin or get_uv_ctr.' + print *,' ' + endif + endif + + npts = ceiling(rads/(dtk*dell)) + + else + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,guesslon,guesslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to ' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + ifmret = 92 + return + endif + + endif + +c +c --------------------------------------------------------------- +c + if ( verb .ge. 3 ) then + print *,' ' + write (6,39) guesslon,360.-guesslon,guesslat + 39 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + if (cparm == 'vmag') then + print *,'ilonfix= (unused) jlatfix= (unused)' + & ,' npts= ',npts + print *,'ilonfix and jlatfix are meaningless for computing' + print *,'vmag, so ignore the large values you see for them.' + else + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + endif + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + + if ( verb .ge. 3 ) then + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: find_maxmin 1 ',i2.2,':',i2.2,':',i2.2) + endif + + ibct=0 + ibarnes_loopct = 0 + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (guesslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = guesslon - 360. + else + temp_guesslon = guesslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = guesslon + endif + + jix = 0 + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + jloop: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = guesslat + dell*float(j) + + iix = 0 + +c vlat(jix) = rlatt + + iloop: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c if (cparm == 'vmag') then +c print *,' ' +c print '(a16,i6,a4,i6,2(a8,f8.3),a12,f8.3)' +c & ,'in find_max, i= ',i +c & ,' j= ',j,' rlatt= ',rlatt,' rlont= ',rlont +c & ,' 360-rlont= ',360.-rlont +c endif + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT: icvpret= ',icvpret + endif + + cycle iloop + endif + + call calcdist(rlont,rlatt,temp_guesslon,guesslat,dist,degrees) + if (dist .gt. rads) cycle iloop + + if (cparm == 'vmag') then + +c This next bit of code gets the ij coordinates for an 8x8 +c box around the current point under consideration. These ij +c coordinates are sent to barnes so that barnes only loops +c 64 times, as opposed to nearly 10,000 if the whole 97x97 +c array were sent. So, fix rlatt to the grid point just +c northward of rlatt and fix rlont to the grid point just +c eastward of rlont. Note that this makes for a modified +c barnes analysis in that we're sort of specifying ahead of +c time exactly which grid points will be included and we'll +c be excluding some points that would be near the periphery +c of each (rlont,rlatt)'s range, but as long as we're consis- +c tent and do it this way for each point, it's well worth the +c trade-off in cpu time. Parameter maxvgrid determines what +c size array to send to barnes (maxvgrid=8 means 8x8) + + jvlatfix = int((vmag_latmax - rlatt)/dy + 1.) + ivlonfix = int((rlont - temp_grid_minlon)/dx + 2.) +c ivlonfix = int((rlont - vmag_lonmin)/dx + 2.) + + ibeg = ivlonfix - (maxvgrid/2) + iend = ivlonfix + (maxvgrid/2 - 1) + jbeg = jvlatfix - (maxvgrid/2 - 1) + jend = jvlatfix + (maxvgrid/2) + + if (ibeg < 1 .or. jbeg < 1 .or. + & iend > imax .or. jend > jmax) then + + ! DO NOT quit if we find a boundary outside the grid + ! bounds. Rather, just set the J violating bound(s) to + ! the min or max limit, and for I bounds, allow the + ! program to continue down to subsequent code below, + ! provided it's a global grid. + +c print *,'!!! ' +c print *,'!!! Before vmag adjustments, boundaries are: ' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt,' dx= ',dx +c print *,'!!! temp_grid_minlon= ',temp_grid_minlon +c print *,'!!! vmag_latmax= ',vmag_latmax +c print *,'!!! ivlonfix = ',ivlonfix,' jvlatfix = ',jvlatfix +c print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax +c print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Vmag will not be computed for' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Vmag will not be computed for ' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,'!!! ' + print *,'!!! *AFTER* vmag adjustments, boundaries are: ' + print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax + print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + endif + + endif + + endif + +ctpm6/14 if (cparm == 'vmag') then +ctpm6/14 ri = re * 3 +c print '(a36,f10.4,a6,f10.4)' +c & ,' + before call to vmag barnes, re= ',re,' ri= ',ri +ctpm6/14 endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,bskip1,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes...' + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After 1st findmax loop, # calls to barnes = ',ibct + print *,'Total # of barnes loop iterations = ',ibarnes_loopct + endif + +c + 55 format ('i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ',f7.3 + & ,' barnval= ',f11.5) + 56 format ('k= ',i3,' i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ' + & ,f7.3,' barnval= ',f11.5) + + if (ctlon < 0.) then + ! We have grid-wrapped to find the ctlon, which was found to be + ! < 0, so for reporting purposes and for the start of the next + ! loop, set ctlon to positive degress east. + ctlon = ctlon + 360. + endif + + if (cparm == 'zeta') then + + if ( verb .ge. 3 ) then + print *,'!!! Zeta check, fmax= ',fmax,' fmin= ',fmin + write (6,61) 360.-ctlon,ctlat,fmax*100000.,fmin*100000. + endif + + else + + if ( verb .ge. 3 ) then + write (6,63) 360.-ctlon,ctlat,fmin + endif + + endif + 61 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax (x10e5) = ',f10.3,' fmin (x10e5) = ',e15.3) + 63 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmin = ',e15.3) + 111 format (i2,' rlont= ',f7.2,'W rlatt= ',f7.2,' zeta= ',f13.8) + +c Through interpolation, the grid for vmag has already been +c minimized considerably, we don't need to go through the 2nd part +c of this subroutine, which halves the grid spacing. + + if (nhalf < 1 .or. cparm == 'vmag') then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c ------------------------------------------------------------- +c If the grid spacing is +c fine enough (I've chosen 0.2-deg as a min threshold), there is +c no need to halve the grid more than 3 times, as halving a +c 0.2-deg grid 3 times gives a resolution of 0.025-deg (2.7 km), +c or a max error in the position estimate of 2.7/2 = 1.35 km. + + if ((dx+dy)/2. <= 0.2) then + if ((dx+dy)/2. <= 0.05) then + nhalf = 1 + else + nhalf = 2 + endif + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +ctpm6/14 ctpm npts = 3 +ctpm6/14 npts = npts/2 +ctpm6/14 npts = max(npts,1) + npts = 3 + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only +c do this once for this grid-refinement (even though the grid is +c redefined 3 times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to get_ij_bounds' + print *,'!!! just before nhalf loop. Stopping processing' + print *,'!!! for storm number ',ist + endif + + ifmret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + + if ( verb .ge. 3 ) then + print *,' ' + endif + + if ((dx+dy)/2. <= 1.25 .and. ri >= 300 .and. re >= 150) then + retmp = re + ritmp = ri + re = re * 0.5 + ri = ri * 0.5 + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re has been reduced' + print *,'from ',retmp,' to ',re,', and ri has been reduced ' + print *,'from ',ritmp,' to ',ri + endif + + else + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re and ri have NOT ' + print *,'been changed. re = ',re,' ri = ',ri + endif + + endif + + ibct=0 + ibarnes_loopct = 0 + do k=1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: find_maxmin kloop, k= ',i2,' ',i2.2,':' + & ,i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat + fmax = -1.0e+15; fmin = 1.0e+15 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'find_maxmin nhalf loop, cparm= ',cparm,' k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,iskip,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes, k= ',k + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop2 + enddo jloop2 + + if ( verb .ge. 3 ) then + if (cparm == 'zeta') then + write (6,71) k,360.-ctlon,ctlat,fmax*100000.,fmin*100000. + else + write (6,73) k,360.-ctlon,ctlat,fmax,fmin + endif + endif + + enddo + + 71 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax (x10e5) = ',f10.3,' fmin (x10e5) = ',e15.3) + 73 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax = ',e15.3,' fmin = ',e15.3) + + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ppp after 2nd findmax loop, # calls to barnes = ' + & ,ibct + print *,'ppp Total # of barnes loop iterations = ' + & ,ibarnes_loopct + endif + + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine barnes(flon,flat,rlon,rlat,iimax,jjmax,iibeg,jjbeg + & ,iiend,jjend,fxy,defined_pt,bskip,re,ri,favg,icount,ctype + & ,trkrinfo,iret) +c +c ABSTRACT: This routine performs a single-pass barnes anaylsis +c of fxy at the point (flon,flat). The e-folding radius (km) +c and influence radius (km) are re and ri, respectively. +c +c NOTE: The input grid that is searched in this subroutine is most +c likely NOT the model's full, original grid. Instead, a smaller +c subgrid of the original grid is searched. The upper left and +c lower right grid point indices are passed into this subroutine +c (iibeg, jjbeg, iiend, jjend) for this subgrid. These indices are +c determined in the subroutine get_ij_bounds, and the purpose of +c doing it this way is to limit the number of points for which the +c subroutine has to calculate distances (for a global 1 deg grid, +c the number of loop iterations is reduced from 65160 to somewhere +c around 600). +c +c NOTE: This subroutine will immediately exit with a non-zero +c return code if it tries to access a grid point that does not have +c valid data. This would happen in the case of a regional grid, if +c you try to access a point near the edge of the grid (remember that +c because of the interpolation for the regional grids, there will be +c areas around the edges that have no valid data). +c +c INPUT: +c flon Lon value for center point about which barnes anl is done +c flat Lat value for center point about which barnes anl is done +c rlon Array of lon values for each grid point +c rlat Array of lat values for each grid point +c iimax Max number of pts in x-direction on input grid +c jjmax Max number of pts in y-direction on input grid +c iibeg i index for grid point to start barnes anlysis (upp left) +c jjbeg j index for grid point to start barnes anlysis (upp left) +c iiend i index for last grid point in barnes anlysis (low right) +c jjend j index for last grid point in barnes anlysis (low right) +c fxy Real array of data on which to perform barnes analysis +c defined_pt Logical; bitmap array used for regional grids +c bskip integer to indicate number of grid points to skip during +c a barnes loop, in order to speed processing +c re input e-folding radius for barnes analysis +c ri input influence radius for searching for min/max +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, in +c this barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c favg Average value about the point (flon,flat) +c iret Return code from this subroutine +c + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real fxy(iimax,jjmax), rlon(iimax), rlat(jjmax) + real degrees + integer bskip + logical(1) defined_pt(iimax,jjmax) + character(*) ctype + +c -------------------------- + + res = re*re + wts = 0.0 + favg = 0.0 + + icount = 0 + + do jix=jjbeg,jjend,bskip + do iix=iibeg,iiend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > iimax) then + if (trkrinfo%gridtype == 'global') then + i = iix - iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i,' imax= ',iimax + print *,' ' + endif + + stop 97 + endif + endif + + icount = icount + 1 + + call calcdist(flon,flat,rlon(i),rlat(j),dist,degrees) + + if (dist .gt. ri) cycle + + if (defined_pt(i,j)) then + if (fxy(i,j) >-999.01 .and. fxy(i,j) <-998.99) then + ! This is a patch. Even though this (i,j) is a valid + ! point, its zeta value has been set to -999 because a + ! neighboring point in subroutine rvcal was found + ! to be out of the grid boundaries. + cycle + endif + wt = exp(-1.0*dist*dist/res) + wts = wts + wt + favg = favg + wt*fxy(i,j) + else + if (ctype == 'vitals') then + continue + else +carw print *,' ' +carw print *,'!!! UNDEFINED PT OUTSIDE OF GRID IN BARNES....' +carw print *,'!!! i= ',i,' j= ',j +carw print *,'!!! flon= ',flon,' flat= ',flat +carw print *,'!!! rlon= ',rlon(i),' rlat= ',rlat(j) +carw print *,'!!! re= ',re,' ri= ',ri +carw print *,'!!! EXITING BARNES....' +carw print *,' ' +carw iret = 95 +carw return + endif + endif + + enddo + enddo + + if (wts > 1.0E-5) then + favg = favg/wts + else + favg = 0.0 + endif + iret = 0 +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,rglatmax,rglatmin,rglonmax,rglonmin,geslon,geslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) +c +c ----------------------------------------------------------- +c ABSTRACT: This subroutine figures out, based on ri, dx and dy and +c the guess latitude and longitude positions, the farthest reaching +c grid points that are searchable by an analysis subroutine. The +c purpose is to return indices for a subgrid that is much smaller +c than the original, full grid. This smaller subgrid can then be +c passed to a subsequent analysis or interpolation subroutine, and +c work can be done on this smaller array. This can help save time, +c especially in the barnes analysis subroutine, as work will only +c be done on, say, a 20 x 20 array (400 pts) instead of on a +c 360 x 181 array (65160 pts). It's crucial, however, to make sure +c that the ibeg, jbeg, iend and jend are extended far enough out to +c fully encompass any search that would be done. Below is a +c diagram showing the different grids.... +c +c Full Global or Regional Model Grid (Grid F) -----------> +c ---------------------------------------------------------------- +c | | (ibeg,jbeg) | +c | | x = ij position that the | (Grid R) | +c | | geslat/geslon is fixed to. ._______________. | +c | | | | | +c | | Even though only the points | (Grid B) | | +c | | within Grid B will be checked | . . . . k | | +c v | later on for a max/min (in the | . . . . . | | +c | case of a subsequent call to | . . x . e | | +c | find_maxmin), the barnes anal- | . . . . . | | +c | ysis will include all pts sur- | . . . . . | | +c | rounding these Grid B points | | | +c | that are within a radius of ri. ._______________. | +c | So in the case of pt. k, that ri | +c | radius may extend all the way to the Grid R | | +c | boundary, thus we need to include those (iend,jend) | +c | points within our ibeg-jbeg-iend-jend bounds. | +c | | +c ---------------------------------------------------------------- +c +c Remember that the grids we deal with start north and increase +c south, so the northernmost latitude on the input grid will have +c a j index of 1. +c +c INPUT: +c npts Num pts from x to edge of max/min search grid (Grid B) +c (i.e., You define the size of Grid B by the value of +c npts that you pass into this subroutine). +c nhalf Number of times the grid spacing will be halved +c ri Radius of influence (for use in barnes analysis) +c imax Number of points in x-direction on original grid +c jmax Number of points in y-direction on original grid +c dx Input grid spacing in i-direction on original grid +c dy Input grid spacing in j-direction on original grid +c rglatmax Value of northern-most latitude on original grid +c rglatmin Value of southern-most latitude on original grid +c rglonmax Value of eastern-most longitude on original grid +c rglonmin Value of western-most longitude on original grid +c geslat Value of latitude of guess position of storm +c geslon Value of longitude of guess position of storm +c +c OUTPUT: +c ilonfix i index on full, input grid that storm is fixed to +c jlatfix j index on full, input grid that storm is fixed to +c ibeg i index for top left of sub-array (Grid R) of input grid +c iend i index for bot right of sub-array (Grid R) of input grid +c jbeg j index for top left of sub-array (Grid R) of input grid +c jend j index for bot right of sub-array (Grid R) of input grid +c igiret Return code from this subroutine +c + USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + real tmpangle +c + igiret = 0 +c +c -------------------------------------- +c GET BEGINNING AND ENDING J POINTS.... +c +c (1) Calculate number of searchable, max/min pts, that is, the pts +c from x to the edge of Grid B. +c (2) Calculate number of pts beyond the last search point in Grid +c B, but are within the bounds of Grid R and thus can be +c included in the barnes analysis. +c (3) Add (1) and (2) to get the max number of pts to subtract/add +c to x to get jbeg and jend. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Beginning of get_ij_bounds...' + print *,' geslat= ',geslat,' geslon= ',geslon + print *,' ' + endif + + +c If nhalf > 0: This occurs in the case of a call from fmax, when +c the grid spacing is halved nhalf times. In this case, we have to +c do extra work to figure out the maximum possible grid point. For +c this case: +c jhlatpts = # of grid pts to last possible search pt (from x to +c edge of Grid B in above diagram), plus a cushion. +c jripts = # of searchable grid points within radius ri of last +c possible search pt (num pts between edge of Grid B +c and edge of Grid R in above diagram), plus a cushion +c jbmaxlatpts = # of pts (in j direction) from x to the edge of +c Grid R to include in this subgrid. +c +c If nhalf = 0: In this case, the grid spacing will not be reduced, +c so the number of pts in j direction from x to the edge of Grid +c B will be the input parameter npts, and just multiply it by 2, +c and add 2 for a cushion to get jmaxlatpts. Typically, this sub +c is called from find_maxmin, and in that sub, the first time that +c this sub is called, nhalf will = 0. Then, after a first-shot +c center is found, the grid spacing will be cut in order to rerun +c barnes on a smaller grid, and that's when nhalf will be sent +c here as 3. +c + if (nhalf > 0) then + rdeg = 0.0 + do i = 1,nhalf + rdeg = rdeg + float(npts) * (1./(float(i)*2)) * (dx+dy)/2 + enddo + jhlatpts = ceiling(rdeg/dy) + 1 + jripts = ceiling((ri + 1.)/(dtk*dx)) + 1 + jbmaxlatpts = jhlatpts + jripts + else + jbmaxlatpts = npts * 2 + 2 + endif +c +c +c Roughly fix geslat to the grid point just poleward of geslat. +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' +++ Near top of get_ij_bounds, ' + print *,' +++ geslat= ',geslat,' geslon= ',geslon + print *,' +++ rglatmax= ',rglatmax,' rglatmin= ',rglatmin + print *,' +++ rglonmax= ',rglonmax,' rglonmin= ',rglonmin + print *,' +++ imax= ',imax,' jmax= ',jmax + print *,' +++ dx= ',dx,' dy= ',dy,' nhalf= ',nhalf + print *,' +++ npts= ',npts + if(nhalf>0) then + print *,' +++ jhlatpts= ',jhlatpts,' jripts= ',jripts + else + print *,' +++ nhalf<=0 so jhlatpts and jripts unused' + endif + print *,' +++ jbmaxlatpts= ',jbmaxlatpts + endif + + if (geslat >= 0.0) then + jlatfix = int((rglatmax - geslat)/dy + 1.) + else + jlatfix = ceiling((rglatmax - geslat)/dy + 1.) + endif + + if ( verb .ge. 3 ) then + print *,' +++ jlatfix= ',jlatfix + endif + + jbeg = jlatfix - jbmaxlatpts + jend = jlatfix + jbmaxlatpts + if (jbeg > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jbeg > jmax' + print *,'!!! jbeg = ',jbeg,' jmax= ',jmax + endif + + igiret = igiret + 1 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jend < 1, jend = ',jend + endif + + igiret = igiret + 1 + return + endif + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' +++ jbeg= ',jbeg,' jend= ',jend + endif + + ! If using a global grid, avoid using the pole points, or else + ! you'll get a cosfac = 0 and then a divide by zero!!! + + if (jend == jmax .and. rglatmin == -90.0) then + jend = jmax - 2 + endif + if (jbeg == 1 .and. rglatmax == 90.0) then + jbeg = 3 + endif + +c ----------------------------------------- +c NOW GET BEGINNING AND ENDING I POINTS.... +c +c Using the map factor (cos lat), figure out, based on ri, the +c max distance beyond the last search point in x-direction (in +c degrees) that could be searched at this guess latitude (geslat) +c (i.e., in the diagram above, the max num pts from pt. e eastward +c to the edge of Grid R). Calculate how many grid points that is, +c add 2 to it for a cushion, & add the number of points (npts) +c within the defined search grid (Grid B) to get ibmaxlonpts. +c +c April, 2007: A min statement was put on the calculation to +c derive dlon, since with that cosine in there, the values of +c of dlon could get pretty ridiculous as you approach the poles. +c Also, the cosine factor (cosfac) used to be computed at the +c most poleward latitude possible given the jend here. For +c similar concerns with cosines near the poles, I've scrapped +c this to instead compute the cosine factor at the input +c guess latitude. - tpm + + cosfac = cos (geslat * dtr) + tmpangle = cosfac * dtk + dlon = min((ri /tmpangle ),20.0) +c dlon = min((ri / (cosfac * dtk)),20.0) +c + if (nhalf > 0) then + ihlonpts = ceiling(rdeg/dx) + 1 + ibmaxlonpts = ihlonpts + ceiling(dlon/dx) + 2 + else + ibmaxlonpts = npts + ceiling(dlon/dx) + 2 + endif + + if ( verb .ge. 3 ) then + if(nhalf>0) then + print *,' +++ rdeg= ',rdeg,' ri= ',ri,' cosfac= ',cosfac + print *,' +++ dtr= ',dtr,' dtk= ',dtk,' dlon= ',dlon + else + print*,' +++ nhalf<=0 so rdeg,ri,cosfac,dtr,dtk,dlon unused' + endif + print *,' +++ ibmaxlonpts= ',ibmaxlonpts,' dx= ',dx,' dy= ',dy + endif + +c Roughly fix geslon to the grid point just EASTward of geslon. + + ilonfix = int((geslon - rglonmin)/dx + 2.) + + ibeg = ilonfix - ibmaxlonpts + iend = ilonfix + ibmaxlonpts + + if ( verb .ge. 3 ) then + print *,' +++ (orig) ilonfix= ',ilonfix + print *,' +++ (orig) ibeg= ',ibeg,' iend= ',iend + print *,' +++ ' + endif + + if (ibeg > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 1 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, ibeg > imax' + print *,'!!! for a non-global grid' + print *,'!!! ibeg = ',ibeg,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + ! For a regional grid, just set iend to be imax + iend = imax + endif + endif + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + ! For a regional grid, just set ibeg to be 1 + ibeg = 1 + endif + endif + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + + if ( verb .ge. 3 ) then + print *,'!!! ERROR in get_ij_bounds, iend < 1' + print *,'!!! for a non-global grid' + print *,'!!! iend = ',iend,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine check_bounds (guesslon,guesslat,ist,ifh,trkrinfo + & ,icbret) +c +c ABSTRACT: This subroutine checks to make sure that the requested +c storm is in fact within the model's grid boundaries; +c this is only a concern for the regional models. +c + USE def_vitals; USE grid_bounds; USE set_max_parms + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + if (guesslon > glonmax .or. guesslon < glonmin .or. + & guesslat > glatmax .or. guesslat < glatmin) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is outside of grid' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + goto 125 + else + icbret = 0 + endif + + ! We have encountered problems with global grids where we + ! continue tracking almost the whole way to the pole. While + ! that's nice to do that, it creates problems for array + ! indices, especially in subroutine getradii. So we will cut + ! our losses and eliminate tracking of storms within + ! 5 degrees of the pole for global grids. + + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'global')then + if (guesslat > 85.0 .or. guesslat < -85.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is too close to the' + print *,'!!! N or S Pole for global tracking.' + print *,'!!! STOPPING TRACKING FOR THIS STORM DUE TO POLE' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + else + icbret = 0 + endif + endif + + 125 continue +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calcdist(rlonb,rlatb,rlonc,rlatc,xdist,degrees) +c +c ABSTRACT: This subroutine computes the distance between two +c lat/lon points by using spherical coordinates to +c calculate the great circle distance between the points. +c Figure out the angle (a) between pt.B and pt.C, +c N. Pole then figure out how much of a % of a great +c x circle distance that angle represents. +c / \ +c b/ \ cos(a) = (cos b)(cos c) + (sin b)(sin c)(cos A) +c / \ +c pt./<--A-->\c NOTE: The latitude arguments passed to the +c B / \ subr are the actual lat vals, but in +c \ the calculation we use 90-lat. +c a \ +c \pt. NOTE: You may get strange results if you: +c C (1) use positive values for SH lats AND +c you try computing distances across the +c equator, or (2) use lon values of 0 to +c -180 for WH lons AND you try computing +c distances across the 180E meridian. +c +c NOTE: In the diagram above, (a) is the angle between pt. B and +c pt. C (with pt. x as the vertex), and (A) is the difference in +c longitude (in degrees, absolute value) between pt. B and pt. C. +c +c !!! NOTE !!! -- THE PARAMETER ecircum IS DEFINED (AS OF THE +c ORIGINAL WRITING OF THIS SYSTEM) IN KM, NOT M, SO BE AWARE THAT +c THE DISTANCE RETURNED FROM THIS SUBROUTINE IS ALSO IN KM. +c + USE trig_vals + + real degrees +c + if (rlatb < 0.0 .or. rlatc < 0.0) then + pole = -90. + else + pole = 90. + endif +c + distlatb = (pole - rlatb) * dtr + distlatc = (pole - rlatc) * dtr + difflon = abs( (rlonb - rlonc)*dtr ) +c + cosanga = ( cos(distlatb) * cos(distlatc) + + & sin(distlatb) * sin(distlatc) * cos(difflon)) + +c This next check of cosanga is needed since I have had ACOS crash +c when calculating the distance between 2 identical points (should +c = 0), but the input for ACOS was just slightly over 1 +c (e.g., 1.00000000007), due to (I'm guessing) rounding errors. + + if (cosanga > 1.0) then + cosanga = 1.0 + endif +c added on 04/28/2016-------------------------- + if (cosanga < -1.0) then + cosanga = -1.0 + endif +c added on 04/28/2016-------------------------- + + degrees = acos(cosanga) / dtr + circ_fract = degrees / 360. + xdist = circ_fract * ecircum +c +c NOTE: whether this subroutine returns the value of the distance +c in km or m depends on the scale of the parameter ecircum. +c At the original writing of this subroutine (7/97), ecircum +c was given in km. +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine subtract_cor (imax,jmax,dy,level) +c +c ABSTRACT: This subroutine subtracts out the coriolis parameter +c from the vorticity values. It is needed because at the original +c writing of this system, all of the forecast centers who included +c vorticity included only absolute vorticity. +c + USE tracked_parms; USE trig_vals; USE grid_bounds + + implicit none + + integer :: i,j,imax,jmax,level + real :: dy,coriolis,rlat +c + do j=1,jmax + rlat = glatmax - ((j-1) * dy) + coriolis = 2. * omega * sin(rlat*dtr) + do i=1,imax + zeta(i,j,level) = zeta(i,j,level) - coriolis + enddo + enddo +c + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_grib_file_name (ifh,gfilename,ifilename) + +c ABSTRACT: This subroutine uses various input regarding the model +c and forecast hour and generates the name of the input grib file +c for this particular forecast hour. Remember that the lead time +c is in minutes and that 5 spaces must be reserved for the lead +c time (e.g., f00360). File name should be something that looks +c like either, e.g., "gfdl.6thdeg.katrina12l.2005082818.f00720", +c or "gfdl.6thdeg.2005082818.f00720" (the part in there with the +c storm name & ID is optional). The grib index file name should +c be exactly the same as the grib data file itself, but with the +c character string ".ix" added onto the end of the name. +c +c NOTE: Array iftotalmins is brought in via module tracked_parms. +c +C INPUT: +c ifh integer array index for current lead time +c +c OUTPUT: +c gfilename GRIB file name +c ifilename GRIB index file name + + USE gfilename_info; USE tracked_parms; USE atcf + USE verbose_output + + implicit none + + character(*) gfilename,ifilename + character cfmin*5,cymdh*10 + integer ifh,nlen1,nlen2,nlen3,nlen4,nlen5 + +c Convert integer minutes to 5-position character, with +c leading zeroes, and convert 10-digit integer date into +c 10-position character. Then trim the various input variables +c and combine all into the file name. + + write (cfmin,'(i5.5)') iftotalmins(ifh) + write (cymdh,'(i10.10)') atcfymdh + + nlen1 = len_trim(gmodname) + gfilename = trim(gmodname(1:nlen1)) + + nlen2 = len_trim(rundescr) + + gfilename = trim(gfilename(1:nlen1))//'.'//trim(rundescr(1:nlen2)) + + nlen3 = len_trim(atcfdescr) + nlen4 = len_trim(gfilename) + +c If an extension to the name with the ATCF or storm name descriptor +c was included, then add it to the name now. Otherwise, just add +c the starting date and the lead time in minutes. + + if (nlen3 > 0) then + gfilename = trim(gfilename(1:nlen4))//'.' + & //trim(atcfdescr(1:nlen3))//'.'//cymdh//'.f'//cfmin + else + gfilename = trim(gfilename(1:nlen4))//'.'//cymdh//'.f'//cfmin + endif + +c Create the name for the grib index file, which is just the name of +c the grib file, with "ix" added to the end of it. + + nlen5 = len_trim(gfilename) + ifilename = trim(gfilename(1:nlen5))//'.ix' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,72) 'gfilename',gfilename + write (6,72) 'ifilename',ifilename + endif + + 72 format (1x,'In get_grib_file_name, file name for ',a9 + & ,' is ',a120) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata (readflag,valid_pt,imax,jmax,ifh + & ,need_to_flip_lats,need_to_flip_lons,inp,lugb,lugi + & ,trkrinfo) +c +c ABSTRACT: This subroutine reads the input GRIB file for the +c tracked parameters. It then calls subroutines to convert the +c data from a 1-d array into a 2-d array if the read was successful. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature (I jerry-rigged this by storing +c the data as being at the 401 mb level.) +c +c INPUT: +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c inp of a derived type, contains user-input info +c lugb integer unit number of input grib file +c lugi integer unit number of input grib index file +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE verbose_output; USE params; USE grib_mod; USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + type (datecard) inp + type (gribfield) :: gfld,prevfld,holdgfld +c + integer, parameter :: jf=40000000 + integer, parameter :: nparms=14 + real, allocatable :: f(:) + real :: dmin,dmax,firstval,lastval + logical(1), allocatable :: lb(:) + logical(1) valid_pt(imax,jmax),readflag(nparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1) file_open + logical :: unpack=.true. + logical :: open_grb=.false. + character*1 :: lbrdflag + character*8 :: chparm(nparms) + CHARACTER(len=8) :: pabbrev + character (len=10) big_ben(3) + integer date_time(8) + integer,dimension(200) :: jids,jpdt,jgdt + integer :: listsec1(13) + integer, intent(in) :: imax,jmax + integer igparm(nparms),iglev(nparms),iglevtyp(nparms) + integer ig2_parm_cat(nparms),ig2_parm_num(nparms) + integer ig2_lev_val(nparms),ig2_lev_typ(nparms) +cJ.Peng---12/13/2016------------------ + integer ig2_lev_11_cmc(nparms),ig2_lev_val_cmc(nparms) + integer ig2_lev_11_cmcd(nparms),ig2_lev_val_cmcd(nparms) + + integer cpsig2_parm_cat(nlevs_cps),cpsig2_parm_num(nlevs_cps) + integer cpsig2_lev_typ(nlevs_cps),cpsig2_lev_val(nlevs_cps) + + integer ec_igparm(nparms),ec_iglev(nparms),ec_iglevtyp(nparms) + integer cpsgparm(nlevs_cps),cpsglev(nlevs_cps) + integer cpsglevtyp(nlevs_cps) + integer ec_cpsgparm(nlevs_cps) + integer jpds(200),jgds(200),kpds(200),kgds(200) + integer igvret,ifa,ila,ip,ifh,i,j,k,kj,iret,kf,lugb,lugi + integer jskp,jdisc,np + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer pdt_4p0_vert_level,pdt_4p0_vtime + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 +c + lbrdflag = 'n' + +c The following data statements contain the parameters that will be +c used to search the grib files. The first 9 parameters will all be +c used to locate the storm position. The last 4 parameters (500 mb +c u- and v-components and 10 m u- and v- components) will not be +c used for tracking, but only for helping to estimate the next first +c guess position (500 mb winds) and for estimating the max near- +c surface wind speeds in the vicinity of the storm (10 m winds). +c +c ** NOTE: iglevtyp(12 & 13) and iglev(12 & 13) are initialized to +c 0 just to satisfy the IBM xlf compiler, which barks about +c there being too few initial values in the list when I +c only had 11 values there -- even though the real +c initialization for these variables is done just about +c 10 lines below. +c +c ** NOTE: The new ECMWF hi-res data uses the ECMWF GRIB parameter +c ID table, which has different values than the NCEP +c table. Therefore, we needed to add the variables and +c data values for ec_igparm, ec_iglevtyp and ec_iglev. +c +c July 2007: Read statements added for GP height for cyclone +c phase space (CPS) algorithm. + + data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11/ + data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 + & ,100/ + data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500,401/ + + data cpsgparm /13*7/ + data ec_cpsgparm /13*156/ + data cpsglevtyp /13*100/ + data cpsglev /900,850,800,750,700,650,600,550,500,450,400 + & ,350,300/ + + data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 + & ,131,132,130/ + data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 + & ,100/ + data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 + & ,401/ + + data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' + & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' + & ,'vgrid','temp'/ + + data ig2_parm_cat /2,2,2,2,2,2,3,3,3,2,2,2,2,0/ + data ig2_parm_num /10,10,2,3,2,3,5,5,1,2,3,2,3,0/ + data ig2_lev_typ /100,100,100,100,100,100,100,100,101,103,103 + & ,100,100,100/ + data ig2_lev_val /850,700,850,850,700,700,850,700,0,10,10,500,500 + & ,401/ +cJ.Peng----12/13/2016--------------------------- + data ig2_lev_11_cmc /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0/ + data ig2_lev_val_cmc/85,7,85,85,7,7,85,7,0,10,10,5,5,40100/ + + data ig2_lev_11_cmcd /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0/ + data ig2_lev_val_cmcd /85,7,85,85,7,7,85,7,0 + & ,10,10,5,5,40100/ + + data cpsig2_parm_cat /13*3/ + data cpsig2_parm_num /13*5/ + data cpsig2_lev_typ /13*100/ + data cpsig2_lev_val /900,850,800,750,700,650,600,550,500,450,400 + & ,350,300/ + +c This next bit is needed because we need to read the near-surface +c winds, and while several models provide us with 10m winds, the +c UKMET gives us surface winds, while nogaps gives us 10m winds. +c For GFDL, we have 35m winds. + +c Model numbers used: (1) AVN, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) Early Eta, (7) NOGAPS, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble, +c (13) SREF Ensemble, +c (14) NCEP Ensemble (from ensstat mean fields), +c (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) NCEP Ensemble RELOCATION +c (21) UKMET hi-res (from NHC) + + if (trkrinfo%gribver == 2) then + +c So far, for GRIB v2, we have all the same IDs for 10m winds for +c all models, so no need to break out by model like we do for +c GRIB v1 in the else portion of this if statement. However, we +c do need to check to see if the input model = 1 or 8 (which is +c the GFS and GDAS). If so, then we want to look for the +c so-called membrane MSLP, which has a GRIB2 param num of 192. + + if (inp%model == 1 .or. inp%model == 10) then + ig2_parm_num(9) = 192 ! Membrane MSLP for GFS/gefs only + endif + + if ( verb .ge. 3 ) then + print *,' ' + endif + + else + + if (inp%model == 1 .or. inp%model == 2 .or. + & inp%model == 5 .or. inp%model == 6 .or. inp%model == 8 .or. + & inp%model == 10 .or. inp%model == 13 .or. + & inp%model == 17 .or. + & inp%model == 14 .or. inp%model == 19 .or. + & inp%model == 20 .or. inp%model == 16 .or. + & inp%model == 22 .or. inp%model == 15) then + iglevtyp(10) = 105 + iglevtyp(11) = 105 + iglev(10) = 10 + iglev(11) = 10 + else if (inp%model == 3) then ! UKMET: "surface" winds + iglevtyp(10) = 1 + iglevtyp(11) = 1 + iglev(10) = 0 + iglev(11) = 0 + else if (inp%model == 4) then ! ECMWF hi-res: "surface" winds + ec_iglevtyp(10) = 1 + ec_iglevtyp(11) = 1 + ec_iglev(10) = 0 + ec_iglev(11) = 0 + else if (inp%model == 7) then ! NOGAPS: 10m winds + iglevtyp(10) = 105 + iglevtyp(11) = 105 + iglev(10) = 10 + iglev(11) = 10 + else if (inp%model == 9) then ! GFDL: 35m winds + iglevtyp(10) = 105 + iglevtyp(11) = 105 + iglev(10) = 10 + iglev(11) = 10 + else if (inp%model == 21) then ! ECMWF Ensemble - 10m winds and +c pmsl level ID + iglevtyp(10) = 105 + iglevtyp(11) = 105 + iglev(10) = 10 + iglev(11) = 10 + iglevtyp(9) = 1 + endif + + if (inp%model == 1 .or. inp%model == 8 .or. inp%model == 22) + & then + ! For GFS & GDAS models, use the membrane (Eta reduction) MSLP, + ! also known by the GRIB abbreviation of MSLET + igparm(9) = 130 + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata. A return' + print *,'code (iret) not equal to zero indicates that ' + print *,'subroutine getgb was unable to find the requested ' + print *,'parameter. This could be simply because the parm is ' + print *,'not included in the grib file (this is likely for ' + print *,'ECMWF data, as they limit what they send us), or it ' + print *,'could indicate a problem with the grib index file.' + endif + + + if (allocated(f)) deallocate(f) + if (allocated(lb)) deallocate(lb) + allocate (f(imax*jmax),stat=ifa) + allocate (lb(imax*jmax),stat=ila) + if (ifa /= 0 .or. ila /= 0) then + print *,' ' + print *,'!!! ERROR in getdata allocating f or lb array.' + print *,'!!! ifa = ',ifa,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + if (trkrinfo%gribver == 2) then + + ! Reading from a GRIB v2 file.... + + grib2_standard_parm_read_loop: do ip = 1,nparms + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 ! meteorological products + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for input parameter by production template 4.0. This +c tave program is used primarily for temperature, but still we +c will leave that as a variable and not-hard wire it in case we +c choose to average something else in the future. + + ! We are looking for Temperature or GP Height here. This + ! block of code, or even the smaller subset block of code that + ! contains the JPDT(1) and JPDT(2) assignments, can of course + ! be modified if this program is to be used for interpolating + ! other variables.... + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = ig2_parm_cat(ip) + JPDT(2) = ig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + + JPDT(10) = ig2_lev_typ(ip) +cJ.Peng----12/13/2016---------------------------- + if (inp%model == 16 ) then + JPDT(11) = ig2_lev_11_cmc(ip) + JPDT(12) = ig2_lev_val_cmc(ip) + else if (inp%model == 15 ) then + JPDT(11) = ig2_lev_11_cmcd(ip) + JPDT(12) = ig2_lev_val_cmcd(ip) + else + JPDT(11) = 0 + if (JPDT(10) == 100) then ! isobaric surface + JPDT(12) = ig2_lev_val(ip) * 100 ! GRIB2 levels are in Pa + else + JPDT(12) = ig2_lev_val(ip) ! This is going to be either mslp + & ! or 10m winds. + endif + endif + + if ( verb .ge. 3 ) then + print *,'before getgb2 call, value of unpack = ',unpack + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is CLOSED' + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,531) date_time(5),date_time(6),date_time(7) + 531 format (1x,'TIMING: before getgb2-1',i2.2,':',i2.2,':',i2.2) + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,532) date_time(5),date_time(6),date_time(7) + 532 format (1x,'TIMING: after getgb2-1',i2.2,':',i2.2,':',i2.2) + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 in getdata = ',iret + print *,'after getgb2 call, value of unpacked = ' + & ,gfld%unpacked + print *,'after getgb2 call, gfld%ndpts = ',gfld%ndpts + print *,'after getgb2 call, gfld%ibmap = ',gfld%ibmap + endif + + if ( iret == 0) then + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + print *,' gfld%idrtnum = ', gfld%idrtnum + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb .ge. 3 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ndpts ! Number of gridpoints returned from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if ( verb .ge. 3 ) then + + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ndpts,firstval,lastval,dmin,dmax +cJ.Peng---& ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + + endif + + select case (chparm(ip)) + case ('absv') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb2 could not find parm: ' + & ,chparm(ip) + print *,'!!! at level = ',ig2_lev_val(ip) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib2_standard_parm_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c This is the GRIB2 reading section. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + grib2_cps_parm_lev_loop: do ip = 1,nlevs_cps + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + + jpds = -1 + jgds = -1 + j=0 + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = cpsig2_parm_cat(ip) + JPDT(2) = cpsig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + + JPDT(10) = cpsig2_lev_typ(ip) + if (JPDT(10) == 100) then ! isobaric surface + JPDT(12) = cpsig2_lev_val(ip) * 100 ! GRIB2 levels + ! are in Pa + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR in getdata: JPDT(10) array value' + print *,'should only be 100 in this CPS section' + print *,'for GRIB2 data.' + endif + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,731) date_time(5),date_time(6),date_time(7) + 731 format (1x,'TIMING: before getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn + & ,jgdt,unpack,krec,gfld,iret) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,732) date_time(5),date_time(6),date_time(7) + 732 format (1x,'TIMING: after getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 (PHASE) in getdata = ',iret + print *,'after getgb2 call(PHASE),' + & ,' value of unpacked = ',gfld%unpacked + print *,'after getgb2 (PHASE) call, gfld%ndpts = ' + & ,gfld%ndpts + print *,'after getgb2 (PHASE) call, gfld%ibmap = ' + & ,gfld%ibmap + + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb2 (PHASE) call, j= ',j,' k= ',k + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb2 (PHASE) call, j= ',j,' k= ',k + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + +c Determine packing information from GRIB2 file. +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb .ge. 3) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) + & then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb .ge. 3 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ' + & ,gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ' + & ,gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ndpts ! Number of gridpoints returned + ! from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do +c this once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb .ge. 3) then + + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) + & then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.' + & ,gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ' + & ,gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ' + & ,gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.' + & ,gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline + & ,gfld%ipdtmpl(1),gfld%ipdtmpl(2)) + + print *,' ' + write (6,231) + 231 format (' rec# param level byy bmm bdd ' + & ,'bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ndpts,firstval,lastval,dmin + & ,dmax +cJ.Peng---& ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo grib2_cps_parm_lev_loop + + endif + + endif + + else + + ! Reading from a GRIB v1 file.... + + grib1_read_loop: do ip = 1,nparms + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then ! ECMWF hi-res data uses ECMWF table + jpds(5) = ec_igparm(ip) + jpds(6) = ec_iglevtyp(ip) + jpds(7) = ec_iglev(ip) + else ! All other models use NCEP-standard GRIB table + jpds(5) = igparm(ip) + jpds(6) = iglevtyp(ip) + jpds(7) = iglev(ip) + endif + + if (jpds(5) == 999) then + cycle + endif + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,831) date_time(5),date_time(6),date_time(7) + 831 format (1x,'TIMING: before getgb-1',i2.2,':',i2.2,':',i2.2) + + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,832) date_time(5),date_time(6),date_time(7) + 832 format (1x,'TIMING: after getgb-1',i2.2,':',i2.2,':',i2.2) + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb call, j= ',j,' k= ',k + & ,' iftotalmins= ' + & ,iftotalmins(ifh),' parm # (ip) = ',ip,' iret= ',iret + else + print *,'After getgb call, j= ',j,' k= ',k,' ifhours= ' + & ,ifhours(ifh),' parm # (ip) = ',ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,29) + else + write (6,31) + endif + 29 format (' rec# parm# levt lev byy bmm bdd bhh fmin' + & ,' npts minval maxval') + 31 format (' rec# parm# levt lev byy bmm bdd bhh fhr ' + & ,' npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + + select case (chparm(ip)) + case ('absv') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb could not find parm: ',chparm(ip) + print *,'!!! at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib1_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + cps_grib1_lev_loop: do ip = 1,nlevs_cps + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then + ! Use different grib parm id for ECMWF GP height + jpds(5) = ec_cpsgparm(ip) + else + jpds(5) = cpsgparm(ip) + endif + jpds(6) = cpsglevtyp(ip) + jpds(7) = cpsglev(ip) + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,841) date_time(5),date_time(6),date_time(7) + 841 format (1x,'TIMING: before getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,842) date_time(5),date_time(6),date_time(7) + 842 format (1x,'TIMING: after getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,39) + else + write (6,41) + endif + 39 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fmin npts minval maxval') + 41 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fhr npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo cps_grib1_lev_loop + + endif + + endif + + endif +c + deallocate (f) + deallocate (lb) +c + return + end +c +c------------------------------------------------------------------- +c +c------------------------------------------------------------------- + subroutine bitmapchk (n,ld,d,dmin,dmax) +c +c This subroutine checks the bitmap for non-existent data values. +c Since the data from the regional models have been interpolated +c from either a polar stereographic or lambert conformal grid +c onto a lat/lon grid, there will be some gridpoints around the +c edges of this lat/lon grid that have no data; these grid +c points have been bitmapped out by Mark Iredell's interpolater. +c To provide another means of checking for invalid data points +c later in the program, set these bitmapped data values to a +c value of -999.0. The min and max of this array are also +c returned if a user wants to check for reasonable values. +c + logical(1) ld + dimension ld(n),d(n) +c + dmin=1.E15 + dmax=-1.E15 +c + do i=1,n + if (ld(i)) then + dmin=min(dmin,d(i)) + dmax=max(dmax,d(i)) + else + d(i) = -999.0 + endif + enddo +c + return + end +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic (imax,jmax,lb1d,lb2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of logical data (lb1d) into a 2-dimensional output +c array (dimension imax,jmax) of logical data (lb2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NOGAPS grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c lb1d 1-d array containing logical bitmap values +c iscanflag This is kgds(11), an integer value in the GDS, +c which holds the scanning mode for the data values +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + logical(1) lb1d(imax*jmax),lb2d(imax,jmax) + logical(1) :: need_to_flip_lats +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + lb2d(ilon,ilatix) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + lb2d(ilon,ilat) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine conv1d2d_real (imax,jmax,dat1d,dat2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of real data (dat1d) into a 2-dimensional output +c array (dimension imax,jmax) of real data (dat2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NOGAPS grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d real array of data +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c dat2d 2-d real array of data +c + logical(1) :: need_to_flip_lats + real dat1d(imax*jmax),dat2d(imax,jmax) +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + dat2d(ilon,ilatix) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + dat2d(ilon,ilat) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_nlists (inp,trkrinfo) +c +c ABSTRACT: This subroutine simply reads in the namelists that are +c created in the shell script. Namelist datein contains the +c starting date information, plus the model identifier. Namelist +c stswitch contains the flags for processing for each storm. +c + USE inparms; USE set_max_parms; USE atcf; USE trkrparms; USE phase + USE structure; USE gfilename_info + USE verbose_output; USE waitfor_parms + + implicit none + + integer ifh + type (datecard) inp + type (trackstuff) trkrinfo +c + namelist/datein/inp + namelist/atcfinfo/atcfnum,atcfname,atcfymdh,atcffreq + namelist/trackerinfo/trkrinfo + namelist/phaseinfo/phaseflag,phasescheme,wcore_depth + namelist/structinfo/structflag,ikeflag + namelist/fnameinfo/gmodname,rundescr,atcfdescr + namelist/verbose/verb + namelist/waitinfo/use_waitfor,wait_min_age,wait_min_size + & ,wait_max_wait,wait_sleeptime + & ,use_per_fcst_command,per_fcst_command + +c Set namelist default values: + use_per_fcst_command='t' + per_fcst_command=' ' + atcffreq=600 + trkrinfo%want_oci=.false. + trkrinfo%gribver=1 ! Set to GRIB1 as default, can be set to + ! something else in the namelist input. + + read (5,NML=datein,END=801) + 801 continue + read (5,NML=atcfinfo,END=807) + 807 continue + print *,'just before trackerinfo read namelist' + read (5,NML=trackerinfo,END=809) + 809 continue + print *,'just after trackerinfo read namelist' + read (5,NML=phaseinfo,END=811) + 811 continue + read (5,NML=structinfo,END=815) + 815 continue + read (5,NML=fnameinfo,END=817) + 817 continue + read (5,NML=verbose,END=819,ERR=833) + 819 continue + read (5,NML=waitinfo,END=821) + 821 continue + goto 837 + 833 continue + verb = 1 + 837 continue + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After datein namelist in trak.f, namelist ' + & ,'parms follow:' + print *,'Forecast initial year = byy = ',inp%byy + print *,'Forecast initial month = bmm = ',inp%bmm + print *,'Forecast initial day = bdd = ',inp%bdd + print *,'Forecast initial hour = bhh = ',inp%bhh + print *,'Forecast model identifier = model= ',inp%model + print *,'Forecast model type = modtyp= ',inp%modtyp + print *,'Forecast model data lead time units= lt_units= ' + & ,inp%lt_units + print *,'Forecast model data sequencing setup= file_seq= ' + & ,inp%file_seq + print *,'Forecast model nest type = ',inp%nesttyp +c + print *,' ' + print *,'Values read in from atcfinfo namelist: ' + write (6,89) atcfnum,atcfname + write (6,90) atcfymdh + write (6,92) atcffreq + 89 format ('ATCF ID = ',i2,' ATCF Name = ',a4) + 90 format ('ATCF date (initial date on output atcf records) = ' + & ,i10) + 92 format ('ATCF output frequency (in hours*100) = atcffreq = ',i6) +c + print *,' ' + print *,'Values read in from trackerinfo namelist follow: ' + write (6,101) ' western boundary = westbd = ',trkrinfo%westbd + write (6,101) ' eastern boundary = eastbd = ',trkrinfo%eastbd + write (6,101) ' northern boundary = northbd = ',trkrinfo%northbd + write (6,101) ' southern boundary = southbd = ',trkrinfo%southbd + write (6,102) ' tracker type = ',trkrinfo%type + write (6,103) ' mslp threshold = mslpthresh = ' + & ,trkrinfo%mslpthresh + write (6,103) ' v850 threshold = v850thresh = ' + & ,trkrinfo%v850thresh + write (6,104) ' model grid type = ',trkrinfo%gridtype + write (6,101) ' Contour interval to be used = ',trkrinfo%contint + write (6,106) ' Flag for whether or not roci will be computed' + & ,' and written out for tracker-type case = ' + & ,trkrinfo%want_oci + write (6,105) ' Flag for whether or not vitals will be written ' + & ,'out = ',trkrinfo%out_vit + write (6,107) ' Flag for which GRIB version (1 or 2) the input' + & ,' data will be in = ',trkrinfo%gribver + write (6,108) ' Flag for input GRIB2 JPDTN (0 or 1) = ' + & ,trkrinfo%g2_jpdtn + 101 format (a31,f7.2) + 102 format (a16,a7) + 103 format (a31,f7.4) + 104 format (a19,a8) + 106 format (a46,a41,L1) + 105 format (a48,a6,a1) + 107 format (a47,a19,i1) + 108 format (a38,i1) + + print *,' ' + print *,'Values read in from phaseinfo namelist: ' + write (6,211) phaseflag,phasescheme + write (6,212) wcore_depth + 211 format ('Storm phase flag = ',a1,' Phase scheme = ',a4) + 212 format ('Storm phase, warm core depth (wcore_depth) = ',f7.2) + + print *,' ' + print *,'Values read in from structinfo namelist: ' + write (6,93) structflag + write (6,95) ikeflag + 93 format ('Structure flag = ',a1) + 95 format ('IKE flag = ',a1) + + print *,' ' + print *,'Values read in for grib file name from fnameinfo' + & ,' namelist: ' + write (6,131) gmodname + write (6,133) rundescr + write (6,135) atcfdescr + 131 format ('Model name description = gmodname = ',a4) + 133 format ('Forecast run description = rundescr = ',a40) + 135 format ('Optional ATCF / Storm name description = atcfdescr = ' + & ,a40) + + print *,' ' + print *,'Value read in from verbose namelist:' + write (6,141) verb + 141 format ('Value read in for verbose flag = verb = ',i2) + + print *,' ' + print *,'Values read in from waitinfo namelist:' + write (6,151) use_waitfor + write (6,152) wait_min_age + write (6,153) wait_min_size + write (6,154) wait_max_wait + write (6,155) wait_sleeptime + if(len_trim(per_fcst_command)>0) then + write (6,156) trim(per_fcst_command) + else +c No command specified, so disable the feature + use_per_fcst_command='n' + endif + 151 format ('Flag for input file waiting = use_waitfor = ',a1) + 152 format ('min age (time in seconds since last mod) = ' + & ,'wait_min_age = ',i8) + 153 format ('min file size in bytes = wait_min_size = ',i12) + 154 format ('max number of seconds to wait for each file = ' + & ,'wait_max_wait = ',i6) + 155 format ('number of seconds to sleep between checks = ' + & ,'wait_sleeptime = ',i6) + 156 format ('command to run after every forecast time = "',A,'"') +c + if (use_waitfor == 'y') then + if (inp%file_seq == 'multi') then + continue + else + print *,' ' + print *,'!!! ERROR: The use_waitfor flag is set to "y".' + print *,' This requires that the inp%file_seq flag be' + print *,' set to "multi", but you have specified ' + print *,' something else. ' + print *,' inp%file_seq = ',inp%file_seq + print *,' STOPPING....' + print *,' ' + STOP 95 + endif + endif +c + endif + return + end +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_fhours (ifhmax) +c +c ABSTRACT: This subroutine reads in a text file that contains the +c forecast times that will be read in. The format of the file is +c in "MMMMM", i.e., minutes, for example, for a forecast going out +c to 120h, the file would look like this: +c +c For reference, here +c are the times that +c match up with the +c minutes on the left: +c +c 1 0 0:00 +c 2 240 4:00 +c 3 270 4:30 +c 4 300 5:00 +c 5 330 5:30 +c 6 360 6:00 +c 7 600 10:00 +c 8 630 10:30 +c 9 660 11:00 +c 10 690 11:30 +c 11 720 12:00 +c 12 960 16:00 +c 13 990 16:30 +c . . . +c . . . +c . . . +c 87 7200 120:00 +c +c Note that we are now allowing for sub-hourly time intervals. +c + USE tracked_parms + USE verbose_output + + implicit none +c + integer, parameter :: iunit_fh=15 + integer itmphrs(750),itmpmins(750),input_mins(750),itmpltix(750) + integer ifhmax,inphr,inpmin,ict,i,ifa,ifma,icma,ira,inpltix,ila + real xminfract + + itmphrs = -99 + itmpmins = -99 + + if (allocated(ifhours)) deallocate (ifhours) + if (allocated(iftotalmins)) deallocate (iftotalmins) + if (allocated(ifclockmins)) deallocate (ifclockmins) + if (allocated(fhreal)) deallocate (fhreal) + if (allocated(ltix)) deallocate (ltix) + + ict = 0 + do while (.true.) + + if ( verb .ge. 3 ) then + print *,'Top of while loop in read_fhours' + endif + + read (iunit_fh,85,end=130) inpltix,inpmin + write (6,85) inpltix,inpmin + + if (inpmin >= 0 .and. inpmin < 150000) then + ict = ict + 1 + itmpltix(ict) = inpltix + itmphrs(ict) = inpmin / 60 + itmpmins(ict) = mod(inpmin,60) + input_mins(ict) = inpmin + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Input minutes not between 0 and 150000' + print *,'!!! inpmin= ',inpmin + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + if ( verb .ge. 3 ) then + print *,'readloop, ict= ',ict,' inpmin= ',inpmin + endif + + enddo + + 130 continue + + ifhmax = ict + + 85 format (i4,1x,i5) + + if ( verb .ge. 3 ) then + print *,' ' + endif + + allocate (ifhours(ifhmax),stat=ifa) + allocate (iftotalmins(ifhmax),stat=ifma) + allocate (ifclockmins(ifhmax),stat=icma) + allocate (fhreal(ifhmax),stat=ira) + allocate (ltix(ifhmax),stat=ila) + if (ifa /= 0 .or. ifma /= 0 .or. icma /= 0 .or. ira /= 0 .or. + & ila /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_fhours allocating either ifhours,' + print *,'!!! iftotalmins, ifclockmins or fhreal.' + print *,'!!! ifa = ',ifa,' ifma= ',ifma,' ira= ',ira + print *,'!!! icma= ',icma,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + do i = 1,ifhmax + + ltix(i) = itmpltix(i) + xminfract = float(itmpmins(i)) / 60. + fhreal(i) = float(itmphrs(i)) + xminfract + ifhours(i) = itmphrs(i) + ifclockmins(i) = itmpmins(i) + iftotalmins(i) = input_mins(i) + + if (i > 1) then + if (fhreal(i) > fhreal(i-1)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: In read_fhours, the time read in ' + print *,'!!! is not greater than the previous time.' + print *,'!!! i= ',i + print *,'!!! fhreal(i)= ',fhreal(i) + print *,'!!! fhreal(i-1)= ',fhreal(i-1) + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + endif + + if ( verb .ge. 3 ) then + write (6,87) i,ltix(i),iftotalmins(i),fhreal(i),ifhours(i) + & ,ifclockmins(i) + endif + + enddo + + 87 format (1x,'i= ',i3,' input lead time index= ',i4,' minutes= ' + & ,i5,' real_lead_time= ',f6.2,' clock_lead_time= ',i3,':' + & ,i2) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + ii=1 + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + maxstorm = numtcv + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that you have the Fortran' + print *,'!!! unit assigned right in your script.' + endif + + iret = 99 + return + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + ii = numtcv + 1 + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1,1x + & ,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo (imax,jmax,ifh,dx,dy,lugb,lugi,trkrinfo + & ,need_to_flip_lats,need_to_flip_lons,inp,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just read the +c grib file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE tracked_parms; USE inparms + USE verbose_output; USE params; USE grib_mod + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + + type(gribfield) :: gfld,prevfld,holdgfld + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1), allocatable :: lb(:) + logical :: unpack=.true. + logical :: open_grb=.false. + CHARACTER(len=8) :: pabbrev + integer,dimension(200) :: jids,jpdt,jgdt + integer, parameter :: jf=40000000 + integer :: listsec1(13) + integer pdt_4p0_vert_level,pdt_4p0_vtime + real xhold,xlondiff,xlatdiff,temp,firstval,lastval + real, allocatable :: f(:) + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer jpds(200),jgds(200),igetpds(200),igetgds(200) + integer, intent(in) :: ifh + integer, intent(out) :: imax,jmax + integer iia,ija,ila,midi,midj,i,j,iix,jix,ifa,iret + integer iscanflag,iggret,kf,k,lugb,lugi,jskp,jdisc + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 + + iggret = 0 + + allocate (lb(jf),stat=ila); allocate (f(jf),stat=ifa) + if (ila /= 0 .or. ifa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating either lb or f' + print *,'!!! ila = ',ila,' ifa= ',ifa + endif + iggret = 97 + return + endif + + if (trkrinfo%gribver == 2) then + + ! Search for a record from a GRIB2 file + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 ! meteorological products + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for Temperature or GP Height by production template.... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + ! Request a record on a lat/lon grid. + + jgdtn = 0 + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpdt(8) = 0 + jpdt(9) = iftotalmins(ifh) + else + jpdt(8) = 1 + jpdt(9) = ifhours(ifh) + endif + + print *,'before getgb2 call, lugb= ',lugb,' lugi= ',lugi + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + if ( iret.ne.0) then + print *,' ' + print *,' ERROR: getgb2 error in getgridinfo = ',iret + print *,' FATAL ERROR: cannot proceed without info ' + print *,' from getgridinfo. STOPPING....' + stop 95 + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if ( verb .ge. 3 ) then + print *,' ' + print *,' -- BEGIN getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb .ge. 3 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'PDT num= gfld%ipdtnum= ',gfld%ipdtnum + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + imax = gfld%igdtmpl(8) + jmax = gfld%igdtmpl(9) + dx = float(gfld%igdtmpl(17))/1.e6 + dy = float(gfld%igdtmpl(17))/1.e6 + kf = gfld%ngrdpts + + holdgfld = gfld + + if (verb .ge. 3) then + + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ndpts,firstval,lastval +cJ.Peng---& ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + + print *,' ' + print *,' -- END getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' ' + print *,' ' + + endif + + need_to_flip_lons = .false. + + iscanflag = gfld%igdtmpl(19) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(gfld%igdtmpl(12))/1.e6 + glatmax = float(gfld%igdtmpl(15))/1.e6 + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(gfld%igdtmpl(15))/1.e6 + glatmax = float(gfld%igdtmpl(12))/1.e6 + need_to_flip_lats = .false. + endif + + glonmin = float(gfld%igdtmpl(13))/1.e6 + glonmax = float(gfld%igdtmpl(16))/1.e6 + + print *,'TEST getgridinfo: glatmin= ',glatmin + print *,'TEST getgridinfo: glatmax= ',glatmax + print *,'TEST getgridinfo: glonmin= ',glonmin + print *,'TEST getgridinfo: glonmax= ',glonmax + + else + + ! Search for a record from a GRIB1 file + + jpds = -1 + jgds = -1 + + jgds(1) = 0 ! Request a record that's on a lat/lon grid + + if ( verb .ge. 3 ) then + print *,'before getgb in getgridinfo, ifh= ',ifh + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* Forecast hour: ',i4,':',i2.2) + print *,' ifhours(ifh)= ',ifhours(ifh) + print *,' iftotalmins(ifh)= ',iftotalmins(ifh) + endif + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + j=0 + + call getgb(lugb,lugi,jf,j,jpds,jgds, + & kf,k,igetpds,igetgds,lb,f,iret) + + if (iret.ne.0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo calling getgb' + print *,'!!! Return code from getgb = iret = ',iret + endif + + iggret = iret + else + iggret=0 + imax = igetgds(2) + jmax = igetgds(3) + dx = float(igetgds(9))/1000. + dy = float(igetgds(10))/1000. + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,' dx= ',dx,' dy= ',dy + endif + + +c ---------------------------------------------------------------- +c Get boundaries of the data grid. NOTE: gds(4) is referred to in +c GRIB documenatation as the "Latitude of origin", which might +c imply "minimum Latitude". However, for the grids that we'll be +c using in this program, the "Latitude of origin" will be listed +c under gds(4) as the northernmost point (eg., in MRF, +c gds(4) = 90), so for this program, use gds(4) as your max lat, +c and gds(7) as your min lat. However, in case NCEP, UKMET or +c ECMWF change their convention and begin flipping their grids, a +c check is made to make sure that the max lat is not less than the +c min lat. +c +c BUGFIX (August, 2001): It is possible to have an input grid +c which goes from south to north (such as NOGAPS). In this case, +c we flip the data in subroutine conv1d2d_real. However, the max +c and min latitudes listed in the GRIB GDS will be confused, so we +c need to check the value of the GRIB scanning mode flag here. + + need_to_flip_lons = .false. + + iscanflag = igetgds(11) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(igetgds(4))/1000. + glatmax = float(igetgds(7))/1000. + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(igetgds(7))/1000. + glatmax = float(igetgds(4))/1000. + need_to_flip_lats = .false. + endif + + glonmin = float(igetgds(5))/1000. + glonmax = float(igetgds(8))/1000. + + endif + +c After this point in this subroutine, nothing is GRIB1 / GRIB2 +c specific, so it does not need to be within the if/then +c statement above that differentiated between GRIB / GRIB2. + + if (glonmin < 0.0) glonmin = 360. - abs(glonmin) + if (glonmax < 0.0) glonmax = 360. - abs(glonmax) + + if (glonmin < 0.0) then + glonmin = 360. - abs(glonmin) + if (glonmax <= 0.0) then + glonmax = 360. - abs(glonmax) + else + glonmax = 360 + abs(glonmax) + endif + endif + + if (glatmax < glatmin) then + temp = glatmax + glatmax = glatmin + glatmin = temp + endif + + if (glonmin > 200.0 .and. glonmin <= 360.) then + if (glonmax < 50.) then + ! Likely GM-wrapping in current record + glonmax = glonmax + 360. + endif + endif +c + if ( verb .ge. 3 ) then + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + print *,' ' + print *,'NOTE: For regional grids, valid data points might' + print *,'NOT extend all the way to the gds-defined grid ' + print *,'boundary, due to the fact that data have been ' + print *,'interpolated from a NPS or Lamb-Conf grid onto a ' + print *,'lat/lon grid. This program checks the logical ' + print *,'bitmap for valid data points, but just keep this in' + print *,'mind if trying to debug errors that occur near the' + print *,'grid boundaries for regional models.' + endif + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glat)) deallocate(glat) + if (allocated(glon)) deallocate(glon) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + endif + + iggret = 96 + return + endif + + do j=1,jmax + glat(j) = glatmax - (j-1)*dy + enddo + do i=1,imax + glon(i) = glonmin + (i-1)*dx + enddo + + deallocate (lb); deallocate(f) + +c -------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have +c forgotten to change the input grid bounds from a global grid +c run). Modify the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_valid_point (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) +c +c ABSTRACT: This subroutine checks to see if the input lat/lon +c point is associated with four surrounding (i,j) locations that +c have valid data. The writing of this routine was prompted by the +c HFIP project in February, 2009. Some of their high resolution +c data for their inner nests contained grids that had been rotated +c from native map projections to regular lat/lon grids, but that +c rotation left "empty" spots on the lat/lon grid where there is +c no data. Then when searching in find_maxmin, we were running +c barnes iterations from these lat/lon locations where there was +c no data, which would give artificially low values at those +c lat/lon locations (because the barnes scheme would only include +c points that were relatively far away where there was valid data). +c So in this routine, we call subroutine fix_latlon_to_ij in order +c to get the nearest (i,j) coordinates, and then we check all of +c these points to make sure that valid data exist. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing in i-direction +c dy grid spacing in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c rlatt,rlont input lat/lon about which we will check the +c surrounding (i,j) locations for valid data. +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c icvpret return code from this routine. A value of 0 means that +c all is okay and the input point is surrounded by valid +c data. + + USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,ifix,jfix + integer ifilret,icvpret + character(*) cmaxmin + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real rlont,rlatt,xdum,gridpoint_maxmin + real dx,dy,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +c + call fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt + & ,xdum,ifix,jfix,gridpoint_maxmin,'checker' + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) + + if (ifilret /= 0) then + icvpret = 99 + return + endif + + if (valid_pt(ifix,jfix)) then + icvpret = 0 + else + icvpret = 99 + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.10) then + grfact = 4 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif + + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (1*grfact) + iend = ipfix + (2*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (2*grfact) + iend = ipfix + (1*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (1*grfact) + iend = ipfix + (1*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (2*grfact) + jend = jpfix + (1*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (1*grfact) + jend = jpfix + (2*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (1*grfact) + jend = jpfix + (1*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +c print *,' End of fix_latlon_to_ij, gridpoint_maxmin = ' +c & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine rvcal (imax,jmax,dlon,dlat,z,vp) +c +c ABSTRACT: This routine calculates the relative vorticity (zeta) +c from u,v on an evenly-spaced lat/lon grid. Centered finite +c differences are used on the interior points and one-sided +c differences are used on the boundaries. +c +c NOTE: There are 3 critical arrays in this subroutine, the first +c being zeta and the 2nd and 3rd being u and v. There is a +c critical difference in the array indexing for the levels. For +c zeta, the array is dimensioned with levels from 1 to 3, with +c 1 = 850, 2 = 700, 3 = sfc. However, there is an extra level +c for the winds, such that the level dimension goes 1 = 850, +c 2 = 700, 3 = 500, 4 = sfc. So we need to adjust for that in +c this routine. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE trig_vals; USE grid_bounds + USE verbose_output + + implicit none + + dimension cosfac(jmax),tanfac(jmax) + real tmpzeta(imax,jmax) + real xlondiff,xlatdiff,dlon,dlat,dfix + real dlat_edge,dlat_inter,dlon_edge,dlon_inter + real rlat(jmax),cosfac,tanfac + integer z,iscanflag,nlat,nlon,i,j,imax,jmax,w + integer ii,jj + logical(1) vp(imax,jmax) + +c -------------------------- + +c Figure out what level of data we have and what the array +c indices should be. + + if (z == 1) then + ! z = 1 for 850 mb zeta, w = 1 for 850 mb winds + w = 1 + else if (z == 2) then + ! z = 2 for 700 mb zeta, w = 2 for 700 mb winds + w = 2 + else if (z == 3) then + ! z = 3 for sfc zeta, w = 4 for sfc (10m) winds + w = 4 + endif + +c Calculate grid increments for interior and edge points. + +c IMPORTANT: If dtk is defined in module trig_vals in km, then +c we need to multiply by 1000 here to get meters. If it's defined +c as meters, just let it be. Since the wind values are given in +c meters, that's why we need the dlon values to be in meters. + + if (dtk < 750.) then ! chances are, dtk was defined as km + dfix = 1000.0 + else ! dtk was already defined as meters + dfix = 1.0 + endif + + dlon_edge = dtk * dfix * dlon ! Di dist over 1 grid pt + dlat_edge = dtk * dfix * dlat ! Dj dist over 1 grid pt + dlon_inter = dtk * dfix * 2.0 * dlon ! Di dist over 2 grid pts + dlat_inter = dtk * dfix * 2.0 * dlat ! Dj dist over 2 grid pts + + +c Calculate required trig functions. These are functions of +c latitude. Remember that the grid must go from north to south. +c This north-to-south requirement has +c already been checked in subroutine getgridinfo. If necessary, +c any flipping of the latitudes was done there, and flipping of +c the data, again if necessary, was done in subroutine getdata. + + do j=2,jmax-1 + rlat(j) = glatmax - ((j-1) * dlat) + cosfac(j) = cos(dtr*rlat(j)) + tanfac(j) = (tan(dtr*rlat(j)))/erad + enddo + +c Set trig factors at end points to closest interior point +c to avoid a singularity if the domain includes the poles, +c which it will for the global grids (MRF, GDAS, GFS, UKMET,NCE) + + cosfac(1) = cosfac(2) + tanfac(1) = tanfac(2) + cosfac(jmax) = cosfac(jmax-1) + tanfac(jmax) = tanfac(jmax-1) + +c NOTE: These next bits of vorticity calculation code assume that +c the input grid is oriented so that point (1,1) is the upper +c left-most (NW) and point (imax,jmax) is the lower right- +c most point. Any other grids will probably crash the +c program due to array out of bounds errors. +c NOTE: Before each calculation is done, the logical array is +c checked to make sure that all the data points in this +c calculation have valid data (ie., that the points are not +c outside a regional model's boundaries). +c +c !!! IMPORTANT NOTE: While testing this, I uncovered a bug, which was +c that I had the "j+1" and "j-1" reversed. Just from a physical +c understanding, the du/dy term at a point is calculated by taking +c the u value north of the point minus the u value south of the +c point. Intuitively, this is u(j+1) - u(j-1). However, we have +c designed this program to have the northernmost point as +c the beginning of the grid (i.e., for the global grids, j=1 at 90N, +c and j increases southward). Thus, if you would do u(j+1) - +c u(j-1), you would actually be taking the u value south of the +c point minus the u value north of the point, EXACTLY THE OPPOSITE +c OF WHAT YOU WANT. Therefore, the vorticity calculations have +c been changed so that we now have u(j-1) - u(j+1). +c +c UPDATE FEB 2009: With limited domain grids that have missing +c data on them (such as you would have for a grid that has been +c converted from a non-lat/lon grid to a lat/lon grid), we were +c running into problems below with the setting of zeta values to +c a missing value of -999. In place of this, the easiest thing to +c do is to simply assign a value of the background coriolis value +c to that point. No, this is not correct, but it is the easiest +c workaround for this right now. Setting it to zero would be too +c far off. Setting it to the coriolis component has a net effect +c of not having much impact on the barnes scheme result. +c +c --------------- +c Interior points +c --------------- + + if ( verb .ge. 3 ) then + print *,'Just before inter rvcalc, dlon_inter = ',dlon_inter + & ,' dlat_inter = ',dlat_inter + endif + + do j=2,jmax-1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1) .and. vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo + enddo +c +c ----------------------------- +c Bottom (Southernmost) points +c ----------------------------- +c + j=jmax + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------- +c Top (Northernmost) points +c -------------------------- +c + j=1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c ------------------------------- +c Left edge (Westernmost) points +c ------------------------------- +c + i=1 + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------------- +c Right edge (Easternmost) points +c -------------------------------- +c + i=imax + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c --------- +c SW corner +c --------- + i=1 + j=jmax + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i+1,j,w)-v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NW corner +c --------- + i=1 + j=1 + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NE corner +c --------- + i=imax + j=1 + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c SE corner +c --------- + i=imax + j=jmax + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i,j,w)-v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + do ii=1,imax + do jj=1,jmax + tmpzeta(ii,jj) = zeta(ii,jj,z) * 1.e5 + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine first_ges_center (imax,jmax,dx,dy,cparm,fxy + & ,cmaxmin,trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) +c +c ABSTRACT: This subroutine scans an array and picks out areas of +c max or min, then loads those center positions into the first- +c guess lat & lon arrays to be used by subroutine tracker for +c locating the very specific low center positions. +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c dx Grid spacing in i-direction for the input grid +c dy Grid spacing in j-direction for the input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c finf Logical. Field of influence. Dimension same as fxy +c cmaxmin Char string to indicate if search is for a max or a min +c trkrinfo Derived type that holds/describes various tracker parms, +c including the contour interval to be used +c ifh Index for the forecast hour +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c contour_info Type cint_stuff from module contours. Contains +c contour information +c +c OUTPUT: +c maxmini Integer array containing i-indeces of max/min locations +c maxminj Integer array containing j-indeces of max/min locations +c ifgcret return code from this subroutine +c +c OTHER: +c storm Contains the tcvitals for the storms (module def_vitals) + + USE trkrparms; USE grid_bounds; USE set_max_parms; USE def_vitals + USE contours; USE tracked_parms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,n,isstart,ifamret,ibeg,jbeg,iend,jend + integer ifh,maxstorm,imax,jmax,itemp,ifgcret + integer stormct,oldstormct + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + character(*) cparm,cmaxmin + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real dmax,dmin,dx,dy,dbuffer,tmp + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of first_ges_center *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for new lows at hour ',i4,':',i2.2) + print *,'*-------------------------------------------------*' + endif + + +c First check the user-supplied grid boundaries to see if we will +c scan the entire array or just a portion of it. + + if (trkrinfo%northbd < -998.0 .or. trkrinfo%southbd < -998.0 .or. + & trkrinfo%westbd < -998.0 .or. trkrinfo%eastbd < -998.0) then + ! User did not specify a subgrid, so scan the whole domain + ibeg = 1 + iend = imax + jbeg = 1 + jend = jmax + else + +c if (trkrinfo%westbd > 360.0 .or. trkrinfo%eastbd < 0.0 .or. +c & trkrinfo%westbd < 0.0 .or. + + if (trkrinfo%westbd > 360.0 .or. + & trkrinfo%northbd > 90.0 .or. trkrinfo%northbd <-90.0 .or. + & trkrinfo%southbd > 90.0 .or. trkrinfo%southbd <-90.0 .or. + & trkrinfo%westbd >= trkrinfo%eastbd .or. + & trkrinfo%southbd >= trkrinfo%northbd) then + + if (trkrinfo%westbd > trkrinfo%eastbd) then + + if (trkrinfo%westbd < 360.0 .and. + & trkrinfo%eastbd >= 0.0)then + + ! In this special case, the user has specified that the + ! western boundary be to the west of the Greenwich + ! meridian and the eastern boundary be to the east of it. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE: The user supplied grid lon boundaries' + print *,'++ span across the Greenwich meridian.' + print *,'++ ' + print *,'++ Western boundary: ',trkrinfo%westbd + print *,'++ Eastern boundary: ',trkrinfo%eastbd + print *,'++ Northern boundary: ',trkrinfo%northbd + print *,'++ Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ! Calculate the beginning and ending i and j points for + ! this case of spanning the Greenwich meridian. The + ! beginning and ending j points are, obviously, the same + ! as for the regular case below in the else. The + ! i-beginning point will also be the same as for the + ! regular case. However, the i-ending point will be + ! modified for the meridian wrap; it will be > imax. + + jbeg = int(((glatmax + dy - trkrinfo%northbd) + & / dy) + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) + & / dy) + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) + & / dx) + 0.5) +c iend = int(((trkrinfo%eastbd - glonmin + dx) +c & / dx) + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) + & / dx) + 0.5) + imax + + goto 377 + + endif + endif + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. There is a' + print *,'!!! problem with the user-supplied grid ' + print *,'!!! boundaries. Please check them and ' + print *,'!!! resubmit the program.' + print *,'!!!' + print *,'!!! Western boundary: ',trkrinfo%westbd + print *,'!!! Eastern boundary: ',trkrinfo%eastbd + print *,'!!! Northern boundary: ',trkrinfo%northbd + print *,'!!! Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ifgcret = 91 + return + + 377 continue + + else + ! Calculate the beginning and ending i and j points.... + jbeg = int(((glatmax + dy - trkrinfo%northbd) / dy) + & + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) / dy) + & + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) / dx) + & + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) / dx) + & + 0.5) + endif + endif + +c Scan the requested portion of the grid and pick out the max and +c min data values, figure out what the max and min contour levels +c will be, and fill an array with the values of the various +c intermediate, incremental contour levels. + + if (trkrinfo%contint <= 0) then + + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. For a midlat' + print *,'!!! or tcgen run of the tracker, the contour' + print *,'!!! interval supplied by the user is not ' + print *,'!!! greater than 0.' + print *,'!!! ' + print *,'!!! User-supplied contint = ',trkrinfo%contint + print *,' ' + endif + + ifgcret = 91 + return + endif + + dmin = 9.99e20 + dmax = -9.99e20 + + do j = jbeg,jend + do i = ibeg,iend + if (i > imax) then + itemp = i - imax ! If wrapping past GM + else + itemp = i + endif + if (valid_pt(itemp,j)) then + if (fxy(itemp,j) < dmin) dmin = fxy(itemp,j) + if (fxy(itemp,j) > dmax) dmax = fxy(itemp,j) + endif + enddo + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*--------------------------------------------*' + print *,'In first_ges_center, dmin= ',dmin,' dmax= ',dmax + endif + + +c We want to allow for storms moving out of the sub-region, +c in which case we might hit slightly lower or higher +c contours than were found in the sub-region, so allow for +c an extra buffer and modify dmin and dmax.... + + dbuffer = (dmax - dmin) / 2.0 + dmax = dmax + dbuffer + dmin = dmin - dbuffer + + if ( verb .ge. 3 ) then + print *,'after adjustment, dmin= ',dmin,' dmax= ',dmax + endif + +c Next 2 lines changed for compiler compatibility on +c other platforms.... +c contour_info%xmaxcont = dmax - amod(dmax,trkrinfo%contint) +c contour_info%xmincont = dmin - amod(dmin,trkrinfo%contint) + + tmp = trkrinfo%contint + contour_info%xmaxcont = dmax - mod(dmax,tmp) + contour_info%xmincont = dmin - mod(dmin,tmp) + + if ( verb .ge. 3 ) then + print *,'A1 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A1 contour_info%xmincont= ',contour_info%xmincont + endif + + if (contour_info%xmincont > contour_info%xmaxcont) then + contour_info%xmincont = contour_info%xmaxcont + endif + +c if (dmin > contour_info%xmincont) then +c contour_info%xmincont=contour_info%xmincont + trkrinfo%contint +c endif +c if (dmax < contour_info%xmaxcont) then +c contour_info%xmaxcont=contour_info%xmaxcont - trkrinfo%contint +c endif + + if ( verb .ge. 3 ) then + print *,'A2 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A2 contour_info%xmincont= ',contour_info%xmincont + print *,'maxconts= ',maxconts + endif + +c NOTE: In the loop below, the contour_info%contvals array is now +c (5/2003) no longer used in subsequent subroutines. But we still +c need to figure out the value of the contvals as we iterate the +c loop so we can know when we've surpassed dmax and can stop +c incrementing contour_info%numcont, which we do need in subsequent +c subroutines. + + contour_info%numcont = 0 + do n = 1,maxconts + contour_info%numcont = contour_info%numcont + 1 + contour_info%contvals(n) = contour_info%xmincont + + & float(n-1)*trkrinfo%contint +c print *,'n= ',n,' contour_info%contvals(n)= ' +c & ,contour_info%contvals(n) + if (contour_info%contvals(n) >= dmax) exit + enddo + + oldstormct = stormct + call find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) + + if (stormct > 0) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' ' + print *,'!!! ************************************************' + print *,'!!! ' + print *,'!!! NOTE: In first_ges_center, the value of stormct' + print *,'!!! returned from find_all_maxmins is not greater' + print *,'!!! than 0. This means there are no new centers' + print *,'!!! to track, which is not likely. Perhaps you are' + print *,'!!! searching over too small of an area??' + print *,'!!! ' + print *,'!!! ************************************************' + print *,' ' + endif + + endif + + if (stormct > oldstormct .and. stormct > 0) then + isstart = oldstormct + 1 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,*) 'New search: ' + write (6,*) 'Possible new max/min locations at ifh= ',ifh + write (6,*) '--------------------------------------------' + endif + + do n = isstart,stormct + if (trkrinfo%type == 'midlat') then + storm(n)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(n)%tcv_center = 'TCG ' + endif + slonfg(n,ifh) = glonmin + (maxmini(n)-1)*dx + slatfg(n,ifh) = glatmax - (maxminj(n)-1)*dy + storm(n)%tcv_stspd = -99 + storm(n)%tcv_stdir = -99 + write (storm(n)%tcv_storm_id,'(i4.4)') n + write (storm(n)%tcv_storm_name,'(i4.4)') n + stormswitch(n) = 1 + if (cparm == 'mslp') then + + if ( verb .ge. 3 ) then + write (6,71) maxmini(n),maxminj(n),slonfg(n,ifh) + & ,360.-slonfg(n,ifh),slatfg(n,ifh) + & ,slp(maxmini(n),maxminj(n))/100.0 + endif + + endif + enddo + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' New search: ' + print *,'!!! NOTE: No new storms found in find_all_maxmins' + print *,'!!! at ifh = ',ifh,' stormct= ',stormct + print *,'!!! oldstormct= ',oldstormct + print *,' ' + endif + + endif + + 71 format (1x,'i= ',i3,' j= ',i3,' lon: ',f7.2,'E (',f6.2,'W)' + & ,2x,' lat: ',f6.2,' mslp: ',f6.1,' mb') +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) +c +c ABSTRACT: This subroutine will search an area delineated by +c input i and j indeces in order to find all local maxes or mins +c in that area. The (i,j) locations of the maxes/mins are returned +c in the maxmini and maxminj arrays. The input 3-character string +c cmaxmin will tell the subroutine to look for a "max" or a "min". +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c ibeg i-index for upper left location of grid to search +c iend i-index for lower right location of grid to search +c jbeg j-index for upper left location of grid to search +c jend j-index for lower right location of grid to search +c fxy Real array of data values +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c contour_info Type cint_stuff from module contours containing the +c the following 4 variables: +c 1. xmincont Real value for min contour level in the fxy data array +c 2. xmaxcont Real value for max contour level in the fxy data array +c 3. contvals Real array holding values of cont levels at this time +c 4. numcont Number of contour intervals found at this time +c trkrinfo derived type containing various user-input tracker parms +c cmaxmin String that declares if "min" or "max" is being searched +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c +c OUTPUT: +c maxmini integer array containing i-indeces of the max/min points +c maxminj integer array containing j-indeces of the max/min points +c ifamret return code from this subroutine + + USE trkrparms; USE set_max_parms; USE contours + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + integer stormct,i,j,ibeg,iend,jbeg,jend,ix,jx,ixp1,ixm1 + integer ip,jp,maxstorm,jxp1,jxm1,ifamret,isret,iaret + integer isoiret,icccret,igicwret,imax,jmax + character ccflag*1,get_last_isobar_flag*1 + character(*) cmaxmin + logical(1) still_finding_valid_maxmins,rough_gradient_check_okay + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real xavg,stdv,search_cutoff,dmin,dmax,sphere_cutoff + real plastbar,rlastbar +c----- + still_finding_valid_maxmins = .true. + + +c print *,'ctm beg of find_all_maxmins, maxstorm= ',maxstorm + + +c First, we want to get the mean and standard deviation of the input +c field to be searched. We can use the standard deviation info as +c part of our guideline for when to stop searching for maxes & mins. +c We will set the search cut-off threshold at 1/2 standard deviation +c above the mean for min searches. So, for the example of mslp, if +c the mean pressure over the whole domain is 1010 mb and the +c standard deviation is 12 mb, then when we are searching, if the +c lowest available (i.e., hasn't been found in a previous iteration +c of this loop) pressure is 1016, then it's time to stop searching. + + call avgcalc (fxy,imax*jmax,valid_pt,xavg,iaret) + call stdevcalc (fxy,imax*jmax,valid_pt,xavg,stdv,isret) + if (iaret /= 0 .or. isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_all_maxmins, the calls to avgcalc' + print *,'!!! and/or stdevcalc returned an error.' + print *,'!!! iaret= ',iaret,' isret= ',iaret + print *,' ' + endif + + ifamret = 98 + return + endif + + if (cmaxmin == 'min') then + search_cutoff = xavg + stdv*0.5 + else + search_cutoff = xavg - stdv*0.5 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In find_all_maxmins, search_cutoff= ',search_cutoff + print *,' ' + endif + +c Now begin to search the domain. We do a simple gridpoint scan, +c and once we find the max/min value, we pass the (i,j) coordinates +c at that point to a routine to check for a closed contour. Then +c we mask out those points in the contour (or, if there is not a +c closed contour, just the 8 points immediately surrounding the low +c center) and we do another iteration of search_loop to look for +c more lows. We mask out points we've found so that on subsequent +c iterations of search_loop, we don't find the same old center +c again and again and again..... + + search_loop: do while (still_finding_valid_maxmins) + + dmin = 9.99e20 + dmax = -9.99e20 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + ip = i + jp = j + + if (ip > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: In find_all_maxmins, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. The search' + print *,'!!! will not extend to the user-requested' + print *,'!!! grid boundary.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',ip + print *,' ' + endif + + exit iloop + + endif + endif + + if (valid_pt(ip,jp) .and..not. masked_out(ip,jp)) then + if (cmaxmin == 'min') then + if (fxy(ip,jp) < dmin) then + dmin = fxy(ip,jp) + ix = ip + jx = jp + endif + else + if (fxy(ip,jp) > dmax) then + dmax = fxy(ip,jp) + ix = ip + jx = jp + endif + endif + endif + + enddo iloop + enddo jloop + + if (cmaxmin == 'min') then + if (dmin < search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + else + if (dmax > search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + endif + +c As a rough first check, see if the neighboring points on all +c 4 sides have a gradient sloping down into the found min point, +c or at least that there is a flat field not having a gradient +c sloping away from the center point. + + call get_ijplus1_check_wrap (imax,jmax,ix,jx,ixp1,jxp1,ixm1 + & ,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In find_all_maxmins, the center we found' + print *,'!!! is too close to the grid boundary and will' + print *,'!!! NOT be checked for a closed contour.' + print *,'!!! ix= ',ix,' jx= ',jx,' fxy= ',fxy(ix,jx) + print *,'!!! ' + print *,' ' + endif + + masked_out(ix,jx) = .true. + cycle search_loop + endif + + if (cmaxmin == 'min') then + if (fxy(ix,jx) <= fxy(ixp1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxm1) .and. + & fxy(ix,jx) <= fxy(ixm1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + else + if (fxy(ix,jx) >= fxy(ixp1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxm1) .and. + & fxy(ix,jx) >= fxy(ixm1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + endif + + if (rough_gradient_check_okay) then + + if ( verb .ge. 3 ) then + print *,'Found a possible max/min at ix= ',ix,' jx= ',jx + endif + + +c From this rough check, we appear to have a gradient sloping +c in towards the center point. Now call the subroutine to +c check whether or not there is in fact a closed contour +c surrounding this local maximum or minimum. + + get_last_isobar_flag = 'n' + ccflag = 'n' + call check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,ccflag,cmaxmin,trkrinfo + & ,1,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if (ccflag == 'y') then + if (stormct < maxstorm) then + stormct = stormct + 1 + + if ( verb .ge. 3 ) then + print *,'AAA stormct= ',stormct,' ix= ',ix,' jx= ',jx + endif + + maxmini(stormct) = ix + maxminj(stormct) = jx + else + + if ( verb .ge. 3 ) then + print *,'---max stormct reached, stormct= ', stormct + endif + + endif + else + + if ( verb .ge. 3 ) then + print *,'!!! contour check negative, ccflag= ',ccflag + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*-----------------------------------------------*' + print *,' ' + endif + + endif + +c Regardless of whether or not the found point turns out to have +c a closed contour, we don't want to find this local minimum or +c its 8 surrounding points again in a search on a subsequent +c iteration of this loop. + + masked_out(ix,jx) = .true. + masked_out(ix,jxp1) = .true. + masked_out(ixp1,jxp1) = .true. + masked_out(ixp1,jx) = .true. + masked_out(ixp1,jxm1) = .true. + masked_out(ix,jxm1) = .true. + masked_out(ixm1,jxm1) = .true. + masked_out(ixm1,jx) = .true. + masked_out(ixm1,jxp1) = .true. + + enddo search_loop + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,closed_contour,cmaxmin,trkrinfo + & ,num_requested_contours,contour_info + & ,get_last_isobar_flag,plastbar,rlastbar,icccret) +c +c ABSTRACT: This subroutine checks a field of data around an input +c (ix,jx) data point to see if a closed contour exists around +c that data point. It can check for a closed contour on a max or a +c min field, depending on the value of the input variable 'cmaxmin'. +c The algorithm works by examining rings of the 8 data points +c surrounding a data point that is in the contour interval. For +c example, in the diagram below, the X represents the location of +c the local minimum value which was passed into this routine with +c the coordinates (ix,jx), let's say it's 985 mb. And let's assume +c that the data values at points A-I are all in the 4 mb contour +c interval of 985-989 mb, and that all the surrounding points have +c data values >= 989. To test for a closed contour, we first check +c the ring of 8 points immediately around point X to see what their +c data values are. If a data value is found that is below the +c lower limit of this contour interval (985 mb) or lower than the +c local minimum value at the X point that we initially targeted +c (985 mb), then we do NOT have a closed contour, and we exit this +c subroutine. But in our example, that's not the case, and we have +c 5 points (B,D,E,F,G) that are in the interval. So in our next +c iteration of the loop, we set up 5 rings, each one set up around +c the points found in the first iteration (B,D,E,F,G), and we check +c the 8 points around each of those points. A logical array is +c used so that as soon as a point is found, it is flagged as being +c found. In this way, when we look at the ring around point D, for +c example, we won't pick point X again and set up another ring +c around it in the next ring iteration and end up in an infinite +c loop, going back and forth between point X and point D. While +c checking the 8 points in a ring, if a found data value is above +c our contour interval (i.e., >= 989 mb), we just ignore the +c point; we only mark points that are in our contour interval, +c and again, if we find a point below our contour interval, we +c exit the subroutine with a flag indicating a closed contour was +c NOT found. So in this method, we keep spreading out from the +c initial local minimum and creating and checking new rings until +c we either: (a) Hit the edge of the regional grid, in which case +c we consider a closed contour NOT found, (b) Run into a data +c point that has been marked as being under the influence of +c another nearby low, in which case we consider a closed contour +c NOT found, (c) Run into a point which is below (above) our +c contour interval for a min (max) check, in which case we +c consider a closed contour NOT found, or (d) we run out of +c points to keep searching, we have no rings left to create and +c check because all of the surrounding points are above (below) +c our contour interval for a min (max) check, and by default we +c consider this a closed contour and return to the calling +c subroutine a flag indicating such. +c +c + + + + + + + + + + +c + + + + + + + + + + +c + + A B + + + + + + +c + + C D X E + + + + +c + + + + F G + + + + +c + + + + + H I + + + +c + + + + + + + + + + +c + + + + + + + + + + +c +c UPDATE: This subroutine was updated to keep searching for +c multiple closed contours until it can't find anymore. The +c input parameter num_requested_contours dictates how many +c contours to search for. In the case of just trying to roughly +c locate new centers and establish that there is a closed +c circulation, num_requested_contours will = 1, and we will exit +c after finding that 1 contour. But for a check after making a +c full center fix, we set num_requested_contours = 999 so that +c we can keep searching for all closed contours around the low. +c In this 999 case, you will eventually get to a point where +c there is no closed contour. In that case, in the standard +c output you will see a message telling you that you hit a point +c that is not in the contour and that there is no closed contour, +c but you will also notice that the ccflag = y, meaning there is +c a closed contour (because you have found at least 1 closed +c contour along the way). The reason to keep searching for more +c closed contours is that we can then return the value of the +c outermost closed isobar. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c cmaxmin character string ('max' or 'min') that tells this +c routine what we're looking for. +c trkrinfo derived type that holds/describes various tracker parms +c contour_info Type cint_stuff from module contours. Contains +c contour information +c num_requested_contours For the simple first_ges_center check, +c this will be 1 (we just want to know if there's at +c least 1 closed contour). For the verifying check after +c we've found a center, this will be 9999 (i.e., just keep +c searching for more contours) +c get_last_isobar_flag character ('y' or 'n') to indicate whether +c or not to report on the value of the last closed isobar +c and the radius of the last closed isobar. +c +c OUTPUT: +c closed_contour character; A returned value of 'y' indicates that +c this routine was able to find a closed contour. +c plastbar Contains the value of the last closed isobar (unrounded) +c rlastbar Contains the mean radius of the last closed isobar +c +c LOCAL: +c num_pts_in_all_contours Counter for the number of pts inside of +c the contour we're looking at +c next_ring_ct Counter for the number of points that have been +c tagged to be used as center points for the next +c iteration of multiple_ring_loop. +c next_contour_ct Counter for the number of points that have been +c tagged to be used as center points in the first iteration +c through single_contour_scan_loop as we begin to scan +c points in the *next* contour interval. This counter gets +c incremented when, for example, we are searching points +c around a current center point and we find one that is not +c in our current interval, but rather is in the next +c interval. We want to remember this point and store the +c location, so we increment this counter and store the +c location in next_contour_i and next_contour_j arrays. +c beyond_contour_ct Counter for the number of points that have been +c tagged to be used as center points for some subsequent +c iteration of successive_contours_loop. This is +c different from next_contour_ct, which is used to hold +c the locations of points that are definitely in the +c *next* contour interval. Here, we have points that we +c just store in a pool of potential points to be searched +c in future iterations. These points can come about in +c cases where there is a very intense, very compact low +c with a tight pressure gradient, such that multiple +c contour intervals could be spanned in between 2 adjacent +c gridpoints (this is especially the case if the contour +c interval you have chosen is small). You need to be +c careful with how you handle this array. Once you find +c that you have searchable points in next_contour_i or +c next_contour_j, do not just simply empty out this +c beyond_contour count and its i and j arrays. The +c reason being that some of these "beyond" points may end +c up being used and searched in subsequent iterations, but +c not if we just delete them now. + + + USE set_max_parms; USE trkrparms; USE contours; USE grid_bounds + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,ir,iria,irja,irx,jrx,ix,jx,imax,jmax + integer nb,ibx,jby,nct,iflip + integer mr,ringct,ixp1,ixm1,jxp1,jxm1,nring,iter + integer icenx,jcenx,icccret,next_ring_ct,igicwret + integer num_pts_in_all_contours,next_contour_ct + integer beyond_contour_ct + integer num_pts_in_one_contour + integer num_requested_contours,num_found_contours + integer nm,im,jm,inall,insingle,isc_count,rlast_distct + character found_a_point_in_our_contour*1,closed_contour*1 + character found_a_point_below_contour*1 + character found_a_point_above_contour*1,get_last_isobar_flag*1 + character(*) cmaxmin + logical(1) still_scanning + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + logical(1) point_is_already_in_our_contour(imax,jmax) + logical(1) point_is_already_in_next_contour(imax,jmax) + logical(1) point_is_already_in_beyond_pool(imax,jmax) + integer isni,isnj,inci,incj,ibci,ibcj,ihmi,ihmj,itmi,itmj + integer, allocatable :: search_next_i(:) + integer, allocatable :: search_next_j(:) + integer, allocatable :: next_contour_i(:) + integer, allocatable :: next_contour_j(:) + integer, allocatable :: beyond_contour_i(:) + integer, allocatable :: beyond_contour_j(:) + integer, allocatable :: hold_mask_i_loc(:) + integer, allocatable :: hold_mask_j_loc(:) + integer, allocatable :: temp_mask_i_loc(:) + integer, allocatable :: temp_mask_j_loc(:) + integer, allocatable :: ringposi(:),ringposj(:) + real,allocatable :: ringpos(:,:) + real fxy(imax,jmax),contvals(maxconts) + real contlo,conthi,xcentval,contlo_next,conthi_next + real dist,degrees,rlast_distsum,plastbar,rlastbar +c + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + allocate (search_next_i(imax*jmax),stat=isni) + allocate (search_next_j(imax*jmax),stat=isnj) + allocate (next_contour_i(imax*jmax),stat=inci) + allocate (next_contour_j(imax*jmax),stat=incj) + allocate (beyond_contour_i((imax*jmax)/2),stat=ibci) + allocate (beyond_contour_j((imax*jmax)/2),stat=ibcj) + allocate (hold_mask_i_loc(imax*jmax),stat=ihmi) + allocate (hold_mask_j_loc(imax*jmax),stat=ihmj) + allocate (temp_mask_i_loc(imax*jmax),stat=itmi) + allocate (temp_mask_j_loc(imax*jmax),stat=itmj) + if (isni /= 0 .or. isnj /= 0 .or. inci /= 0 .or. incj /= 0 .or. + & ibci /= 0 .or. ibcj /= 0 .or. ihmi /= 0 .or. ihmj /= 0 .or. + & itmi /= 0 .or. itmj /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various search, hold and temp arrays.' + print *,'!!! isni = ',isni,' isnj= ',isnj + print *,'!!! inci = ',inci,' incj= ',incj + print *,'!!! ibci = ',ibci,' ibcj= ',ibcj + print *,'!!! ihmi = ',ihmi,' ihmj= ',ihmj + print *,'!!! itmi = ',itmi,' itmj= ',itmj + print *,' ' + endif + + STOP 98 + endif + + closed_contour = 'n' + xcentval = fxy(ix,jx) + num_found_contours = 0 + next_contour_ct = 0 + beyond_contour_ct = 0 + num_pts_in_all_contours = 0 + hold_mask_i_loc = 0 + hold_mask_j_loc = 0 + beyond_contour_i = 0 + beyond_contour_j = 0 + point_is_already_in_our_contour = .false. + point_is_already_in_beyond_pool = .false. + icccret = 0 + isc_count = 0 + plastbar = -999.0 + rlastbar = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* Top of check_closed_contour, ix= ',ix,' jx= ',jx + print *,'*-----------------------------------------------*' + print *,' ' + print *,'fxy(ix,jx)= ',fxy(ix,jx),' xcentval= ',xcentval + endif + +c First, set up the contour intervals that will be used. In +c the original version of this code, we used preset +c standard intervals (984,988,992,996,1000,1004....). But upon +c further review, it was decided that this was too arbitrary. +c So instead, we consider the found min (max) value to be the +c bottom (top) of the list of contour intervals. In this way, +c we can clearly specify and screen storms based on the "depth" +c of the pressure field as compared to the surroundings. + + i = 1 + do while (i <= maxconts) + if (cmaxmin == 'min') then + contvals(i) = xcentval + float(i-1)*trkrinfo%contint + i = i + 1 + else + iflip = maxconts - i + 1 + contvals(iflip) = xcentval - float(i-1)*trkrinfo%contint + i = i + 1 + endif + enddo + +c This successive_contours loop is the master loop.... + + successive_contours_loop: do while (num_found_contours < + & num_requested_contours) + +c Find the contour interval in which the center value resides. +c Note that the lower bound is included for a min check, while +c the upper bound is included for a max check. Note also that +c this subroutine can be used to find the last closed contour, +c and part of that functionality shows up in the next while +c statement where we reference "num_found_contours" in the +c array indeces for the contour values. Basically, the way we +c do this is, for example, if our central value is 990.4 mb and +c our contour interval is 4 mb, then in the first run through +c successive_contours_loop we see if we have a closed contour in +c the interval 990.4-994.4. If yes, then the next time through +c this loop, we see if we have a closed contour in the interval +c 994.4-998.4. If yes, then the next loop check is for 998.4- +c 1002.4, and so on.... We stop searching if we find a value +c that is either below the xcentval input into this subroutine +c or below the lower value of the current contour interval (this +c would mean a change in the gradient and would indicate that, +c in the case of mslp, we are heading down towards another, +c different low). + + isc_count = isc_count + 1 + + point_is_already_in_next_contour = .false. + + i = 1 + do while (i < maxconts) + if (cmaxmin == 'min') then + if (contvals(i) <= xcentval .and. xcentval < contvals(i+1)) + & then + + if ( verb .ge. 3 ) then + print *,'At A, num_found_contours= ',num_found_contours + endif + + contlo = contvals(i+num_found_contours) + conthi = contvals(i+1+num_found_contours) + + if ( verb .ge. 3 ) then + print *,'At A, contlo= ',contlo,' conthi= ',conthi + endif + exit + + endif + else + if (contvals(i) < xcentval .and. xcentval <= contvals(i+1)) + & then + contlo = contvals(i-num_found_contours) + conthi = contvals(i-num_found_contours+1) + exit + endif + endif + i = i + 1 + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'num_found_contours= ',num_found_contours + print *,'contlo= ',contlo,' conthi= ',conthi + print *,'xcentval= ',xcentval + endif + + +c This single_contour_scan_loop is the main loop for searching +c for one individual contour. If it is determined that a contour +c exists, control is returned to the successive_contours_loop, +c and if more contours were requested to be found, then the +c search continues onward & outward.... + + temp_mask_i_loc = 0 + temp_mask_j_loc = 0 + + iter = 1 + num_pts_in_one_contour = 0 + still_scanning = .true. + + rlast_distsum = 0.0 + rlast_distct = 0 + + single_contour_scan_loop: do while (still_scanning) + +c print *,' ' +c print *,' top of single contour scan loop' +c print *,'+++ iter= ',iter +c print *,' N1: next_contour_ct= ',next_contour_ct + + if (iter == 1 .and. num_found_contours == 0) then + ! For the first iteration, we have only the first ring, + ! which is centered on the input minimum/maximum point. + ringct = 1 + search_next_i(1) = ix + search_next_j(1) = jx + +c point_is_already_in_our_contour(ix,jx) = .true. +c num_pts_in_one_contour = num_pts_in_one_contour + 1 +c temp_mask_i_loc(num_pts_in_one_contour) = ix +c temp_mask_j_loc(num_pts_in_one_contour) = jx + + else if (iter == 1 .and. num_found_contours > 0) then + ! This is the first iteration in a *new* contour. + ! That is, we have already found 1 or more previous + ! contours while in previous iterations of + ! successive_contours_loop and we are now beginning + ! to look for the next contour. + +c print *,' N2: next_contour_ct= ',next_contour_ct + + if (next_contour_ct == 0) then + ! This would be for the special case in which, for + ! example, you've got a very intense, compact storm + ! that "skips" a contour. That is, suppose the + ! min pressure of a storm is 982 mb, and we are + ! utilizing a 4-mb contour interval, but all + ! surrounding data points are, say, 987 mb or + ! higher. Then, next_contour_ct would be 0 since no + ! data points were found in the next contour interval + ! of 982-986 mb, but we can continue searching since the + ! gradient is still sloping the correct way. The code in + ! this if statement handles this special case. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ALERT: next_contour_ct = 0 ' + endif + + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + +c print *,'b4 ZZ, ringct= ',ringct +c print *,'at ZZ, bcc= ',beyond_contour_ct +c & ,'contlo_next= ',contlo_next +c & ,'conthi_next= ',conthi_next + + bey_con_min_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_min_loop + endif + +c print *,'-- ZZ, ibx= ',ibx,' jby= ',jby +c & ,' fxy(ibx,jby)= ',fxy(ibx,jby) + + if (fxy(ibx,jby) >= contlo_next .and. + & fxy(ibx,jby) < conthi_next) then + +c print *,'>> ZZ HIT!!, ibx= ',ibx,' jby= ',jby +c +c print *,' +++ BEYOND in NEXT: i= ',ibx,' j= ',jby +c & ,' fxy= ',fxy(ibx,jby) + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + +c print *,'.. ZZ, next_contour_ct= ',next_contour_ct + + enddo bey_con_min_loop + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + +c print *,'At A, beyond_contour_ct= ',beyond_contour_ct +c print *,' contlo_next = ',contlo_next +c print *,' conthi_next = ',conthi_next + + bey_con_max_loop: do nb = 1,beyond_contour_ct + +c print *,'in bey_con_max_loop, nb= ',nb + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_max_loop + endif + +c print *,'ibx= ',ibx,' jby= ',jby,' data= ' +c & ,fxy(ibx,jby) + + if (fxy(ibx,jby) > contlo_next .and. + & fxy(ibx,jby) <= conthi_next) then + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + +c print *,' ++ HIT! ibx= ',ibx,' jby= ',jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + enddo bey_con_max_loop + endif + + if (next_contour_ct > 0) then + ringct = next_contour_ct + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! XXX next_contour_ct not > 0 !!!' + print *,'next_contour_ct= ',next_contour_ct + print *,'beyond_contour_ct= ',beyond_contour_ct + print *,'ringct= ',ringct + print *,'next_ring_ct= ',next_ring_ct + print *,'cycling to top of successive_contours_loop..' + print *,' ' + endif + + ! The number of rings that we have available to search + ! in the next contour interval is 0, so cycle all the + ! way back to the top of the outer loop, which is + ! successive_contours_loop, so that we can increase the + ! contour bounds and search inside those new bounds. + ! Again, this is for the case in which we have an + ! intense, compact storm and we are using a small + ! contour interval, such that we are essentially + ! "skipping" over one of these intervals in one of the + ! loop iterations. We need to bump up the + ! num_found_contours by one in order to increase the + ! array index in the contvals array at the top of the + ! successive_contours_loop. It is kosher to do this + ! since the reason we are cycling back to the top of + ! that loop is that we are skipping over a contour + ! interval. + + num_found_contours = num_found_contours + 1 + cycle successive_contours_loop + + endif + + else + + ringct = next_contour_ct + + endif + + do nring = 1,ringct + search_next_i(nring) = next_contour_i(nring) + search_next_j(nring) = next_contour_j(nring) +c print *,'at A, nring= ',nring,' next_contour_i(nring)= ' +c & ,next_contour_i(nring),' next_contour_j(nring)= ' +c & ,next_contour_j(nring) + enddo + + next_contour_ct = 0 + + else + ringct = next_ring_ct + endif + + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + allocate (ringposi(ringct),stat=iria) + allocate (ringposj(ringct),stat=irja) + if (iria /= 0 .or. irja /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various ring arrays. iria = ',iria + print *,'!!! irja = ',irja + print *,' ' + endif + + STOP 98 + endif + +ctm +c print *,' ' +c print *,'ringct= ',ringct + + do nring = 1,ringct + ringposi(nring) = search_next_i(nring) + ringposj(nring) = search_next_j(nring) +ctm +c print *,'nring= ',nring,' ringposi= ',ringposi(nring) +c & ,' ringposj= ',ringposj(nring) + enddo + + next_ring_ct = 0 + + ! This next loop reviews the points that have been + ! labelled for the "beyond_contour" pool. As we get further + ! into successive iterations of successive_contours_loop, + ! some of these previously "beyond" points are now within + ! the contour interval range that we are checking, so we + ! need to go through the list of "beyond" points and remove + ! any that are no longer in that "beyond" category.... + + check_beyond_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! This point may have been removed already in a + ! previous iteration of successive_contours_loop. + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle check_beyond_loop + endif + + ! Check to see if any of the points being searched in the + ! upcoming multiple_ring_loop are points that had previously + ! been saved as "beyond_contour" points. If so, remove + ! their status as "beyond_contour" points by setting the + ! logical flag to false. + + do nring = 1,ringct + + if (ibx == ringposi(nring) .and. jby == ringposj(nring)) + & then +c print *,' ' +c print *,'!!! beyond remove: ibx= ',ibx,' jby= ',jby + point_is_already_in_beyond_pool(ibx,jby) = .false. + endif + + enddo + + enddo check_beyond_loop + + +c In each iteration of single_contour_scan_loop, we can have a +c different number of rings to analyze. In the first +c iteration, we only have 1 ring, the initial ring around the +c local max/min that was input to this subroutine. Subsequent +c iterations will have a variable number of rings, depending on +c how many new data points within our contour interval were +c found in the previous iteration. + + multiple_ring_loop: do mr = 1,ringct + + icenx = ringposi(mr) + jcenx = ringposj(mr) + +ctm +c print *,' --- iter= ',iter,' mr= ',mr,' icenx= ',icenx +c & ,' jcenx= ',jcenx + + call get_ijplus1_check_wrap (imax,jmax,icenx,jcenx,ixp1,jxp1 + & ,ixm1,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NO CLOSED CONTOUR: The call to ' + print *,'!!! get_ijplus1_check_wrap indicates the' + print *,'!!! max/min contour extends past the edge of' + print *,'!!! our regional grid. ' + print *,' ' + print *,' ' + endif + + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c For each individual ring, we check all 8 points surrounding +c the center point. The points are numbered for each ring as +c shown in the diagram to the right of the "select case" +c statement just below. REMEMBER: The j in our grids +c increases from north to south, so that for a global grid, +c j=1 is at 90N and j=jmax is at 90S. + + individual_ring_loop: do ir = 1,9 + + select case (ir) + case (1); irx=ixm1; jrx=jcenx;! 2 3 4 + case (2); irx=ixm1; jrx=jxm1; ! + case (3); irx=icenx;jrx=jxm1; ! + case (4); irx=ixp1; jrx=jxm1; ! 1 (icenx,jcenx) 5 + case (5); irx=ixp1; jrx=jcenx;! + case (6); irx=ixp1; jrx=jxp1; ! + case (7); irx=icenx;jrx=jxp1; ! 8 7 6 + case (8); irx=ixm1; jrx=jxp1; ! + case (9); irx=icenx; jrx=jcenx; ! = center pt of ring + end select + +c Make sure the point we are looking at has valid data. +c This is an issue only on regional grids, where we have a +c buffer of bitmapped (null) data points surrounding the +c real grid. + +c print *,'ind ring loop: ir= ',ir,' irx= ',irx,' jrx= ',jrx + + if (.not. valid_pt(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a non-' + print *,'!!! valid point, meaning we are near the' + print *,'!!! bounds of the grid, or at least the ' + print *,'!!! bounds of the valid data for this ' + print *,'!!! grid. We will skip the' + print *,'!!! search for this center.' + print *,'!!! ' + print *,'!!! (i,j) of non-valid pt = (' + & ,irx,',',jrx,')' + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c Check to make sure that the point we are looking at is +c not considered under the influence of another nearby low. + + if (masked_out(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a point' + print *,'!!! that has been masked out, meaning it' + print *,'!!! belongs under the influence of ' + print *,'!!! another nearby low, so we will skip' + print *,'!!! the search for this center....' + print *,'!!! ' + print *,'!!! Min central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Masked-out value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of masked value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we have already hit this point on a previous ring +c check, then just ignore this point and cycle past it. + + if (point_is_already_in_our_contour(irx,jrx)) then +ctm +c print *,' ' +c print *,'Pt. AAA, already-in-contour.....' +c print *,'irx= ',irx,' jrx= ',jrx + cycle individual_ring_loop + endif + +c For a MIN check, check to see if the data point is below +c the contour interval or is below the local minimum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c For a MAX check, check to see if the data point is above +c the contour interval or is above the local maximum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c +c For example, for mslp, this would be as we're moving +c outward away from lower pressures to higher pressures, +c and then all of a sudden we come upon a lower pressure. +c This probably means we're heading toward another low +c pressure area, so mark the point and return to the +c calling routine. + + found_a_point_below_contour = 'n' + found_a_point_above_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) < xcentval .or. fxy(irx,jrx) < contlo) + & then + found_a_point_below_contour = 'y' + endif + else + if (fxy(irx,jrx) > xcentval .or. fxy(irx,jrx) > conthi) + & then + found_a_point_above_contour = 'y' + endif + endif + + if (found_a_point_below_contour == 'y' .or. + & found_a_point_above_contour == 'y') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a data' + print *,'!!! value that is less (greater) than the' + print *,'!!! current contour interval bound for a' + print *,'!!! min (max) and/or is less (greater) ' + print *,'!!! than the minimum (maximum) central ' + print *,'!!! value that we are centering the ' + print *,'!!! search on.' + print *,'!!! ' + print *,'!!! Central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Flagged value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of flagged value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we've made it this far, then we at least know that the +c gradient is still heading in the right direction. Do the +c check now to see if the value at this point is within our +c specific contour interval (there is the possibility that +c the value is beyond our interval, which will be checked +c for just below, and if that's the case, then that point +c will be processed in a subsequent iteration of this loop +c that encompasses that correct contour interval). + + found_a_point_in_our_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) >= contlo .and. fxy(irx,jrx) < conthi) + & then + found_a_point_in_our_contour = 'y' + endif + else + if (fxy(irx,jrx) > contlo .and. fxy(irx,jrx) <= conthi) + & then + found_a_point_in_our_contour = 'y' + endif + endif + + if (found_a_point_in_our_contour == 'y') then + ! We've found a data point in our interval, something + ! that is inside the closed contour, and it hasn't been + ! marked as being found in a previous iteration of this + ! loop, so mark it now and store the (i,j) location so + ! that we can scan a ring around this point in a + ! successive iteration of this loop for more potential + ! points within this interval... + + point_is_already_in_our_contour(irx,jrx) = .true. + + next_ring_ct = next_ring_ct + 1 + search_next_i(next_ring_ct) = irx + search_next_j(next_ring_ct) = jrx + +c print *,'at B, next_ring_ct= ',next_ring_ct +c & ,' search_next_i()= ',search_next_i(next_ring_ct) +c & ,' search_next_j()= ',search_next_j(next_ring_ct) + + num_pts_in_one_contour = num_pts_in_one_contour + 1 + temp_mask_i_loc(num_pts_in_one_contour) = irx + temp_mask_j_loc(num_pts_in_one_contour) = jrx + + if (get_last_isobar_flag == 'y') then + call calcdist (glon(ix),glat(jx) + & ,glon(irx),glat(jrx),dist,degrees) + rlast_distsum = rlast_distsum + dist + rlast_distct = rlast_distct + 1 + endif + +ctm +c print *,' ' +c print *,' PT IN! irx= ',irx,' jrx= ',jrx,' xval= ' +c & ,fxy(irx,jrx) +c print *,'next_ring_ct= ',next_ring_ct +c print *,'num_pts_in_one_contour= ' +c & ,num_pts_in_one_contour + endif + +c If we've made it this far AND the +c found_a_point_in_our_contour flag indicates that this +c point is not in our contour interval, then by default that +c means that this point is for a contour interval beyond +c what we're currently looking at. E.g., if we're looking +c at the contours around a 972 mb low and we're moving +c outward and currently checking the 984-988 mb contour +c interval, it means that we found, say, a gridpoint with +c 991 mb. So we want to mark that point for a future +c iteration of this loop that would be checking the +c 988-992 mb contour interval. + + if (found_a_point_in_our_contour /= 'y' .and. + & .not. point_is_already_in_next_contour(irx,jrx)) then + ! We've found a data point that is beyond our interval, + ! so this is not a concern for finding the bounds of + ! our current contour interval, but we want to mark + ! these points and remember them for the next iteration + ! of successive_scan_loop. (For example, suppose we + ! are currently searching for points in the 984-988 mb + ! range, and we find a point that is 990 -- mark it + ! here to be remembered when we scan for 988-992 mb). + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + if (fxy(irx,jrx) >= contlo_next .and. + & fxy(irx,jrx) < conthi_next) then + ! "NEXT_CONTOUR" Comment: + ! We've found a point that is in the very next + ! contour interval.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. + else if (fxy(irx,jrx) >= conthi_next) then + ! "BEYOND_CONTOUR" Comment: + ! This point is at least 1 contour interval beyond + ! the next contour interval. Dump the info into + ! these i and j arrays. This info will be used if + ! in the next iteration of single_contour_scan_loop, + ! next_contour_ct = 0. That would mean that we + ! have, e.g., an intensely deep low with a sharp + ! mslp gradient that essentially "skips" over a + ! contour interval. E.g., if using a 4 mb interval, + ! we go from 947 to 953 AND there are NO + ! intervening gridpoints in the 948-952 interval. + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) + endif + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + if (fxy(irx,jrx) > contlo_next .and. + & fxy(irx,jrx) <= conthi_next) then + ! See "NEXT_CONTOUR" comment just above.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. +c print *,'NEXT ncc= ',next_contour_ct +c & ,'next_contour_i()= ' +c & ,next_contour_i(next_contour_ct) +c & ,'next_contour_j()= ' +c & ,next_contour_j(next_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + else if (fxy(irx,jrx) <= contlo_next) then + ! See "BEYOND_CONTOUR" comment just above.... + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'BEYOND bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + endif + endif + endif + + enddo individual_ring_loop + + enddo multiple_ring_loop + + if (next_ring_ct > 0) then + iter = iter + 1 + else + icccret = 0 + still_scanning = .false. + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + num_found_contours = num_found_contours + 1 + closed_contour = 'y' + if (num_found_contours == 1) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Closed contour found ' + endif + + endif + endif + + enddo single_contour_scan_loop + + do insingle = 1,num_pts_in_one_contour + num_pts_in_all_contours = num_pts_in_all_contours + 1 + inall = num_pts_in_all_contours + hold_mask_i_loc(inall) = temp_mask_i_loc(insingle) + hold_mask_j_loc(inall) = temp_mask_j_loc(insingle) + enddo + + if (get_last_isobar_flag == 'y') then + if (cmaxmin == 'min') then + plastbar = conthi + else + plastbar = contlo + endif + if (rlast_distct > 0) then + rlastbar = rlast_distsum / float(rlast_distct) + rlastbar = rlastbar * 0.539638 ! convert km to nm + else + rlastbar = -999.0 + endif + endif + + enddo successive_contours_loop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'END SUM: num of iterations = ',isc_count + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine get_ijplus1_check_wrap (imax,jmax,i,j,iplus1,jplus1 + & ,iminus1,jminus1,trkrinfo,igicwret) +c +c ABSTRACT: This subroutine takes an (i,j) position input and +c returns the four neighboring (i,j) points to the east, south, +c west and north. The routine checks for wrap around the GM, so +c that if, for example, you are on a global 360x181 grid and you +c are at point i=360, then i+1 = 361, so you need something to +c adjust that back to i = 1. Likewise, if you are at i=1 and +c looking for point i-1, it will adjust it to be point 360 +c instead of the meaningless point 0 (i=0). + + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer i,j,imax,jmax,iplus1,jplus1,iminus1,jminus1,igicwret + + igicwret = 0 + + jplus1 = j + 1 + jminus1 = j - 1 + iplus1 = i + 1 + iminus1 = i - 1 + + if (iplus1 > imax) then + if (trkrinfo%gridtype == 'global') then + iplus1 = iplus1 - imax ! If wrapping east of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is too close to the eastern bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested eastern ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',iplus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (iminus1 < 1) then + if (trkrinfo%gridtype == 'global') then + iminus1 = imax + iminus1 ! If wrapping west of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested western search boundary' + print *,'!!! is too close to the western bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested western ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested western i = ',iminus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (jplus1 > jmax .or. jminus1 < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The ' + print *,'!!! user-requested northern or southern search' + print *,'!!! boundary is too close to the bounds of the' + print *,'!!! grid. Cut back your requested northern or' + print *,'!!! southern boundary by a degree or 2 in the' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested northern j = ',jminus1 + print *,'!!! User-requested southern j = ',jplus1 + print *,'!!! jmax of grid = ',jmax + print *,' ' + endif + + igicwret = 91 + return + endif + + return + end + +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + SUBROUTINE qsort(x,ind,n) +c +c Code converted using TO_F90 by Alan Miller +c Date: 2002-12-18 Time: 11:55:47 + + IMPLICIT NONE + INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(12, 60) + + REAL (dp), INTENT(IN) :: x(n) + INTEGER, INTENT(OUT) :: ind(n) + INTEGER, INTENT(IN) :: n + +c *************************************************************************** +c +c ROBERT RENKA +c OAK RIDGE NATL. LAB. +c +c THIS SUBROUTINE USES AN ORDER N*LOG(N) QUICK SORT TO SORT A REAL (dp) +c ARRAY X INTO INCREASING ORDER. THE ALGORITHM IS AS FOLLOWS. IND IS +c INITIALIZED TO THE ORDERED SEQUENCE OF INDICES 1,...,N, AND ALL INTERCHANGES +c ARE APPLIED TO IND. X IS DIVIDED INTO TWO PORTIONS BY PICKING A CENTRAL +c ELEMENT T. THE FIRST AND LAST ELEMENTS ARE COMPARED WITH T, AND +c INTERCHANGES ARE APPLIED AS NECESSARY SO THAT THE THREE VALUES ARE IN +c ASCENDING ORDER. INTERCHANGES ARE THEN APPLIED SO THAT ALL ELEMENTS +c GREATER THAN T ARE IN THE UPPER PORTION OF THE ARRAY AND ALL ELEMENTS +c LESS THAN T ARE IN THE LOWER PORTION. THE UPPER AND LOWER INDICES OF ONE +c OF THE PORTIONS ARE SAVED IN LOCAL ARRAYS, AND THE PROCESS IS REPEATED +c ITERATIVELY ON THE OTHER PORTION. WHEN A PORTION IS COMPLETELY SORTED, +c THE PROCESS BEGINS AGAIN BY RETRIEVING THE INDICES BOUNDING ANOTHER +c UNSORTED PORTION. +c +c INPUT PARAMETERS - N - LENGTH OF THE ARRAY X. +c +c X - VECTOR OF LENGTH N TO BE SORTED. +c +c IND - VECTOR OF LENGTH >= N. +c +c N AND X ARE NOT ALTERED BY THIS ROUTINE. +c +c OUTPUT PARAMETER - IND - SEQUENCE OF INDICES 1,...,N PERMUTED IN THE SAME +c FASHION AS X WOULD BE. THUS, THE ORDERING ON +c X IS DEFINED BY Y(I) = X(IND(I)). +c +c ********************************************************************* + + ! NOTE -- IU AND IL MUST BE DIMENSIONED >= LOG(N) WHERE LOG HAS BASE 2. + + !********************************************************************* + + INTEGER :: iu(21), il(21) + INTEGER :: m, i, j, k, l, ij, it, itt, indx + REAL :: r + REAL (dp) :: t + + ! LOCAL PARAMETERS - + + ! IU,IL = TEMPORARY STORAGE FOR THE UPPER AND LOWER + ! INDICES OF PORTIONS OF THE ARRAY X + ! M = INDEX FOR IU AND IL + ! I,J = LOWER AND UPPER INDICES OF A PORTION OF X + ! K,L = INDICES IN THE RANGE I,...,J + ! IJ = RANDOMLY CHOSEN INDEX BETWEEN I AND J + ! IT,ITT = TEMPORARY STORAGE FOR INTERCHANGES IN IND + ! INDX = TEMPORARY INDEX FOR X + ! R = PSEUDO RANDOM NUMBER FOR GENERATING IJ + ! T = CENTRAL ELEMENT OF X + + IF (n <= 0) RETURN + + ! INITIALIZE IND, M, I, J, AND R + + DO i = 1, n + ind(i) = i + END DO + m = 1 + i = 1 + j = n + r = .375 + + ! TOP OF LOOP + + 20 IF (i >= j) GO TO 70 + IF (r <= .5898437) THEN + r = r + .0390625 + ELSE + r = r - .21875 + END IF + + ! INITIALIZE K + + 30 k = i + + ! SELECT A CENTRAL ELEMENT OF X AND SAVE IT IN T + + ij = i + r*(j-i) + it = ind(ij) + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) > t) THEN + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + END IF + + ! INITIALIZE L + + l = j + + ! IF THE LAST ELEMENT OF THE ARRAY IS LESS THAN T, + ! INTERCHANGE IT WITH T + indx = ind(j) + IF (x(indx) >= t) GO TO 50 + ind(ij) = indx + ind(j) = it + it = indx + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) <= t) GO TO 50 + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + GO TO 50 + + ! INTERCHANGE ELEMENTS K AND L + + 40 itt = ind(l) + ind(l) = ind(k) + ind(k) = itt + + ! FIND AN ELEMENT IN THE UPPER PART OF THE ARRAY WHICH IS + ! NOT LARGER THAN T + + 50 l = l - 1 + indx = ind(l) + IF (x(indx) > t) GO TO 50 + + ! FIND AN ELEMENT IN THE LOWER PART OF THE ARRAY WHCIH IS NOT SMALLER THAN T + + 60 k = k + 1 + indx = ind(k) + IF (x(indx) < t) GO TO 60 + + ! IF K <= L, INTERCHANGE ELEMENTS K AND L + + IF (k <= l) GO TO 40 + + ! SAVE THE UPPER AND LOWER SUBSCRIPTS OF THE PORTION OF THE + ! ARRAY YET TO BE SORTED + + IF (l-i > j-k) THEN + il(m) = i + iu(m) = l + i = k + m = m + 1 + GO TO 80 + END IF + + il(m) = k + iu(m) = j + j = l + m = m + 1 + GO TO 80 + + + ! BEGIN AGAIN ON ANOTHER UNSORTED PORTION OF THE ARRAY + + 70 m = m - 1 + IF (m == 0) RETURN + i = il(m) + j = iu(m) + + 80 IF (j-i >= 11) GO TO 30 + IF (i == 1) GO TO 20 + i = i - 1 + + ! SORT ELEMENTS I+1,...,J. NOTE THAT 1 <= I < J AND J-I < 11. + + 90 i = i + 1 + IF (i == j) GO TO 70 + indx = ind(i+1) + t = x(indx) + it = indx + indx = ind(i) + IF (x(indx) <= t) GO TO 90 + k = i + + 100 ind(k+1) = ind(k) + k = k - 1 + indx = ind(k) + IF (t < x(indx)) GO TO 100 + + ind(k+1) = it + GO TO 90 + END SUBROUTINE qsort diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/gettrk_modules.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/gettrk_modules.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/gettrk_modules.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/gettrk_modules.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile index 58cf46ff5c..4615197edd 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc +FCOMP= mpiifort -nofree +CCOMP= mpiicc FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) CFLAGS= -O3 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_hera similarity index 85% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_hera index 934cad805c..d948effc95 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_hera @@ -1,10 +1,10 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc -FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 -CFLAGS= -O2 -axSSE4.2,AVX,CORE-AVX2 +FCOMP= mpiifort -nofree +CCOMP= mpiicc +FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) +CFLAGS= -O2 gettrk_g2: gettrk_main_g2.f gettrk_modules.o module_waitfor.o cwaitfor.o @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_jet new file mode 100644 index 0000000000..5ed2b727ad --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_jet @@ -0,0 +1,37 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +FCOMP= mpiifort -nofree +CCOMP= mpiicc +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +CFLAGS= -O3 -axSSE4.2,AVX,CORE-AVX2 + +gettrk_g2: gettrk_main_g2.f gettrk_modules.o module_waitfor.o cwaitfor.o + @echo " " + @echo " Compiling the main tracking program and subroutines....." + $(FCOMP) $(FFLAGS) gettrk_modules.o module_waitfor.o cwaitfor.o gettrk_main_g2.f $(LIBS) -o gettrk_g2 + @echo " " + +cwaitfor.o: cwaitfor.c + @echo " " + @echo " Compiling the waitfor C routine...." + $(CCOMP) $(CFLAGS) -c cwaitfor.c -o cwaitfor.o + +module_waitfor.o: module_waitfor.f + @echo " " + @echo " Compiling the waitfor fortran module...." + $(FCOMP) $(FFLAGS) -c module_waitfor.f -o module_waitfor.o + +gettrk_modules.o: gettrk_modules.f + @echo " " + @echo " Compiling the regular tracker fortran modules....." + $(FCOMP) $(FFLAGS) -c gettrk_modules.f -o gettrk_modules.o + @echo " " + +CMD = gettrk_g2 + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_orion index 159fbdb71d..bcc0728cc9 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_orion @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc +FCOMP= mpiifort -nofree +CCOMP= mpiicc #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) CFLAGS= -O3 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/module_waitfor.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/module_waitfor.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_g2.fd/module_waitfor.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_g2.fd/module_waitfor.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/cwaitfor.c b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/cwaitfor.c similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/cwaitfor.c rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/cwaitfor.c diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/gettrk_main.gen_g1.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/gettrk_main.gen_g1.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/gettrk_main.gen_g1.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/gettrk_main.gen_g1.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/gettrk_modules.gen.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/gettrk_modules.gen.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/gettrk_modules.gen.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/gettrk_modules.gen.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile index 53cb13f34a..0e6dcb9dfc 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc +FCOMP= mpiifort -nofree +CCOMP= mpiicc FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) CFLAGS= -O3 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_hera similarity index 87% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_hera index 8819c9dc3f..c7e505790a 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_hera @@ -1,10 +1,10 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc -FFLAGS= -O2 -fpe0 -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 -CFLAGS= -O2 -axSSE4.2,AVX,CORE-AVX2 +FCOMP= mpiifort -nofree +CCOMP= mpiicc +FFLAGS= -O2 -fpe0 -i$(ISIZE) -r$(RSIZE) +CFLAGS= -O2 gettrk_gen_g1: gettrk_main.gen_g1.f gettrk_modules.gen.o module_waitfor.o cwaitfor.o @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_jet new file mode 100644 index 0000000000..6d1058d23d --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_jet @@ -0,0 +1,37 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +FCOMP= mpiifort -nofree +CCOMP= mpiicc +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +CFLAGS= -O3 -axSSE4.2,AVX,CORE-AVX2 + +gettrk_gen_g1: gettrk_main.gen_g1.f gettrk_modules.gen.o module_waitfor.o cwaitfor.o + @echo " " + @echo " Compiling the main tracking program and subroutines....." + $(FCOMP) $(FFLAGS) gettrk_modules.gen.o module_waitfor.o cwaitfor.o gettrk_main.gen_g1.f $(LIBS) -o gettrk_gen_g1 + @echo " " + +cwaitfor.o: cwaitfor.c + @echo " " + @echo " Compiling the waitfor C routine...." + $(CCOMP) $(CFLAGS) -c cwaitfor.c -o cwaitfor.o + +module_waitfor.o: module_waitfor.f + @echo " " + @echo " Compiling the waitfor fortran module...." + $(FCOMP) $(FFLAGS) -c module_waitfor.f -o module_waitfor.o + +gettrk_modules.gen.o: gettrk_modules.gen.f + @echo " " + @echo " Compiling the regular tracker fortran modules....." + $(FCOMP) $(FFLAGS) -c gettrk_modules.gen.f -o gettrk_modules.gen.o + @echo " " + +CMD = gettrk_gen_g1 + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_orion index 1926ec8e34..7004c4903c 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_orion @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc +FCOMP= mpiifort -nofree +CCOMP= mpiicc #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) CFLAGS= -O3 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/module_waitfor.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/module_waitfor.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g1.fd/module_waitfor.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g1.fd/module_waitfor.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/cwaitfor.c b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/cwaitfor.c similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/cwaitfor.c rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/cwaitfor.c diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/gettrk_gen_main_g2.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/gettrk_gen_main_g2.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/gettrk_gen_main_g2.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/gettrk_gen_main_g2.f diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/gettrk_gen_main_g2.f_10152019 b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/gettrk_gen_main_g2.f_10152019 new file mode 100644 index 0000000000..185c31cff6 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/gettrk_gen_main_g2.f_10152019 @@ -0,0 +1,20999 @@ + program trakmain +c +c$$$ MAIN PROGRAM DOCUMENTATION BLOCK +c +c Main Program: GETTRK Track model vortices +C PRGMMR: MARCHOK ORG: NP22 DATE: 2002-05-20 +c +c ABSTRACT: This program tracks the average of the max or min +c of several parameters in the vicinity of an input +c first guess (lat,lon) position of a vortex in order to give +c forecast position estimates for that vortex for a given numerical +c model. For the levels 700 & 850 mb, the tracked parameters are: +c Relative vorticity (max), wind magnitude (min), and geopotential +c height (min). Also tracked is the min in the MSLP. So many +c parameters are tracked in order to provide more accurate position +c estimates for weaker storms, which often have poorly defined +c structures/centers. Currently, the system is set up to be able +c to process GRIB input data files from the GFS, MRF, UKMET, GDAS, +c ECMWF, NGM, NAM and FNMOC/NOGAPS models. Two 1-line files +c are output from this program, both containing the forecast fix +c positions that the tracker has obtained. One of these output +c files contains the positions at every 12 hours from forecast +c hour 0 to the end of the forecast. The other file is in ATCF +c format, which is the particular format needed by the Tropical +c Prediction Center, and provides the positions at forecast hours +c 12, 24, 36, 48 and 72, plus the maximum wind near the storm center +c at each of those forecast hours. +c +c Program history log: +c 98-03-16 Marchok - Original operational version. +c 98-07-15 Marchok - Added code to calculate radii of gale-, storm-, +c and hurricane-force winds in each quadrant. +c 99-04-01 Marchok - Added code to be able to read in 4-digit years +c off of the TC Vitals records. +c Added code, including subroutine is_it_a_storm, +c to make a better determination of whether or +c not the center that was found at each time is +c the center of a storm, and not just a passing +c vort max, etc. +c 99-06-15 Marchok - Fixed a bug in calcdist that was triggered by a +c rounding error sending a number just above 1 +c into ACOS to get the distance between 2 +c identical points (which, obviously, is 0). +c 00-06-20 Marchok - Added GDAS option for vortex relocation work. +c Changed nhalf from 3 to 5. Relaxed the +c requirements for pthresh and vthresh. +c 00-11-30 Marchok - Added ability to handle GFDL and NCEP Ensemble +c model data. Extended time range to be able to +c handle 5-day capability. Forecast hours are +c now input via a namelist (easiest way to account +c for NAM, GFS and GFDL having different forecast +c lengths at 00/12z and 06/18z). Model ID's are +c now input via a namelist (makes it easier, for +c example, to run for many different ensemble +c members). Added new output, the atcfunix +c format, needed for 5-day forecasts. +c 01-08-24 Marchok Fixed a bug in rvcal and getgridinfo. When a +c grid that was south-->north is flipped in +c conv1d2d_real to be north-->south, the scanning +c mode flag remains 64 and what we would consider +c the max and min latitudes are reversed, so I +c added code to correct this in both routines. +c 02-05-20 Marchok Weakened the mslp gradient threshold and v850 +c threshold in is_it_a_storm to cut down on the +c number of dropped storms. +c 03-03-18 Marchok Fixed a bug in get_ij_bounds that was allowing +c a cos(90) and cos(-90), which then led to a +c divide by zero. +c 05-08-01 Marchok Updated to allow tracking of ECMWF hi-res, ECMWF +c ensemble, CMC hi-res, CMC ensemble, NCEP +c ensemble. +c 06-11-07 Marchok Updated to locate, and report to the atcfunix +c file, the value of the gridpoint minimum value +c of mslp. Previously, the barnes-averaged +c value had been used. +c 08-01-10 Marchok Changed the storm ID for genesis tracking so +c that the ID includes info +c on storm detection location & time. Added +c algorithms for Hart's cyclone phase space. +c Added new output fields to the atcfunix +c records, actually creating a modified atcfunix +c record, to include things such as the mean & +c max values of zeta850 & zeta700 centered on +c the storm, the speed & direction of storm +c translation, and the Hart CPS parameters. +c 10-01-07 Marchok - input grib lead time can be hrs or minutes +c - added code for warm core check +c - added code to detect genesis +c - added code to report on sfc wind structure +c - added buffer ("grid_buffer") to avoid fixing +c center to boundaries on regional grids +c - modified rvcal to report missing zeta values +c as background coriolis instead of -999, since +c the -999 was messing up center-fixing +c - added 10-m wind and sfc zeta as center-fixing +c parms. +c +c 10-05-25 Slocum Add verbose feature to code +c 0 = Not terminal output, 1 = error messages only +c 2 = all output +c +c 10-05-26 Marchok - added flags and code to check the temporal +c consistency of the mslp closed contour and +c Vt850 checks for tcgen and midlat cases. +c +c Input files: +c unit 11 Unblocked GRIB1 file containing model data +c unit 12 Text file containing TC Vitals card for current time +c unit 31 Unblocked GRIB index file +c +c Output files: +c unit 61 Output file with forecast positions every 12h from +c vt=00h to the end of the forecast +c unit 62 Output file in ATCF format, with forecast positions +c at vt = 12, 24, 36, 48 and 72h, plus wind speeds. +c unit 63 Output file with forecast wind radii for 34, 50 and +c 64 knot thresholds in each quadrant of each storm. +c +c Subprograms called: +c read_nlists Read input namelists for input date & storm number info +c read_tcv_card Read TC vitals file to get initial storm position +c getgridinfo Read GRIB file to get basic grid information +c tracker Begin main part of tracking algorithm +c +c Attributes: +c Language: Standard Fortran_90 +c +c$$$ +c +c------- +c +c LOCAL: +c +c ifhours: Integer array holding numerical forecast times for +c the input model (99 = no more times available). +c These values are read in via a namelist. +c Model numbers used: (1) GFS, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) NAM, (7) NOGAPS, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble (13) SREF +c Ensemble, (14) NCEP Ensemble (from ensstat mean +c fields), (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) Ensemble RELOCATION (21) UKMET hi-res (NHC) +c stormswitch: This switch tells how to handle each storm in +c the TCV file: +c 1 = process this storm for this forecast hour. +c 2 = Storm was requested to be tracked, but either +c the storm went off the grid (regional models), +c the storm dissipated, or the program was +c unable to track it. +c 3 = Storm was NOT requested to be tracked at all. +c storm: An array of type tcvcard. Each member of storm +c contains a separate TC Vitals card. +c maxstorm: Maximum number of storms the system is set up to +c handle at any 1 time. +c slonfg,slatfg: Holds first guess positions for storms. The +c very first, first guess position is read from the +c TC vitals card. (maxstorm,maxtime) +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms) +c + USE def_vitals; USE inparms; USE set_max_parms; USE level_parms + USE trig_vals; USE atcf; USE trkrparms; USE verbose_output +c + implicit none +c + logical(1) file_open + integer date_time(8) + character (len=10) big_ben(3) + integer itret,iggret,iicret,igcret,iret,ifhmax,maxstorm,numtcv + integer iocret + integer, parameter :: lugb=11,lugi=31,lucard=12,lgvcard=14,lout=51 +c + type (datecard) inp + type (trackstuff) trkrinfo + +c -------------------------------------------------------- + + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: beginning ... ',i2.2,':',i2.2,':',i2.2) + + call w3tagb('GETTRK ',1999,0104,0058,'NP22 ') + + pi = 4. * atan(1.) ! Both pi and dtr were declared in module + dtr = pi/180.0 ! trig_vals, but were not yet defined. +c + call read_nlists (inp,trkrinfo) + + call read_fhours (ifhmax) + + call read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_tcv_card, num vitals = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_tcv_card, rc= ',iret + endif + goto 890 + endif + + call read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_gen_vitals, total number of vitals (both' + & ,' TC and non-TC) now = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_gen_vitals, rc= ' + & ,iret + endif + goto 890 + endif + + if (inp%file_seq == 'onebig') then + call open_grib_files (inp,lugb,lugi,'dummy','dummy',lout,iret) + if (iret /= 0) then + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in open_grib_files, rc= ' + & ,iret + endif + STOP 113 + endif + endif + + call tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,itret) +c +890 continue + + igcret=0 + iicret=0 + iocret=0 + + inquire (unit=lugb, opened=file_open) + if (file_open) call baclose(lugb,igcret) + inquire (unit=lugi, opened=file_open) + if (file_open) call baclose(lugi,iicret) + inquire (unit=lout, opened=file_open) + if (file_open) call baclose(lout,iocret) + if ( verb .ge. 3 ) then + print *,'baclose: igcret= ',igcret,' iicret= ',iicret + print *,'baclose: iocret= ',iocret + endif + call w3tage('GETTRK ') +c + stop + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,itret) +c +c ABSTRACT: This subroutine is the core of the program. It contains +c the main loop for looping through all the forecast hours and all +c the storms. Basically, the way it works is that it has an outer +c loop that loops on the forecast hour. At the beginning of this +c loop, the data are read in for all parameters and levels needed +c for tracking. The full regional or global grid is read in. +c If vorticity was not read in (some of the centers do not send us +c vorticity), then vorticity calculations are done on the whole +c grid at both 850 and 700 mb. Then the program goes into the inner +c loop, which loops on storm number (program originally set up to +c handle a max of 15 storms). For each storm, subroutine +c find_maxmin is called for the following parameters: Rel Vort and +c geopotential hgt at 700 & 850 mb, and MSLP. Within find_maxmin, +c a barnes analysis is performed over the guess position of the +c storm to find the max or min value, and then iteratively, the +c grid size is cut in half several times and the barnes analysis +c rerun to refine the positioning of the max or min location. After +c the center positions for these parameters have been obtained, +c subroutine get_uv_center is called to get a center fix for the +c minimum in the wind field, specifically, a minimum in the +c magnitude of the wind speed (vmag). The calculation of the vmag +c minimum is done differently than the calculation for the other +c parameters; for vmag, the grid near the storm center guess +c position is interpolated down to a very fine grid, and then +c find_maxmin is called and a barnes analysis is done on that +c smaller grid. For vmag, there are no further calls made to barnes +c with a smaller grid, since the grid has already been interpolated +c down to a smaller grid. Once all of the parameter center fixes +c have been made, subroutine fixcenter is called to average these +c positions together to get a best guess fix position. Then a check +c is done with a call to subroutine is_it_a_storm to make sure that +c the center that we have found does indeed resemble a tropical +c cyclone. Finally, subroutine get_next_ges is called to make a +c guess position for the next forecast time for this storm. +c +c INPUT: +c inp contains input date and model number information +c maxstorm maximum # of storms to be handled +c numtcv number of storms read off of the tcvitals file +c ifhmax max number of analysis & forecast times to be handled +c trkrinfo derived type that holds/describes various tracker parms +c +c OUTPUT: +c itret return code from this subroutine +c +c LOCAL PARAMETERS: +c storm contains the tcvitals for the storms +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c maxtime Max number of forecast times program can track +c maxtp Max number of tracked parameters program will track. +c Currently (7/97), this maxtp is 9, and these 9 are +c listed just a few lines below. +c readflag L Indicates status of read for each of 13 parms: +c 1: 850 mb absolute vorticity +c 2: 700 mb absolute vorticity +c 3: 850 mb u-comp +c 4: 850 mb v-comp +c 5: 700 mb u-comp +c 6: 700 mb v-comp +c 7: 850 mb gp hgt +c 8: 700 mb gp hgt +c 9: MSLP +c 10: near-surface u-comp +c 11: near-surface v-comp +c 12: 500 mb u-comp +c 13: 500 mb v-comp +c 14: Mean temperature, centered at 400 mb +c +c calcparm L indicates which parms to track and which not to. +c Array positions are defined exactly as for clon +c and clat, listed next, except that, in general, when +c flag 3 is set to a value, flag 4 is set to the same +c value as 3, and when flag 5 is set to a value, flag +c 6 is set to the same value as 5. This is because +c 3 & 4 are for the 850 mb winds, and if either u or +c v is missing, we obviously can't calculate the +c magnitude of the wind. The same applies for 5 & 6, +c which are for the 700 mb winds. +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms). +c For the third position (max_#_parms), here they are: +c 1: Relative vorticity at 850 mb +c 2: Relative vorticity at 700 mb +c 3: Vector wind magnitude at 850 mb +c 4: NOT CURRENTLY USED +c 5: Vector wind magnitude at 700 mb +c 6: NOT CURRENTLY USED +c 7: Geopotential height at 850 mb +c 8: Geopotential height at 700 mb +c 9: Mean Sea Level Pressure +c 10: Vector wind magnitude at 10 m +c 11: Relative vorticity at 10 m +c xmaxwind Contains maximum near-surface wind near the storm +c center for each storm at each forecast hour. +c stderr Standard deviation of the position "errors" of the +c different parameters for each storm at each time. +c fixlat,fixlon: Contain the final coordinates for each storm at +c each forecast hour. These coordinates are a +c weighted average of all the individual parameter +c positions (hgt, zeta, mslp, vmag). +c cvort_maxmin: Contains the characters 'max' or 'min', and is +c used when calling the find_maxmin routine for the +c relative vorticity (Look for max in NH, min in SH). +c vradius Contains the distance from the storm fix position to +c each of the various near-surface wind threshhold +c distances in each quadrant. +c (3,4) ==> (# of threshholds, # of quadrants) +c See subroutine getradii for further details. +c wfract_cov Fractional coverage (areal coverage) of winds +c exceeding a certain threshold (34, 50, 64 kts) in +c each quadrant. +c (5,5,3) ==> (# of quadrants + 1, # of distance bins, +c # of thresholds). +c The "extra" array size for quadrants (5, instead of 4) +c is there to hold the total (i.e., "whole disc") +c statistics. +c See subroutine get_fract_wind_cov for further details +c +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c isastorm Character array used in the call to is_it_a_storm, +c tells whether the minimum requirement for an MSLP +c gradient was met (isastorm(1)), whether for the midlat +c and tcgen cases if a closed mslp contour was found +c (isastorm(2)), and if a circulation exists at 850 mb +c (isastorm(3)). Can have a value of 'Y' (requirement +c met), 'N' (requirement not met) or 'U' (requirement +c undetermined, due to the fact that no center location +c was found for this parameter). +c maxmini These 2 arrays contain the i and j indeces for the +c maxminj max/min centers that are found using the rough check +c in first_ges_ctr and subsequent routines. Only needed +c for a midlatitude or a genesis run, NOT needed for a +c TC tracker run. +c stormct Integer: keeps and increments a running tab of the +c number of storms that have been tracked at any time +c across all forecast hours. Used only for midlat or +c tcgen runs. +c gridprs This contains the actual value of the minimum pressure +c at a gridpoint. The barnes analysis will return an +c area-averaged value of pressure; this variable will +c contain the actual minimum value at a gridpoint near +c the lat/lon found by the barnes analysis. +c closed_mslp_ctr_flag This flag keeps track of the value of the +c closed contour flag returned from subroutine +c check_closed_contour. +c vt850_flag This flag keeps track of the value of the flag for +c the 850 mb Vt check. +c----- +c + USE def_vitals; USE inparms; USE tracked_parms; USE error_parms + USE set_max_parms; USE level_parms; USE grid_bounds; USE trkrparms + USE contours; USE atcf; USE radii; USE trig_vals; USE phase + USE gen_vitals; USE structure; USE verbose_output + USE waitfor_parms; USE module_waitfor +c + implicit none +c + type (datecard) inp + type (trackstuff) trkrinfo + type (cint_stuff) contour_info +c + character, allocatable :: closed_mslp_ctr_flag(:,:)*1 + character, allocatable :: vt850_flag(:,:)*1 + character :: r34_check_okay*1 + character :: need_to_expand_r34(4)*1 +c integer, parameter :: nparms=14 +cJ.Peng----2014-10-01------------------------ + integer, parameter :: nparms=16 + + real, allocatable :: prstemp(:),iwork(:) + integer, parameter :: numdist=14,numquad=4,lout=51 + integer, allocatable :: prsindex(:) + integer imax,jmax,ifh,ist,irf,jj,istmp,ifhtemp,itret,ivpa + integer isiret1,isiret2,isiret3,idum,m,iix,jjx,imode,numtcv + integer iha,isa,iua,iva,iza,maxstorm,ivort,ifix,jfix,issret + integer imoa,imoca,iksa,isda,ileadtime,leadtime_check + integer ioaret,ioaxret,ifgcret,ifmret,igugret,isoiret,icccret + integer igrret,igmwret,iorret,ignret,iovret,icbret,igucret,ita +cJ.Peng----2014-10-01---------- + integer ish, irh + + integer ifilret,ifret,iaret,isret,iotmret,iwa,iisa,sl_counter + integer iicret,igcret,pfcret + logical(1), allocatable :: valid_pt(:,:) + logical(1), allocatable :: masked_outc(:,:),masked_out(:,:) + logical(1) readflag(nparms),calcparm(maxtp,maxstorm) + logical(1) tracking_previously_known_storms + logical(1) need_to_flip_lats,need_to_flip_lons + logical(1) file_open + character cvort_maxmin*3,isastorm(3)*1,ccflag*1,gotten_avg_value*1 + character cmaxmin*3,get_last_isobar_flag*1,wcore_flag*1 + character gfilename*120,ifilename*120,gridmove_status*7 + integer vradius(3,4),igridzeta(nlevm1),imeanzeta(nlevm1) +cJ.Peng----2014-10-01---------- + real igridushe,imeanushe + real igridrhum,imeanrhum + real igridtemp,imeantemp + + integer maxmini(maxstorm),maxminj(maxstorm),pdf_ct_bin(16) + integer ifcsthour,stormct,prevstormct,kf,istmspd,istmdir,iggret + integer igiret,iuret,jdum,icount,ilonfix,jlatfix,igpret,ifhmax + integer ibeg,jbeg,iend,jend,ix1,ix2,n,ilev,npts,icpsa,igzvret +cJ.Peng----2014-10-01---------- + integer iushet, irhumt, itempt + real wcore_mean_val,wcore_point_max + + integer igfwret,ioiret,igisret,iofwret,iowsret,igwsret,igscret + integer pdf_ct_tot,lugb,lugi,iret,icmcf,iccfh,ivt8f + integer waitfor_gfile_status,waitfor_ifile_status + integer wait_max_ifile_wait,ivr,r34_good_ct + integer date_time(8) + character (len=10) big_ben(3) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridprs(maxstorm,maxtime) + real wfract_cov(5,5,3) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real ike(max_ike_cats) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmaxwind(maxstorm,maxtime),xmeanzeta + real stderr(maxstorm,maxtime),xval(maxtp),cps_vals(3) + real gridpoint_maxmin,dist,distnm,xknots,xmaxspeed + real uvgeslon,uvgeslat,xavg,stdv,search_cutoff,re,ri,dx,dy + real xinp_fixlat,xinp_fixlon,degrees,plastbar,rlastbar + real xinterval_fhr,cc_time_sum_tot,cc_time_sum_yes + real max_mslp_850,rmax,sdp,wdp,paramb,vtl_slope,vtu_slope + real xsfclon,xsfclat,cc_time_pct,radmax,r34_dist_thresh + real prev_latmax,prev_latmin,prev_lonmax,prev_lonmin + real vradius_km,hold_old_contint,tcv_max_wind_ms + real tcv_mslp_pa,r34_from_tcv,roci_from_tcv + real proci_from_tcv + + character(pfc_cmd_len) :: pfc_final +c + prev_latmax = -999.0 + prev_latmin = -999.0 + prev_lonmax = -999.0 + prev_lonmin = -999.0 + + icmcf = 0 + ivt8f = 0 + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + allocate (closed_mslp_ctr_flag(maxstorm,ifhmax),stat=icmcf) + allocate (vt850_flag(maxstorm,ifhmax),stat=ivt8f) + ! Initialize flags to 'u', not 'n'. That way, + ! when we are evaluating its value back over recent past hours, + ! we can distinguish a "no" value from an initialized value of + ! 'u' for which a storm hadn't yet been detected. + closed_mslp_ctr_flag = 'u' + vt850_flag = 'u' + endif + + allocate (prsindex(maxstorm),stat=iisa) + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iisa /= 0 .or. iva /= 0 .or. iwa /= 0 .or. icmcf /= 0 .or. + & ivt8f /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating prsindex,' + print *,'!!! prstemp or iwork array for storms: iisa = ',iisa + print *,'!!! iva= ',iva,' iwa= ',iwa,' icmcf= ',icmcf + print *,'!!! ivt8f= ',ivt8f + endif + itret = 94 + return + endif + + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + clon = 0.0 + clat = 0.0 + stderr = stermn ! initialize stderr to 0.1 (error_parms) + itret = 0 + xmaxwind = 0.0 + stormct = 0 + + ! It is critical to initialize the gridprs array to something + ! greater than normal atmospheric pressures (I've chosen 9999.99 + ! mb). This is so that in the sort on pressure before stormloop, + ! the top of the sorting index array will be filled with pressure + ! values from active storms, while those inactive 9999 storms + ! will fill the bottom of the sorting index array (prsindex). + + gridprs = 999999.0 + fixlon = -999.0 + fixlat = -999.0 + + if (inp%file_seq == 'multi') then + ! Each tau will have a separate file, starting with unit + ! number 300 (GRIB data) and 800 (GRIB index file) and + ! incrementing upwards from there for each tau. + lugb = 300 + lugi = 800 + else + ! All lead times are included in one big file. These values + ! for lugb and lugi will remain static for all taus. + lugb = 11 + lugi = 31 + endif + + ifh = 1 + + if ( verb .ge. 3 ) then + print *,'top of tracker, ifh= ',ifh,' ifhmax= ',ifhmax + endif + + ifhloop: do while (ifh <= ifhmax) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------*' + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* New forecast hour: ',i4,':',i2.2) + print *,'*-------------------------------------------*' + endif + + if (inp%file_seq == 'multi') then + lugb = lugb + 1 + lugi = lugi + 1 + + call get_grib_file_name (ifh,gfilename,ifilename) + + if (use_waitfor == 'y') then + + ! First check for existence of grib file.... + + call waitfor(trim(gfilename),waitfor_gfile_status,wait_min_age + & ,wait_min_size,wait_max_wait,wait_sleeptime) + if (waitfor_gfile_status /= 0) then + print *,' ' + write(6,405) + write(6,406) wait_max_wait,trim(gfilename) + 405 format('ERROR: TIMEOUT from waitfor for GRIB file.') + 406 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + ! Now check for existence of index file. Use a separate + ! max_wait time -- a much shorter one -- since once the + ! grib file is there, the index file should appear within + ! a matter of seconds. Also, the index file is much + ! smaller, so set the wait_min_size accordingly. + + wait_max_ifile_wait = 180 + wait_min_size = 500 + call waitfor(trim(ifilename),waitfor_ifile_status,wait_min_age + & ,wait_min_size,wait_max_ifile_wait,wait_sleeptime) + if (waitfor_ifile_status /= 0) then + print *,' ' + write(6,415) + write(6,416) wait_max_ifile_wait,trim(ifilename) + 415 format('ERROR: TIMEOUT from waitfor for INDEX file.') + 416 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + endif + + call open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: from open_grib_files, rc= ' + & ,iret + print *,'!!! Files after hour0 are missing, exiting normally' + stop 0 + endif + endif + + print *,'TEST before getgridinfo in sub tracker' + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is CLOSED' + endif + + call getgridinfo (imax,jmax,ifh,dx,dy,lugb,lugi,trkrinfo + & ,need_to_flip_lats,need_to_flip_lons,inp,iggret) + print *,'TEST after getgridinfo in sub tracker, iggret= ',iggret + + if (iggret /= 0) then + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in getgridinfo, rc= ' + & ,iggret + endif + stop 95 + endif + + if (inp%modtyp == 'regional' .and. inp%nesttyp == 'moveable') + & then + if (glatmax == prev_latmax .and. glatmin == prev_latmin .and. + & glonmax == prev_lonmax .and. glonmin == prev_lonmin) then + ! The moveable, nested regional grid has not moved since + ! the last lead time. This could be an indication that the + ! model lost the storm and so the grid has not moved to + ! stay with the cyclone center. Set a flag to indicate this. + gridmove_status = 'stopped' + else + gridmove_status = 'moving' + endif + else + gridmove_status = 'notappl' + endif + + prev_latmax = glatmax + prev_latmin = glatmin + prev_lonmax = glonmax + prev_lonmin = glonmin + + gotten_avg_value = 'n' + +c First, allocate the working data arrays.... + + if (allocated(valid_pt)) deallocate (valid_pt) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) +cJ.Peng----2014-10-01------------------------------- + if (allocated(ushear)) deallocate (ushear) + if (allocated(rhumid)) deallocate (rhumid) + + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + + ! Allocate all of the allocatable arrays.... + + allocate (valid_pt(imax,jmax),stat=ivpa) + allocate (zeta(imax,jmax,nlevzeta),stat=iza) + allocate (u(imax,jmax,nlevs),stat=iua) + allocate (v(imax,jmax,nlevs),stat=iva) + allocate (hgt(imax,jmax,nlevm1),stat=iha) + allocate (slp(imax,jmax),stat=isa) + allocate (tmean(imax,jmax),stat=ita) +cJ.Peng----2014-10-01------------------------------- + allocate (ushear(imax,jmax),stat=ish) + allocate (rhumid(imax,jmax),stat=irh) + + allocate (masked_out(imax,jmax),stat=imoa) + allocate (masked_outc(imax,jmax),stat=imoca) + + ita=0 + icpsa=0 + if (phaseflag == 'y') then + if (phasescheme == 'cps' .or. phasescheme == 'both') then + if (allocated(cpshgt)) deallocate (cpshgt) + allocate (cpshgt(imax,jmax,nlevs_cps),stat=icpsa) + endif + endif + +c if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. +c & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. +c & imoa /= 0 .or. imoca /= 0) then +cJ.Peng----2014-10-01------------------------------- + if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. + & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. + & imoa /= 0 .or. imoca /= 0 .or. ish /= 0 + & .or. irh /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating arrays.' + print *,'!!! iza = ',iza,' iua= ',iua,' iha= ',iha + print *,'!!! iva = ',iva,' isa= ',isa,' icpsa= ',icpsa + print *,'!!! iksa = ',iksa,' isda= ',isda,' ivpa= ',ivpa + print *,'!!! ita = ',ita,' imoa= ',imoa,' imoca= ',imoca + endif + itret = 94 + return + endif + + masked_out = .false. ! Initialize all pts to false at each hr + masked_outc = .false. ! Initialize all pts to false at each hr + + if ( verb .ge. 3 ) then + print *,'in beginning of tracker, imax= ',imax,' jmax= ',jmax + endif + +c Initialize all readflags to NOT FOUND for this forecast time, +c then call subroutine to read data for this forecast time. + + zeta = -9999.0 + u = -9999.0 + hgt = -9999.0 + v = -9999.0 + slp = -9999.0 + tmean = -9999.0 +cJ.Peng----2014-10-01------------- + ushear = -9999.0 + rhumid = -9999.0 + + + readflag = .FALSE. + + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: b4 getdata ... ',i2.2,':',i2.2,':',i2.2) + + call getdata (readflag,valid_pt,imax,jmax,ifh + & ,need_to_flip_lats,need_to_flip_lons,inp,lugb,lugi + & ,trkrinfo) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,32) date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: after getdata ... ',i2.2,':',i2.2,':',i2.2) + +c Count how many parms were successfully read for this fcst time. +c Also, for right now, put the value of readflag into all of the +c calcparms for parameters 3 through 9. Note that in getdata we +c read in 14 parms, but in this next loop we only check the +c readflags up to maxtp (= 11 as of 8/2009). That's because +c parms 12 & 13 are for 500 mb u & v, which are not used for +c tracking, only for calculating the deep layer mean wind for +c the next guess, and parm 14 is the 300-500 mb mean temperature, +c which is used for determining storm phase. Parms 10 & 11 are +c for the near-surface winds, which are used in estimating surface +c winds near the storm, and will now also be used as a secondary +c parameter for position estimates. + + idum = 0 + do irf = 1,maxtp + if (readflag(irf)) idum = idum + 1 + if (irf > 2) then + do jj=1,maxstorm + if (irf == 10 .or. irf == 11) then +ctpm6/14 revert +cJ.Peng---2014-10-03------------------------------ +c print *,'Setting calcparm to false for 10m stuff...' + if (inp%model == 1) then + calcparm(irf,jj) = .false. + else + calcparm(irf,jj) = readflag(irf) + endif + else + calcparm(irf,jj) = readflag(irf) + endif + enddo + endif + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Of ',maxtp,' trackable parms, you read in ',idum + print *,'parms for this fcst hour from the input grib file.' + endif + +c If not enough tracked parms were read in, exit the program.... + + if (idum == 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in subroutine tracker' + print *,'!!! Not enough tracked parms read in from getdata.' + print *,'!!! Check for a problem with the input GRIB file.' + print *,'!!! Model identifier = ',inp%model + print *,'!!! STOPPING EXECUTION FOR THIS MODEL' + endif + itret = 99 + ifhtemp = ifh + do while (ifhtemp <= ifhmax) + do istmp=1,maxstorm + fixlon (istmp,ifhtemp) = -999.0 + fixlat (istmp,ifhtemp) = -999.0 + enddo + ifhtemp = ifhtemp + 1 + enddo +cJ.P08042014 call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cJ.P08042014 call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +c & ,ioaret) + if (ifh == 1) then + ! Per Jim Gross (1/01), if the tracker ran but was unable + ! to get an initial fix (or, in this case, unable to get + ! the data needed to run), write out zeroes for the 00h + ! fixes to indicate that the tracker ran unsuccessfully, + ! but don't write out any subsequent forecast times + ! with zeroes.... + vradius = 0 + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initial value of 'undetermined' + do istmp = 1,maxstorm + if (stormswitch(istmp) /= 3) then + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0,-999.0,inp,istmp + & ,ifcsthour,0.0,0.0,vradius,maxstorm + & ,trkrinfo,-99.0,-99.0,-99.0,cps_vals + & ,wcore_flag,ioaxret) + +cJ.Peng----2014-10-01-------- no changes --------------- + call output_hfip (-999.0,-999.0,inp,istmp + & ,ifh,0.0,0.0,vradius,-99.0,ioaxret) + endif + enddo + endif + return + endif + +c Parameters 1 & 2 are abs vorticity at 850 & 700. If the data +c files had this parm at 850 & 700 (ECMWF & UKMET do NOT), then +c we don't need to re-calculate relative vorticity, we just need +c to subtract out the Coriolis component. If the files did not +c have vorticity, then we need to calculate relative vorticity. +c If we're able to read vorticity or calculate it, then set the +c vorticity calcparms to TRUE for all storms for now. + + do ivort=1,2 + + if (readflag(ivort)) then + + call subtract_cor (imax,jmax,dy,ivort) + + do jj=1,maxstorm + calcparm(ivort,jj) = .TRUE. + enddo + else + if (ivort == 1) then + if (readflag(3) .and. readflag(4)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(1,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + endif + else + if (readflag(5) .and. readflag(6)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(2,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + endif + endif + endif + + enddo + +c Compute the sfc vorticity if sfc_u and sfc_v have been read in. + + if (readflag(10) .and. readflag(11)) then + ! The 3 in the next call to rvcal is to indicate the 3rd level + ! for the zeta array, which is for the surface (or 10m) data. + call rvcal (imax,jmax,dx,dy,3,valid_pt) + do jj=1,maxstorm +ctpm6/14 calcparm(10,jj) = .TRUE. +ctpm6/14 calcparm(11,jj) = .TRUE. +cJ.Peng--------2014-10-27-------------------------- + calcparm(10,jj) = .TRUE. + calcparm(11,jj) = .TRUE. + if (inp%model == 1) then + calcparm(10,jj) = .FALSE. ! Turned off for GFS hires 6/14 + calcparm(11,jj) = .FALSE. ! Turned off for GFS hires 6/14 + endif + enddo + else + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + calcparm(11,jj) = .FALSE. + enddo + endif + +c --------------------------------------------------------------- +c Now call find_maxmin for the variables zeta, hgt and slp. Only +c process those storms for which stormswitch is set to 1. If a +c storm is selected to be processed, we still have to check the +c calcparm for each parameter, to make sure that the particular +c parm exists at that level and is able to be processed. +c +c The following commented-out data statements are just included +c as a reference so you can see the array positioning of the +c different parameters and levels: +c +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,sfc,sfc +c ,100,100,100/ +c data iglev /850,700,850,850,700,700,850,700,0,sfc,sfc +c ,500,500,400/ +c +c NOTE: For mid-latitude cases, we will track ONLY mslp, which +c is why we set all the other calcparms to 'false' just below. + + if (trkrinfo%type == 'midlat') then + do m = 1,maxstorm + calcparm(1,m) = .false. + calcparm(2,m) = .false. + calcparm(3,m) = .false. + calcparm(4,m) = .false. + calcparm(5,m) = .false. + calcparm(6,m) = .false. + calcparm(7,m) = .false. + calcparm(8,m) = .false. + calcparm(10,m) = .false. + calcparm(11,m) = .false. + enddo + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + call sort_storms_by_pressure (gridprs,ifh,maxstorm,prsindex + & ,issret) + if (ifh == 1) then + stormct = numtcv + endif + endif + + prevstormct = stormct + tracking_previously_known_storms = .true. + + stormloop: do sl_counter = 1,maxstorm + + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initialized value of 'undetermined' + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + ist = prsindex(sl_counter) + else + ist = sl_counter + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + + if (ist == (prevstormct + 1)) then + + ! For the mid-latitude and tropical cyclogenesis cases, we + ! need to scan the mslp field to find new storms. If we + ! are at this point inside the if statement in stormloop, + ! then that means we have looped through and attempted to + ! track all storms that have already been found up to this + ! point in the forecast, and we need to scan the field for + ! any new storms at this forecast hour. If this is for + ! forecast hour = 0, then right off the bat we may be + ! scanning the field (if there were no tcvitals records + ! read in for this forecast), since ist = 1 and + ! (prevstormct + 1) = 0 + 1 = 1. All that the call just + ! below to first_ges_center does is return a rough idea + ! of the location of new lows; more specific locations are + ! obtained through the barnes analysis tracking algorithm + ! further below. + + if (readflag(9)) then + if (ifh > 1) then + ! We need the use of 2 different masks. One + ! (masked_out) is to be used when looking for new lows, + ! so that after we find a new low, we mask out the + ! surrounding area so we don't find it on a subsequent + ! search for this forecast hour. The other + ! (masked_outc) is used in the routine to check for a + ! closed contour. If checking for a closed contour + ! at, say 70W/25N, this and surrounding points may have + ! already been masked out in first_ges_center, so "N" + ! would misleadingly/incorrectly be returned from + ! check_closed_contour, so that is why we need 2 masks. + ! But now after the first forecast hour (t=0), the way + ! we have this set up is that we track previously known + ! storms first, and once we're done with them, we + ! search for new storms at that same forecast hour. + ! But when looking for new storms, we need to know the + ! positions of the previously tracked storms at this + ! current forecast hour, so we copy the masked_outc + ! array to masked_out in this case.... + + masked_out = masked_outc + + endif + call first_ges_center (imax,jmax,dx,dy,'mslp',slp + & ,'min',trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) + tracking_previously_known_storms = .false. + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In subroutine tracker, readflag' + print *,'!!! for mslp indicates that the mslp data' + print *,'!!! is not available for this forecast ' + print *,'!!! hour, and it is needed for a "midlat"' + print *,'!!! or "tcgen" run of the tracker. ' + print *,'!!! We will exit....' + print *,'!!! readflag(9) = ',readflag(9) + print *,'!!! ifh= ',ifh + print *,' ' + endif + itret = 98 + return + endif + endif + endif + + xval = 0.0 ! initialize entire xval array to 0 + isastorm = 'U' ! re-initialize flag for each time, each storm + + select case (stormswitch(ist)) + + case (1) + + vradius = 0 + + if ( verb .ge. 2 ) then + print *,' ---------------------------------------------' + print *,' | *** TOP OF STORM LOOP *** ' + print *,' | Beginning of storm loop in tracker for' + print *,' | Storm number ',ist + write (6,418) ifhours(ifh),ifclockmins(ifh) + 418 format (1x,' | Forecast hour: ',i4,':',i2.2) + print *,' | Storm name = ',storm(ist)%tcv_storm_name + print *,' | Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,' ---------------------------------------------' + print *,' ' + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3 + & ,'_',i3.3,a1,'_',i4.4,a1,'_',a3) + + endif +c First, make sure storm is within the grid boundaries... + + call check_bounds (slonfg(ist,ifh),slatfg(ist,ifh),ist,ifh + & ,trkrinfo,icbret) + if (icbret == 95) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + + if (slatfg(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + if (calcparm(1,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,1),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(1,ist),clon(ist,ifh,1),clat(ist,ifh,1) + & ,xval(1),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(2,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,2),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(2,ist),clon(ist,ifh,2),clat(ist,ifh,2) + & ,xval(2),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(7,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,1),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(7,ist) + & ,clon(ist,ifh,7),clat(ist,ifh,7),xval(7) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(8,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,2),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(8,ist) + & ,clon(ist,ifh,8),clat(ist,ifh,8),xval(8) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(9,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for mslp' + endif + + call find_maxmin (imax,jmax,dx,dy,'slp' + & ,slp,'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(9,ist) + & ,clon(ist,ifh,9),clat(ist,ifh,9),xval(9) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(11,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for sfc zeta' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,3),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(11,ist),clon(ist,ifh,11),clat(ist,ifh,11) + & ,xval(11),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + +c Now get centers for V magnitude at 700 & 850 mb. First, +c get a modified guess lat/lon position for V magnitude. +c Do this because it's more crucial to have a better first +c guess position for the wind minimum than it is for the +c other parms, since in addition to the wind minimum at the +c center of the storm, you can also have many more wind +c minima outside the RMW (this is more of a concern in +c smaller and weaker storms). This modified guess position +c will be an average of the first guess position for this +c time and the fix positions for this time from some of the +c other parameters. + + if (calcparm(3,ist) .and. calcparm(4,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_uv_center for 850 mb ' + endif + + call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,850,valid_pt,calcparm(3,ist) + & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo + & ,igucret) + if (igucret /= 0) then + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + endif + else + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + clon(ist,ifh,3) = 0.0 + clat(ist,ifh,3) = 0.0 + endif + endif + + if (calcparm(5,ist).and. calcparm(6,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_uv_center for 700 mb ' + endif + + call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,700,valid_pt,calcparm(5,ist) + & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo + & ,igucret) + if (igucret /= 0) then + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + endif + else + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + clon(ist,ifh,5) = 0.0 + clat(ist,ifh,5) = 0.0 + endif + endif + + if (calcparm(10,ist) .and. igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_uv_center for the surface ' + endif + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,1020,valid_pt,calcparm(10,ist) + & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) + & ,trkrinfo,igucret) + if (igucret /= 0) then + calcparm(10,ist) = .FALSE. + endif + else + calcparm(10,ist) = .FALSE. + clon(ist,ifh,10) = 0.0 + clat(ist,ifh,10) = 0.0 + endif + +c ------------------------------------------------------ +c All of the parameter center fixes have been done. Now +c average those positions together to get the best guess +c fix position. If a center fix is able to be made, then +c call subroutine get_max_wind to get the maximum near- +c surface wind near the center, and then call get_next_ges +c to get a guess position for the next forecast hour. + + if (stormswitch(ist) == 1) then + + call fixcenter (clon,clat,ist,ifh,calcparm + & ,slonfg(ist,ifh),slatfg(ist,ifh),inp + & ,stderr,fixlon,fixlat,xval,maxstorm,ifret) + + if (ifret == 0) then + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'regional')then + if (fixlon(ist,ifh) > (trkrinfo%eastbd + 7.0) .or. + & fixlon(ist,ifh) < (trkrinfo%westbd - 7.0) .or. + & fixlat(ist,ifh) > (trkrinfo%northbd + 7.0) .or. + & fixlat(ist,ifh) < (trkrinfo%southbd - 7.0)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! will NOT be made for this time due' + print *,'!!! the storm being more than 7 degrees' + print *,'!!! outside the user-specified lat/lon' + print *,'!!! bounds for this run. We will stop' + print *,'!!! tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + 432 format (1x,'!!! Fcst hr = ',i4,':',i2.2) + print *,'!!! fixlat= ',fixlat(ist,ifh) + print *,'!!! fixlon= ',fixlon(ist,ifh) + print *,'!!! User East Bound = ',trkrinfo%eastbd + print *,'!!! User West Bound = ',trkrinfo%westbd + print *,'!!! User North Bound = ',trkrinfo%northbd + print *,'!!! User South Bound = ',trkrinfo%southbd + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + +cJ.Peng----2014-10-01--------------------------------------------- + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cJ.Peng----2014-10-01--------------------------------------------- + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + endif + cycle stormloop + endif + endif + else + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + +c Just because we've found a center doesn't mean there is +c actually a storm there. I noticed in the first year that +c for some decaying or just weak storms, the tracker would +c identify a center to follow, but it may have only been +c a weak trough passing by, or something else that's not +c our storm. This next subroutine checks to see that the +c surface pressure gradient and/or tangential winds at +c 850 mb resemble a storm. It is called twice; the first +c time for MSLP, the 2nd time for 850 mb winds. We will +c apply these storm-checking criteria if either the mslp +c or v850 check come back negative. Remember, there +c is the possibility that centers could not be found for +c 1 or both of these parameters, in which case the isastorm +c flag will have a value of 'U', for "undetermined". + + isiret1 = 0; isiret2 = 0; isiret3 = 0 + + if (ifret == 0) then + + if (calcparm(9,ist)) then + + ! Do a check of the mslp gradient.... + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,clon(ist,ifh,9),clat(ist,ifh,9) + & ,xval(9),trkrinfo,isastorm(1),isiret1) + + endif + + ! If we have found a valid mslp gradient, then make + ! a call to fix_latlon_to_ij to (1) get the actual + ! gridpoint value of the mslp (the value previously + ! stored in xval(9) is an area-averaged value coming + ! from the barnes analysis), and (2) to get the + ! (i,j) indeces for this gridpoint to be used in the + ! call to check_closed_contour below. + ! + ! NOTE: If a mslp fix was not made, or if the mslp + ! "isastorm" flag comes back as no, we make the same + ! call to fix_latlon_to_ij, but we use the mean fix + ! position as our input to search around, and then + ! basically we just find the lowest mslp near that + ! mean fix position. There is a check on the value + ! of xinp_fixlat and xinp_fixlon to make sure that + ! they contain valid values and not just the + ! initialized -999 values. + + if (isiret1 == 0 .and. isastorm(1) == 'Y') then + xinp_fixlat = clat(ist,ifh,9) + xinp_fixlon = clon(ist,ifh,9) + else + xinp_fixlat = fixlat(ist,ifh) + xinp_fixlon = fixlon(ist,ifh) + endif + + if (xinp_fixlat > -99.0 .and. xinp_fixlon > -990.0) + & then + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,xinp_fixlon,xinp_fixlat + & ,xval(9),ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + gridprs(ist,ifh) = gridpoint_maxmin + else + ! Search went out of regional grid bounds.... + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + ! For a "tracker" case, check to see if the user has + ! requested to compute and write out the ROCI. If + ! so, then we make a call to check_closed_contour, + ! being sure to specify 999 as the number of levels + ! to check.... + + if (isiret1 == 0 .and. isastorm(1) == 'Y' .and. + & trkrinfo%type == 'tracker') then + + if (trkrinfo%want_oci) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + if (trkrinfo%contint < 400.0) then + hold_old_contint = trkrinfo%contint + trkrinfo%contint = 400.0 + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before going into routine to diagnose' + print *,'the ROCI for a tracker run, the ' + print *,'requested contour interval is being ' + print *,'adjusted up (coarser) to avoid having' + print *,'the contour check routine break and ' + print *,'return an invalid value.' + print *,'User-requested contint value (Pa) = ' + & ,hold_old_contint + print *,'Modified contint value (Pa) = ' + & ,trkrinfo%contint + endif + endif + + masked_outc = .false. + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ' + & ,rlastbar,' nm' + print *,' ' + endif + + endif + + endif + + ! For the midlat & tcgen cases, do a check to see if + ! there is a closed mslp contour. The ifix and jfix + ! values passed into check_closed_contour are the + ! values for the (i,j) at the gridpoint minimum, + ! which was obtained just above from the call to + ! fix_latlon_to_ij. + + if (isastorm(1) == 'Y' .and. isiret1 == 0 .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ',rlastbar + & ,' nm' + print *,' ' + endif + + ! This next bit of code adds a second layer of closed + ! contour checking. This is to decrease the + ! occurrence of interrupted midlat and tcgen tracks, + ! which usually happens when the closed contour + ! criterion is not met for one time period. So in + ! this next code, we check to see if the ccflag was + ! 'y' for at least half the time over the last 24h. + ! For time periods shorter than 24h (e.g., the storm + ! was just detected at 144h and we are now at 156h), + ! the threshold is still that for at least half of + ! the time the system has been detected as a storm, + ! it must have a ccflag value of 'y'. + + if (ccflag == 'y') then + closed_mslp_ctr_flag(ist,ifh) = 'y' + else + closed_mslp_ctr_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & closed_mslp_ctr_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (closed_mslp_ctr_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.50) then + ccflag = 'y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE ON CLOSED CONTOUR CHECK: The' + print *,' ccflag returned for this hour was' + print *,' NO, but a check of recent ccflags' + print *,' indicates that more than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + ccflag = 'n' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!! NOTE ON CLOSED CONTOUR CHECK: The' + print *,'!! ccflag returned for this hour was' + print *,' NO, and a check of recent ccflags' + print *,' indicates that less than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + if (ccflag == 'y') then + isastorm(2) = 'Y' + else if (ccflag == 'n') then + isastorm(2) = 'N' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*---------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*---------------------------------------*' + print *,' ' + endif + + endif + + ! For tropical cyclones, check the avg 850 mb tangential + ! windspeed close to the storm center.... + + if (trkrinfo%type == 'tcgen' .or. + & trkrinfo%type == 'tracker') then + if (calcparm(3,ist)) then + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,clon(ist,ifh,3),clat(ist,ifh,3) + & ,xval(3),trkrinfo,isastorm(3),isiret3) + + if (trkrinfo%type == 'tcgen') then + ! This next bit of code adds a second layer of 850 + ! mb Vt magnitude checking. This is to decrease + ! the occurrence of interrupted tcgen tracks, + ! which occasionally happens for weak storms when + ! this criterion is not met for one time period. + ! So in this next code, we check to see if the + ! vt850_flag was 'y' for at least 75% of the time + ! over the last 24h. For time periods shorter + ! than 24h (e.g., the storm was just detected at + ! 144h and we are now at 156h), the threshold is + ! still that for at least 75% of the time the + ! system has been detected as a storm, it must + ! have a vt850_flag value of 'y'. + + if (isastorm(3) == 'Y') then + vt850_flag(ist,ifh) = 'y' + else + vt850_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & vt850_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - + & fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (vt850_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / + & cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.75) then + isastorm(3) = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ NOTE ON Vt_850 CHECK: The ' + print *,' isastorm flag returned for ' + print *,' this hour was NO, but a' + print *,' check of recent vt850_flags' + print *,' indicates that more than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE ON Vt_850 CHECK: The ' + print *,'!!! isastorm flag returned for ' + print *,' this hour was NO, and a' + print *,' check of recent vt850_flags ' + print *,' indicates that less than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + endif + + endif + endif + + else + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + isastorm(1) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! could not be made for mslp, ' + print *,'!!! therefore we will stop tracking ' + print *,'!!! for this storm.' + endif + + else + isastorm(1) = 'N' + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a TC tracker case, a fix could' + print *,'!!! not be made using any tracked parms,' + print *,'!!! therefore we will stop tracking for' + print *,'!!! this storm.' + endif + + endif + + if ( verb .ge. 3 ) then + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + + endif + + if (isiret1 /= 0 .or. isiret2 /= 0 .or. isiret3 /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: One of the calls to ' + print *,'!!! is_it_a_storm produced an error.' + print *,'!!! Chances are this is from a call to ' + print *,'!!! get_ij_bounds, meaning we are too close' + print *,'!!! to a regional grid boundary to do this ' + print *,'!!! analysis. Processing will continue....' + print *,'!!! isiret1= ',isiret1,' isiret2= ',isiret2 + print *,'!!! isiret3= ',isiret3 + endif + + endif + + if (isastorm(1) == 'N' .or. isastorm(2) == 'N' .or. + & isastorm(3) == 'N') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! At least one of the isastorm flags from' + print *,'!!! subroutine is_it_a_storm is "N", so ' + print *,'!!! either we were unable to find a good ' + print *,'!!! mslp gradient and/or a valid 850 mb ' + print *,'!!! circulation for the storm at this time,' + print *,'!!! or, for the cases of midlat or tcgen ' + print *,'!!! tracking, a closed mslp contour could ' + print *,'!!! not be found, thus we will stop tracking' + print *,'!!! this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! mslp gradient flag = ',isastorm(1) + print *,'!!! closed contour flag = ',isastorm(2) + print *,'!!! 850 mb winds flag = ',isastorm(3) + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + + ! Now do another check for the tracker and tcgen cases. + ! If the isastorm flags for mslp gradient and v850 BOTH + ! came back positive AND you have been able to locate an + ! 850 mb vort center, just do a check to make sure that + ! the distance between the 850 vort center and the mslp + ! center is not too great. + + if (trkrinfo%type == 'tracker' .or. + & trkrinfo%type == 'tcgen') then + if (isastorm(1) == 'Y' .and. isastorm(3) == 'Y' .and. + & calcparm(1,ist) .and. stormswitch(ist) == 1) then + +c if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) >= 25.0) then +c max_mslp_850 = 405.0 +c else if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) < 25.0) then +c max_mslp_850 = 405.0 +c else +c max_mslp_850 = 323.0 +c endif + + max_mslp_850 = 400.0 + + call calcdist (clon(ist,ifh,9),clat(ist,ifh,9) + & ,clon(ist,ifh,1),clat(ist,ifh,1),dist + & ,degrees) + + if (dist > max_mslp_850) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, the dist betw' + print *,'!!! the mslp center & the 850 zeta ' + print *,'!!! center is too great, thus we will' + print *,'!!! stop tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max dist allowed (km) = ' + & ,max_mslp_850 + print *,'!!! Actual distance (km) = ',dist + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Distance between the parm centers for' + print *,'850 zeta and mslp is ',dist,' (km)' + endif + + endif + endif + endif + + ! Do one final check. Check the new fix position and + ! the old fix position and calculate the speed that the + ! storm would have had to travel to get to this point. + ! If that speed exceeds a certain threshold (~60 kt), + ! assume you're tracking the wrong thing and quit. + ! Obviously, only do this for times > 00h. The check + ! in the if statement to see if the previous hour's + ! lats and lons were > -999 is for the midlat and + ! tcgen cases -- remember, they can have genesis at + ! any hour of the forecast, in which case the previous + ! forecast hour's lat & lon would be -999. + + if (ifh > 1 .and. stormswitch(ist) == 1) then + if (fixlon(ist,ifh-1) > -999.0 .and. + & fixlat(ist,ifh-1) > -999.0 ) then + + if (trkrinfo%type == 'midlat') then + xmaxspeed = maxspeed_ml + else + xmaxspeed = maxspeed_tc + endif + + call calcdist (fixlon(ist,ifh-1),fixlat(ist,ifh-1) + & ,fixlon(ist,ifh),fixlat(ist,ifh),dist + & ,degrees) + + ! convert distance from km to nm and get speed. + + distnm = dist * 0.539638 + xinterval_fhr = fhreal(ifh) - fhreal(ifh-1) + xknots = distnm / xinterval_fhr + + if (xknots > xmaxspeed) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, calculated spd' + print *,'!!! of the storm from the last position' + print *,'!!! to the current position is too high,' + print *,'!!! so we will stop tracking this storm' + print *,'!!! (For fear that we are not actually ' + print *,'!!! tracking our storm, but have instead' + print *,'!!! locked onto some other feature....)' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max speed allowed (kt) = ',xmaxspeed + print *,'!!! Actual speed (kt) = ',xknots + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'The average speed that the storm moved' + print *,'at since the previous forecast time is' + & ,xknots,' knots.' + endif + + endif + + endif + + endif + + endif + +c Now get the maximum near-surface wind speed near the storm +c center (get_max_wind). Also, call getradii to get the +c radii in each storm quadrant of gale-force, storm-force +c and hurricane force winds. + + if (readflag(10) .and. readflag(11) .and. ifret == 0 + & .and. stormswitch(ist) == 1) then + call get_max_wind (fixlon(ist,ifh),fixlat(ist,ifh) + & ,imax,jmax,dx,dy,valid_pt,levsfc + & ,xmaxwind(ist,ifh),trkrinfo,rmax,igmwret) +c if (igmwret /= 0 .and. gridmove_status == 'stopped') then + if (igmwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Return code from get_max_wind is /= 0. ' + print *,'!!! rcc= igmwret= ',igmwret + print *,'!!! Also, this is a moveable, regional grid' + print *,'!!! and the grid did not change from last' + print *,'!!! lead time to current one, so what has' + print *,'!!! likely happened is that the storm has ' + print *,'!!! moved close to the edge of the nested ' + print *,'!!! grid domain, but the nested grid itself' + print *,'!!! had stopped moving, probably because it' + print *,'!!! dropped or lost the storm.' + print *,'!!! ' + print *,'!!! TRACKING WILL STOP FOR THIS STORM' + print *,'!!! ' + endif + + stormswitch(ist) = 2 + cycle stormloop + endif + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + + ! For the radii, we encountered a problem with radmax + ! being too small. It was set at 650 km. Hurricane + ! Sandy exceeded this in the models, so the values + ! returned from getradii were close to the default + ! radmax value of 650 km (350 nm), instead of higher. + ! To fix it, we now use an iterative technique, where + ! we start with radmax as a small value (500 km). If + ! getradii returns a value for R34 in a quadrant that + ! does not exceed 0.97*radmax, then that value is ok. + ! If it does exceed 0.97*radmax, then we bump up radmax + ! by 50 km and call getradii again, looking to diagnose + ! radii only in those quadrants where the + ! need_to_expand_r34 flag = 'n'. + + vradius = 0 + r34_check_okay = 'n' + do ivr = 1,4 + need_to_expand_r34(ivr) = 'y' + enddo + radmax = 500.0 ! Initial radmax, in km + + getrad_iter_loop: do while + & (r34_check_okay == 'n' .and. radmax <= 1050.) + + call getradii (fixlon(ist,ifh),fixlat(ist,ifh),imax,jmax + & ,dx,dy,valid_pt,storm(ist)%tcv_storm_id + & ,ifcsthour,vradius,trkrinfo + & ,need_to_expand_r34,radmax,igrret) + r34_dist_thresh = 0.97 * radmax + r34_good_ct = 0 + do ivr = 1,4 + vradius_km = float(vradius(1,ivr)) / 0.5396 + if (vradius_km < r34_dist_thresh) then + r34_good_ct = r34_good_ct + 1 + need_to_expand_r34(ivr) = 'n' + endif + enddo + if (r34_good_ct == 4) then + r34_check_okay = 'y' + endif + radmax = radmax + 50.0 + enddo getrad_iter_loop + + endif + +c If the user has requested so, then call a routine to +c determine the type of cyclone, using Bob Hart's +c cyclone phase space (CPS) algorithms. It is only used +c for times after t=0, since for the first check (of the +c "parameter B" thickness asymmetry), we need to know +c in which direction the storm is moving. Pulling that +c storm movement data off of the tcvitals is not reliable +c since the model storm may not be moving in the same +c direction as the observed storm. However, we could do +c an upgrade later where this storm movement data is +c pulled from the "genesis vitals", which are derived +c from the model forecast data itself, not the obs. + + if (phaseflag == 'y' .and. stormswitch(ist) == 1) then + wcore_flag = 'u' ! 'u' = undetermined +c call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm +c & ,cps_vals,wcore_flag,igpret) +cJ.Peng----2014-10-01--------------------- + call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag + & ,wcore_mean_val,wcore_point_max,igpret) + + endif + + if (structflag == 'y' .or. ikeflag == 'y') then + call get_sfc_center (fixlon(ist,ifh),fixlat(ist,ifh) + & ,clon,clat,ist,ifh,calcparm,xsfclon + & ,xsfclat,maxstorm,igscret) + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,er_wind,sr_wind,er_vr,sr_vr + & ,er_vt,sr_vt,maxstorm,trkrinfo,igwsret) + if (igwsret == 0) then + call output_wind_structure (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),er_wind,sr_wind + & ,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iowsret) + endif + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,wfract_cov,pdf_ct_bin + & ,pdf_ct_tot,maxstorm,trkrinfo,igfwret) + if (igfwret == 0) then + call output_fract_wind (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),wfract_cov,'earth' + & ,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) + endif + endif + + if (ikeflag == 'y' .and. stormswitch(ist) == 1) then + call get_ike_stats (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,ike,sdp,wdp,maxstorm + & ,trkrinfo,igisret) + if (igisret == 0) then + call output_ike (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),ike,sdp,wdp,maxstorm + & ,ioiret) + endif + endif + +c Now print out the current fix position and intensity +c (in knots) to standard output. Conversion for m/s to +c knots (1.9427) is explained in output_atcf. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to fixcenter, fix positions at ' + write (6,442) ifhours(ifh),ifclockmins(ifh) + 442 format (1x,'forecast hour= ',i4,':',i2.2,' follow:') + print *,' ' + endif + + if (ifret == 0 .and. stormswitch(ist) == 1) then + + if ( verb .ge. 3 ) then + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + & ,int((xmaxwind(ist,ifh)*1.9427) + 0.5) + print *,' ' + endif + + ! Only call output routines every atcffreq/100 hours.... + + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + + if (leadtime_check == 0) then + + ifcsthour = ileadtime / 100 + + call output_atcfunix (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + + ! Get the storm motion vector and the speed of + ! motion so that we can output this in the + ! "atcf_sink" forecast text file. + + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'vitals',trkrinfo + & ,ignret) + else + istmdir = -999 + istmspd = -999 + ignret = 0 + endif + + if ( verb .ge. 3 ) then + write (6,617) istmspd,istmdir,ignret + 617 format (1x,'+++ RPT_STORM_MOTION: istmspd= ',i5 + & ,' istmdir= ',i5,' rcc= ',i3) + endif + + ! Call a routine to find the mean & max relative + ! vorticity near the storm at 850 & 700. These will + ! be written out to the "atcf_sink" fcst text file. + + imeanzeta = -99 + igridzeta = -99 + call get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) + +cJ.Peng----2014-10-01----U-shear and RH max and mean values------- + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + + call get_temp_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeantemp,igridtemp,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,itempt) + + call get_ushe_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanushe,igridushe,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,iushet) + + call get_rhum_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanrhum,igridrhum,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,irhumt) +cJ.Peng----2014-10-01---------------------------------------------- + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (fixlon(ist,ifh) +c & ,fixlat(ist,ifh),inp,ist +c & ,ifcsthour,xmaxwind(ist,ifh) +c & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo +c & ,istmspd,istmdir,plastbar,rlastbar,rmax +c & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) +cJ.Peng----2014-10-01---------------------------------------------- + call output_atcf_gen (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo + & ,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + + call output_atcf_sink (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta + & ,igridzeta,cps_vals,plastbar,rlastbar + & ,ioaxret) + + if (inp%model == 12 .and. ifcsthour == 0) then + ! Write vitals for GFS ens control analysis + call output_tcvitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,iovret) + + endif + endif + + ! The exception here is for the call to the output_hfip + ! routine, which will be called for every lead time + ! that is processed.... + + call output_hfip (fixlon(ist,ifh),fixlat(ist,ifh),inp,ist + & ,ifh,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,rmax,ioaxret) + else + + if ( verb .ge. 3 ) then + write (6,452) 'fixpos ',storm(ist)%tcv_storm_id + & ,' fhr= ',ifhours(ifh),ifclockmins(ifh) + & ,' Fix not made for this forecast hour' + 452 format (1x,a7,1x,a4,a6,i4,':',i2.2,a36) + + print *,' ' + print *,'!!! RETURN CODE from fixcenter not equal to 0,' + print *,'!!! or output from is_it_a_storm indicated the' + print *,'!!! system found was not our storm, or the ' + print *,'!!! speed calculated indicated we may have ' + print *,'!!! locked onto a different center, thus a fix' + print *,'!!! was not made for this storm at this ' + print *,'!!! forecast hour.' + print *,'!!! mslp gradient check = ',isastorm(1) + print *,'!!! mslp closed contour check = ',isastorm(2) + print *,'!!! 850 mb winds check = ',isastorm(3) + print *,'!!! fixcenter return code = ifret = ',ifret + print *,' ' + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + + ! For the vt=00h lead time, if the tracker failed to + ! locate a position, we are going to write out an + ! atcfunix that contains the position, intensity, mslp + ! and 34-kt wind radii from TC Vitals for this storm + ! and initial time.... + + tcv_max_wind_ms = float(storm(ist)%tcv_vmax) + tcv_mslp_pa = float(storm(ist)%tcv_pcen) * 100.0 + + ! Convert tcvitals NE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15ne) + if (r34_from_tcv > 0.0) then + vradius(1,1) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,1) = 0 + endif + + ! Convert tcvitals SE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15se) + if (r34_from_tcv > 0.0) then + vradius(1,2) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,2) = 0 + endif + + ! Convert tcvitals SW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15sw) + if (r34_from_tcv > 0.0) then + vradius(1,3) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,3) = 0 + endif + + ! Convert tcvitals NW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15nw) + if (r34_from_tcv > 0.0) then + vradius(1,4) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,4) = 0 + endif + + ! Convert tcvitals roci from km to nm + + if (storm(ist)%tcv_penvrad > 0) then + roci_from_tcv = float(storm(ist)%tcv_penvrad) + rlastbar = roci_from_tcv * 0.5396 + else + rlastbar = -99.0 + endif + + ! Convert tcvitals pressure at roci from km to nm + + if (storm(ist)%tcv_penv > 0) then + proci_from_tcv = float(storm(ist)%tcv_penv) + plastbar = proci_from_tcv * 100.0 + else + plastbar = -99.0 + endif + + write (6,291) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + & ,atcfymdh + 291 format (1x,'NOTE: TCVITALS_USED_FOR_ATCF_F00 ' + & ,' Storm ID: ',a4,' Storm name: ',a9 + & ,' YMDH: ',i10) + + call output_atcfunix (slonfg(ist,ifh) + & ,slatfg(ist,ifh),inp,ist + & ,ifcsthour,tcv_max_wind_ms + & ,tcv_mslp_pa,vradius,maxstorm,trkrinfo + & ,plastbar,rlastbar,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + +cJ.Peng----2014-10-01--------------------------------------------- + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cJ.Peng----2014-10-01--------------------------------------------- + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (trkrinfo%type == 'tracker') then + ! Update 11/11: For a 'tracker' run, i.e., one in + ! which we know that there is an observed storm in + ! the area, we will assume that there was some type + ! of problem in the initialization that prevented + ! the storm from being found. In this case, even + ! though we have written out zeroes for the 00h + ! time, we want to at least try tracking again at + ! the next lead time. Requested by HWRF folks.... + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',ist + write (6,301) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + 301 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + call get_next_ges (slonfg,slatfg,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + stormswitch(ist) = 1 + endif + + endif + cycle stormloop + endif + + +c Now get first guess for next forecast time's position. +c But first, if this is the first time level (ifh=1) and +c the user has requested that storm vitals be output (this +c is usually only done for model analyses in order to get +c an analysis position from one time to the next), we will +c write out a storm vitals record for this time level. +c Note that we have already gotten the next guess position +c info just above for the case of the repeated analysis +c data, so we'll just output the genesis vitals record. + + if (ifh <= ifhmax) then + if (ifh == 1 .and. trkrinfo%out_vit == 'y') then + call output_gen_vitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,istmspd,istmdir,iovret) + endif + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + else + istmdir = -999 + istmspd = -999 + endif + endif + + case (2) + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Case 2 in tracker for stormswitch' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 +cJ.Peng----2014-10-01--------------------------------------------- + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cJ.Peng----2014-10-01--------------------------------------------- + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + endif + + case (3) + continue + +c print *,' ' +c print *,'!!! Case 3 in tracker for stormswitch' +c print *,'!!! Storm name = ',storm(ist)%tcv_storm_name +c print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + + end select + + enddo stormloop + +c if (trkrinfo%type == 'midlat' .or. +c & trkrinfo%type == 'tcgen') then +c call output_tracker_mask (masked_outc,kpds,kgds,lb,ifh +c & ,imax,jmax,iotmret) +c endif + + if(use_per_fcst_command=='y') then +c User wants us to run a command per forecast time + +! Replace %[FHOUR] with forecast hour, %[FMIN] with forecast minute. + +! The %[] format is chosen to avoid shell syntax errors if someone +! includes unknown %[] constructs. A stray , for example, +! would generate syntax errors or unexpected results in some +! shells. + +! If an unrecognized %[xxx] sequence is used, it will be retained in +! the final command. This allows the underlying command to detect +! the unreplaced %[] and use suitable default values or abort, as +! appropriate. + + pfc_final=per_fcst_command + call argreplace(pfc_final,pfc_cmd_len,'%[FHOUR]', & + & ifhours(ifh)) + call argreplace(pfc_final,pfc_cmd_len,'%[FMIN]', & + & iftotalmins(ifh)) + + if(verb.ge.2) then + print *,' ' + print *,'!!! Running per-fcst command' + print *,'!!! Unparsed = ',trim(per_fcst_command) + print *,'!!! Parsed = ',trim(pfc_final) + endif + call run_command(trim(pfc_final),pfcret) + if(pfcret/=0 .and. verb.ge.1) then + print *,' ' + print *,'!!! Non-zero exit status from per-fcst command' + print *,'!!! Command = ',trim(pfc_final) + print *,'!!! Exit status = ',pfcret + print *,'!!! Continuing anyway...' + elseif(pfcret==0 .and. verb.ge.2) then + print *,' ' + print *,'!!! Per-fcst command returned success status (0)' + endif + endif + + ifh = ifh + 1 + if (ifh > ifhmax) exit ifhloop + + if (inp%file_seq == 'multi') then + call baclose(lugb,igcret) + call baclose(lugi,iicret) + if ( verb .ge. 3 ) then + print *,'baclose return code for unit ',lugb,' = igcret = ' + & ,igcret + print *,'baclose return code for unit ',lugi,' = iicret = ' + & ,iicret + endif + + endif + + enddo ifhloop +c +cJ.P08042014 call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cJ.P08042014 call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +c & ,ioaret) +c + 73 format ('fixpos ',a4,' fhr= ',i4,':',i2.2,' Fix position= ' + & ,f7.2,'E (',f6.2,'W)',2x,f7.2,' Max Wind= ',i3,' kts') + + if (allocated(prstemp)) deallocate (prstemp) + if (allocated(prsindex)) deallocate (prsindex) + if (allocated(iwork)) deallocate(iwork) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) +cJ.Peng----2014-10-01---------------------------------- + if (allocated(ushear)) deallocate (ushear) + if (allocated(rhumid)) deallocate (rhumid) + + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(vt850_flag)) deallocate (vt850_flag) + if (allocated(closed_mslp_ctr_flag)) + & deallocate (closed_mslp_ctr_flag) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine argreplace(arg,n,name,val) + ! This subroutine is used to generate the pre-forecast-command + ! It will edit the command (argument "arg") and replace string + ! name with value val. That is how the per-forecast-command + ! has these modifications: + + ! %[FHOUR] -> replace with -> last forecast hour + ! %[FMIN] -> replace with -> last forecast minute + + implicit none + + integer, intent(in) :: n + character(n), intent(inout) :: arg + character(*), intent(in) :: name + integer, intent(in) :: val + + integer found,namelen,i1,i2 + character(n) :: out + + found=index(arg,name) + namelen=len(name) + i1=found-1 ! last char that is before name + i2=found+namelen ! index of last char in name + + if(found==0) return + + out=' ' + + if(found>1 .and. i21) then +! special case: name is at end of string +! hope the value fits... + write(out,'(A,I0)') arg(1:i1),val + elseif(i2= 1.24 .and. dell < 2.49) then ! UKMET + ri = ritrk_most + radinf = 275.0 + npts = 2 + else ! ECMWF + ri = ritrk_coarse + radinf = 350.0 + npts = 1 + endif + + pthresh = trkrinfo%mslpthresh ! These are read in in + vthresh = trkrinfo%v850thresh ! subroutine read_nlists.... + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,parmlon,parmlat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij B, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij B, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print*,' ' + print*,'!!! ERROR in is_it_a_storm from call to' + print*,'!!! get_ij_bounds, stopping processing for ' + print*,'!!! storm number ',ist + endif + + isiret = 92 + return + endif + +c If the input cparm is slp, then check to see that the MSLP +c gradient in any direction from the MSLP center is at least +c 1mb / 200km, or 0.005mb/km. This is based on discussions with +c Morris & Bob, who have had good results using a 2mb/200km +c requirement. Since their model has a much finer resolution than +c all of the models we run the tracker on AND a much better +c depiction of the hurricane vortex, we do not use a requirement +c as strict as theirs, and so make the requirement only half as +c strong as theirs. +c +c If the input cparm is v850, then check to see that there is +c a circulation at 850 mb. We will do this by calculating the +c tangential wind of all points within a specified radius of +c the 850 minimum wind center, and seeing if there is a net +c average tangential wind speed of at least 5 m/s. +c +c UPDATE APRIL 2000: I've relaxed the thresholds slightly from +c 0.005 mb/km to 0.003 mb/km, and the wind threshold from +c 5 m/s to 3 m/s. Also, note that a special case for GDAS has +c been hardwired in that is weaker (0.002 mb/km and 2 m/s). +c That weaker GDAS requirement is for Qingfu's relocation stuff. +c +c UPDATE JULY 2001: The relaxed requirement put in place in +c April 2000 for the GDAS relocation has also been put in place +c for the GFS ensemble relocation. + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the loop. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, ilonfix= ',ilonfix + & ,' jlatfix= ',jlatfix + print *,'ibeg jbeg iend jend = ',ibeg,jbeg,iend,jend + print *,'cparm= ',cparm,' parmlon parmlat = ',parmlon,parmlat + print *,'parmval= ',parmval + print *,' ' + endif + + vtavg = 0.0 + ivt = 0 + + xmaxpgrad = -999.0 + + jloop: do jix = jbeg,jend,bskip + iloop: do iix = ibeg,iend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine is_it_a_storm' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + i = iix - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine ' + print *,'!!! is_it_a_storm for a non-global grid.' + print *,'!!! STOPPING....' + print *,'!!! i= ',i,' imax= ',imax + print *,' ' + endif + + stop 97 + endif + endif + + call calcdist(parmlon,parmlat,glon(i),glat(j),dist,degrees) + + if (dist > radinf .or. dist == 0.0) cycle + + if (defined_pt(i,j)) then + + if (cparm == 'slp') then + pgradient = (slp(i,j) - parmval) / dist + if (pgradient > xmaxpgrad) xmaxpgrad = pgradient + + if ( verb .ge. 3 ) then + write (6,93) i,j,glon(i),glat(j),dist,slp(i,j),pgradient + endif + + if (pgradient > pthresh) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, valid pgradient found.' + print '(a23,f8.5)',' pgradient threshold = ',pthresh + print '(a23,f8.5)',' pgradient found = ',pgradient + print *,'mslp center = ',parmlon,parmlat,parmval + print *,'pgrad loc = ',glon(i),glat(j),slp(i,j) + endif + + stormcheck = 'Y' + exit jloop + endif + endif + + if (cparm == 'v850') then + call getvrvt (parmlon,parmlat,glon(i),glat(j) + & ,u(i,j,nlev850),v(i,j,nlev850),vr,vt,igvtret) + if ( verb .ge. 3 ) then + write (6,91) i,j,glon(i),glat(j),u(i,j,nlev850) + & ,v(i,j,nlev850),vr,vt + endif + + vtavg = vtavg + vt + ivt = ivt + 1 + endif + + endif + + enddo iloop + enddo jloop + + 91 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' u= ',f8.4,' v= ',f8.4,' vr= ',f9.5,' vt= ',f9.5) + + 93 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' dist= ',f8.2,' slp= ',f10.2,' pgradient= ',f8.5) + + if (stormcheck /= 'Y' .and. cparm == 'slp') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, valid pgradient NOT FOUND.' + write (6,94) '!!! (Max pgradient less than ',pthresh,' mb/km)' + 94 format (1x,a29,5x,f8.5,a7) + write (6,95) '!!! Max pgradient (mb/km) found = ',xmaxpgrad + 95 format (1x,a34,f8.5) + print *,' ' + endif + + endif + + if (cparm == 'v850') then + + if (ivt > 0) then + vtavg = vtavg / float(ivt) + else + vtavg = 0.0 + endif + + if (parmlat > 0) then + if (vtavg >= vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (>= +',vthresh,' m/s for a NH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed +',vthresh + & ,' m/s (NH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + else + if (vtavg <= -vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (<= -',vthresh,' m/s for a SH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed -',vthresh + & ,' m/s (SH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + endif + + endif +c + return + end +c +c----------------------------------------------------------------------- +cJ.Peng----2014-10-01------------------------------------ +c----------------------------------------------------------------------- +c subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm +c & ,cps_vals,wcore_flag,igpret) + subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag + & ,wcore_mean_val,wcore_point_max,igpret) + +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure or phase of a cyclone. Initially, we +c will just have it use the Hart cyclone phase space (CPS) scheme. + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trkrparms; USE grid_bounds + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character wcore_flag*1 +cJ.Peng----2014-10-01------------------------------------ + real wcore_mean_val,wcore_point_max + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real cps_vals(3) + real dx,dy,paramb,vtl_slope,vtu_slope + integer imax,jmax,igpret,igcpret,ist,ifh,maxstorm + integer igvpret,igcv1ret,igcv2ret + logical(1) valid_pt(imax,jmax) +c + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,611) + write (6,613) + write (6,615) + write (6,*) ' ' + + 611 format(1x,'#-----------------------------------------------#') + 613 format(1x,'# start of routine to determine cyclone phase...#') + 615 format(1x,'#-----------------------------------------------#') + endif + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + if (ifh > 1) then + + ! This condition that ifh > 1 is so that we *not* do the cps + ! stuff on fhour=0 stuff, since we don't know the storm motion + ! direction for the analysis. + + if (fixlon(ist,ifh-1) > -990.0 .and. + & fixlat(ist,ifh-1) > -990.0) then + + ! Similarly, these next two conditions (previous lat and + ! previous lon > -999) are in there in case we're doing a + ! tcgen or midlat case and this is the *first* time level + ! within a forecast that the storm has been detected (again, + ! we don't yet know the storm heading). + + call get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'lower',vtl_slope + & ,maxstorm,igcv1ret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'upper',vtu_slope + & ,maxstorm,igcv2ret) + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh) + & ,paramb,vtl_slope,vtu_slope + endif + + cps_vals(1) = paramb + cps_vals(2) = vtl_slope + cps_vals(3) = vtu_slope + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diagnostics were requested but will NOT' + print *,' >< be performed for this time level since we ' + print *,' >< are at the first time level for this newly' + print *,' >< found storm, therefore we cannot diagnose' + print *,' >< the model direction of storm movement.' + print *,' >< ifh= ',ifh + endif + + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diags were requested but will NOT be' + print *,' >< performed for this time level since we are at' + print *,' >< time level 1 and we cannot diagnose the model' + print *,' >< direction of storm movement. ' + print *,' >< ifh= ',ifh + endif + + endif + + endif + + 73 format ('cps_stats: ',a4,' lead time= ',i3,':',i2,' paramb= ' + & ,f8.2,' vtl= ',f9.2,' vtu= ',f9.2) + + + if (phasescheme == 'vtt' .or. phasescheme == 'both') then +c call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +cJ.Peng----2014-10-01---------------------------------------- + call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag + & ,wcore_mean_val,wcore_point_max,igvpret) + + + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + + 631 format(1x,'#-------------------------------------------------#') + 633 format(1x,'# End of routine to determine cyclone phase... #') + 635 format(1x,'#-------------------------------------------------#') + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines "Parameter B", which determines +c the degree of thermal symmetry between the "left" and "right" +c hemispheres of a storm, in the layer between 900 and 600 mb. +c We evaluate only those points that are within 500 km of the +c storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zthicksum(2) + real rlonc,rlatc,rlonb,rlatb,xdist,degrees,d,cosarg + real st_heading,st_heading_rad,ricps,dx,dy + real pt_dir,pt_dir_rad,zthick,hemval,paramb + real zthick_right_mean,zthick_left_mean + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer left_ct,right_ct,hemis,icount,maxstorm,ip + logical(1) valid_pt(imax,jmax) +c + ricps = 500.0 + +c ----------------------------------------------------------------- +c First, determine the angle that the storm took getting from the +c last position to the current one. +c ----------------------------------------------------------------- + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + if (d == 0.0) then + + ! Storm is stationary... + st_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print '(a43,f9.3)',' In get_cps_paramb, model storm heading = ' + & ,st_heading + print *,' ' + endif + +c ----------------------------------------------------------------- +c Now call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_paramb from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + igcpret = 92 + return + endif + +c ----------------------------------------------------------------- +c Now loop through all of the points of the subdomain. If the +c point is further than 500 km from the storm center, discard it. +c Otherwise, evaluate the angle from the storm center to this point +c to determine the hemisphere of the point, that is, if the point +c is to the left or the right of the storm track. +c ----------------------------------------------------------------- + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the + ! loop for the evaluation of parameter B. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + left_ct = 0 + right_ct = 0 + zthicksum = 0 + icount = 0 + +c print *,'CPS CORE: ibeg= ',ibeg,' iend= ',iend +c print *,'CPS CORE: jbeg= ',jbeg,' jend= ',jend + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + icount = icount + 1 + +c print *,'CPS CORE: ist= ',ist,' ifh= ',ifh,' j= ',j,' i= ',i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_paramb, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Parameter B will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_paramb' + print *,'!!! for a non-global grid.' + print *,'!!! Parameter B will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_PARAMB....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon= ',glon(ip),' glat= ',glat(j) + print *,'!!! Parameter B will not be computed.' + print *,'!!! EXITING GET_CPS_PARAMB....' + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + + !---------------------------------------------------------- + ! Calculate angle from storm center to point, in a 0-360 + ! framework, clockwise positive. + !---------------------------------------------------------- + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-fixlon(ist,ifh)) * dtr + rlatb = fixlat(ist,ifh) * dtr + d = degrees * dtr + + if (d > 0.) then + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_dir_rad = acos(cosarg) + else + pt_dir_rad = 2*pi - acos(cosarg) + endif + else + pt_dir_rad = 0.0 + endif + + pt_dir = pt_dir_rad / dtr + + !------------------------------------------------------------ + ! Based on the angle that the point is from the storm center, + ! determine if the point is to the left or the right of the + ! storm track. + !------------------------------------------------------------ + + if (st_heading >= 180.0) then + if ((st_heading - pt_dir) > 0.0 .and. + & (st_heading - pt_dir) <= 180) then + hemis = 2 + left_ct = left_ct + 1 + else + hemis = 1 + right_ct = right_ct + 1 + endif + else + if ((pt_dir - st_heading) > 0.0 .and. + & (pt_dir - st_heading) <= 180) then + hemis = 1 + right_ct = right_ct + 1 + else + hemis = 2 + left_ct = left_ct + 1 + endif + endif + + !------------------------------------------------------------ + ! Calculate the 600-900 mb thickness at this point and add + ! the thickness value to the array for the correct "storm + ! hemisphere". + !------------------------------------------------------------ + + zthick = cpshgt(ip,j,7) - cpshgt(ip,j,1) + zthicksum(hemis) = zthicksum(hemis) + zthick + + if ( verb .ge. 3 ) then + write (6,51) rlonb/dtr,rlatb/dtr,rlonc/dtr,rlatc/dtr + & ,st_heading,pt_dir,hemis,zthick + endif + + enddo iloop + enddo jloop + + 51 format (1x,'stlon stlat = ',2(f6.2,2x),' ptlon ptlat = ' + & ,2(f6.2,2x),' sthead= ',f6.2,' ptdir= ',f6.2,' hemis= ' + & ,i1,' zthick= ',f7.2) + +c ------------------------------------------------------------------ +c Now calculate parameter B. The hemval parameter = +1 for storms +c in the Northern Hemisphere and -1 for Southern Hemisphere storms. +c ------------------------------------------------------------------ + + zthick_right_mean = zthicksum(1) / float(right_ct) + zthick_left_mean = zthicksum(2) / float(left_ct) + + if (fixlat(ist,ifh) < 0.0) then + hemval = -1.0 + else + hemval = 1.0 + endif + + paramb = hemval * (zthick_right_mean - zthick_left_mean) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' right_ct= ',right_ct,' left_ct= ',left_ct + print *,' zthicksum(1)= ',zthicksum(1) + print *,' zthicksum(2)= ',zthicksum(2) + print *,' zthick_right_mean= ',zthick_right_mean + print *,' zthick_left_mean= ',zthick_left_mean + print *,' hemval= ',hemval + print *,' END of get_cps_paramb, paramb= ',paramb + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,clayer,vth_slope,maxstorm,igcvret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines the thermal wind profile for +c either the lower troposphere (i.e., between 600 and 900 mb) or the +c upper troposphere (i.e., between 300 and 600 mb). We evaluate +c only those points that are within 500 km of the storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character clayer*5 + real tmp1,tmp2,tmp3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zmax(7),zmin(7),zdiff(7),xlolevs(7),xhilevs(7),plev(7) + real dlnp(7),dzdlnp(7),dz(7),lnp(7) + real vth_slope,xdist,degrees,d,cosarg + real ricps,dx,dy,R2 + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j,k,kix + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igcvret,igiret + integer kbeg,kend,icount,maxstorm,ip + logical(1) valid_pt(imax,jmax) + + data xlolevs /900.,850.,800.,750.,700.,650.,600./ + data xhilevs /600.,550.,500.,450.,400.,350.,300./ +c data xlolevs /90000.,85000.,80000.,75000.,70000.,65000.,60000./ +c data xhilevs /60000.,55000.,50000.,45000.,40000.,35000.,30000./ +c + ricps = 500.0 + plev = 0.0 + + if (clayer == 'lower') then + kbeg = 1 + kend = 7 + plev = xlolevs + else + kbeg = 7 + kend = 13 + plev = xhilevs + endif + +c ----------------------------------------------------------------- +c First, call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_vtl from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + igcvret = 92 + return + endif + +c ------------------------------------------------------------------ +c Now loop through all of the points of the subdomain at each level. +c If a point is further than 500 km from the storm center, discard +c it. Otherwise, evaluate the gp height at the point to determine +c if it is a max or a min for the given level. Store the max and +c min height at each level in an array. +c ------------------------------------------------------------------ + +c ! We will want to speed things up for finer resolution grids. +c ! We can do this by skipping some of the points in the +c ! loop for the evaluation of parameter B. +c +c if ((dx+dy)/2. > 0.20) then +c bskip = 1 +c else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then +c bskip = 2 +c else if ((dx+dy)/2. <= 0.10) then +c bskip = 3 +c endif + + bskip = 1 ! Don't do any skipping for now.... + + zmax = -9999999.0 + zmin = 9999999.0 + zdiff = 0.0 + lnp = 0.0 + + levloop: do k = kbeg,kend + + if (kbeg == 7) then + ! processing upper layers (600-300 mb) + kix = k - 6 + else + ! processing lower layers (900-600 mb) + kix = k + endif + + lnp(kix) = log(plev(kix)) + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + icount = icount + 1 + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_vth, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_vth' + print *,'!!! for a non-global grid.' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_VTH....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j,' k= ',k + & ,' clayer= ',clayer + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon(ip)= ',glon(ip),' glat= ',glat(j) + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! EXITING GET_CPS_VTH....' + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + + tmp1 = zmax(kix) + tmp2 = cpshgt(ip,j,k) + tmp3 = zmin(kix) + + zmax(kix) = max(tmp1,tmp2) + zmin(kix) = min(tmp3,tmp2) + +c zmax(kix) = max(zmax(kix),cpshgt(ip,j,k)) +c zmin(kix) = min(zmin(kix),cpshgt(ip,j,k)) + + enddo iloop + enddo jloop + + zdiff(kix) = zmax(kix) - zmin(kix) + + enddo levloop + +c ------------------------------------------------------------------ +c Now calculate the vertical derivative of the gp height, that is, +c d(dz)/d(ln(p)). Here, zdiff is the gp height perturbation at a +c given level, calculated in the loop above; dz is the vertical +c change in that perturbation from one level to the next. +c ------------------------------------------------------------------ + + dz = 0.0 + dlnp = 0.0 + dzdlnp = 0.0 + + do k = 2,7 + dz(k) = zdiff(k) - zdiff(k-1) + dlnp(k) = log(plev(k)) - log(plev(k-1)) + dzdlnp(k) = dz(k) / dlnp(k) + enddo + +c ------------------------------------------------------------------ +c Now call a correlation routine to get the slope of a regression +c line. The independent variable that we input is dlnp, the change +c in log of pressure with height. The dependent variable is +c dzdlnp, the vertical change in the height perturbation with +c respect to the change in pressure. The slope that is returned +c defines whether we've got a cold core or warm core system. +c See Hart (MWR, April 2003, Vol 131, pp. 585-616) for more +c details, specifically his Fig. 3 and the discussion surrounding. +c Note that in the call to calccorr, we are sending only 6 of the +c 7 elements of the dlnp and dzdlnp arrays, beginning with the +c 2nd element of each. That's because the first array value for +c each of those arrays is empty, since in the loop just above, we +c start with kbeg+1, not kbeg. +c ------------------------------------------------------------------ + + call calccorr(lnp(2),zdiff(2),6,R2,vth_slope) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ In get_cps_vth, values for vth follow for ' + & ,'lead time= ',ifhours(ifh),':',ifclockmins(ifh),' ' + & ,storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' ... clayer = ',clayer + print *,' ' + endif + + do k = kbeg,kend + + if (kbeg == 7) then + kix = k - 6 + else + kix = k + endif + + if ( verb .ge. 3 ) then + print *,' ' + write (6,31) k,plev(kix),zmax(kix),zmin(kix),zdiff(kix) + if (kix > 1) then + write (6,32) plev(kix),log(plev(kix)) + & ,plev(kix-1),log(plev(kix-1)) + write (6,33) dz(kix),dlnp(kix),dzdlnp(kix) + else + write (6,34) + endif + endif + + enddo + + 31 format (1x,' +++ k= ',i2,' press= ',f8.1,' zmax= ',f7.2 + & ,' zmin= ',f7.2,' zdiff= ',f7.2) + 32 format (1x,' ln(',f7.1,')= ',f9.6,' ln(',f7.1,')= ',f9.6) + 33 format (1x,' dz= ',f7.2,' dlnp= ',f9.6,' dzdlnp= ',f9.3) + 34 format (1x,' --- First level... no derivatives done...') +c + return + end +c +C---------------------------------------------------- +C +C---------------------------------------------------- + subroutine calccorr(xdat,ydat,numpts,R2,slope) +c +c This subroutine is the main driver for a series of +c other subroutines below this that will calculate the +c correlation between two input arrays, xdat and ydat. +c +c INPUT: +c xdat array of x (independent) data points +c ydat array of y (dependent) data points +c numpts number of elements in each of xdat and ydat +c +c OUTPUT: +c R2 R-squared, the coefficient of determination +c slope Slope of regression line +c +c xdiff array of points for xdat - xmean +c ydiff array of points for ydat - ymean +c yestim array of regression-estimated points +c yresid array of residuals (ydat(i) - yestim(i)) + + USE verbose_output + + implicit none + + real xdat(numpts),ydat(numpts) + real xdiff(numpts),ydiff(numpts) + real yestim(numpts),yresid(numpts) + real xmean,ymean,slope,yint,R2 + integer numpts,i + +c + call getmean(xdat,numpts,xmean) + call getmean(ydat,numpts,ymean) +c + call getdiff(xdat,numpts,xmean,xdiff) + call getdiff(ydat,numpts,ymean,ydiff) +c + call getslope(xdiff,ydiff,numpts,slope) + yint = ymean - slope * xmean +c + call getyestim(xdat,slope,yint,numpts,yestim) + call getresid(ydat,yestim,numpts,yresid) +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * CPS Thermal wind regression details * ' + print *,' *--------------------------------------------------* ' + endif + + call getcorr(yresid,ydiff,numpts,R2) + + if ( verb .ge. 3 ) then + print *,' i ydat xdat ydiff xdiff e' + & ,' e2 ydiff2' + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + do i = 1,numpts + write(6,'(2x,i3,2x,f7.2,2x,f7.4,2x,f7.2,2x,f7.4,3(2x,f7.2))') + & i,ydat(i),xdat(i),ydiff(i) + & ,xdiff(i),yresid(i),yresid(i)*yresid(i) + & ,ydiff(i)*ydiff(i) + enddo + + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + print *,' ' + write (6,'(1x,a13,f9.3,3x,a5,f7.2)') ' means: y: ',ymean + & ,' x: ',xmean + + write (6,*) ' ' + write (6,30) 'slope= ',slope,' y-intercept = ',yint + 30 format (2x,a7,f10.3,a23,f10.3) + if (slope .gt. 0.0) then + write(6,40) 'Regression equation: Y = ',yint,' + ',slope + else + write(6,40) 'Regression equation: Y = ',yint,' - ' + & ,abs(slope) + endif + 40 format (2x,a27,f8.2,a3,f8.2,'X') +c + print *,' ' + write (6,'(1x,a17,f7.4,5x,a7,f7.4)') ' R2(r_squared) = ',R2 + & ,' r = ',sqrt(R2) + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * End of regression details * ' + print *,' *--------------------------------------------------* ' + endif + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getmean(xarr,inum,zmean) +c +c This subroutine is part of the correlation calculation, +c and it simply returns the mean of the input array, xarr. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c +c OUTPUT: +c zmean mean of data values in xarr + + implicit none + + real xarr(inum) + real xsum,zmean + integer i,inum +c + xsum = 0.0 + do i = 1,inum + xsum = xsum + xarr(i) + enddo +c + zmean = xsum / float(MAX(inum,1)) +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getdiff(xarr,inum,zmean,zdiff) +c +c This subroutine is part of the correlation calculation, +c and it returns in the array zdiff the difference values +c between each member of the input array xarr and the +c mean value, zmean. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c zmean mean of input array (xarr) +c +c OUTPUT: +c zdiff array containing xarr(i) - zmean + + implicit none + + real xarr(inum),zdiff(inum) + real zmean + integer i,inum +c + do i = 1,inum + zdiff(i) = xarr(i) - zmean + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + + subroutine getslope(xarr,yarr,inum,slope) +c +c This subroutine is part of the correlation calculation, +c and it returns the slope of the regression line. +c +c INPUT: +c xarr input array of xdiffs (x - xmean) +c yarr input array of ydiffs (y - ymean) +c inum number of points in x & y arrays +c +c OUTPUT: +c slope slope of regression line + + real xarr(inum),yarr(inum) + real slope,sumxy,sumx2 + integer i,inum + +c First sum up the xarr*yarr products.... + + sumxy = 0.0 + do i = 1,inum + sumxy = sumxy + xarr(i) * yarr(i) + enddo + +c Now sum up the x-squared terms.... + + sumx2 = 0.0 + do i = 1,inum + sumx2 = sumx2 + xarr(i) * xarr(i) + enddo + +c Now get the slope.... + + slope = sumxy / sumx2 + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getyestim(xarr,slope,yint,inum,yestim) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the predicted y-values using the +c regression equation that has been calculated. +c +c INPUT: +c xarr array of x data points +c slope slope of the calculated regression line +c yint y-intercept of the calculated regression line +c inum number of input points +c +c OUTPUT: +c yestim array of y pts estimated from regression eqn. + + implicit none + + real xarr(inum),yestim(inum) + real slope,yint + integer i,inum +c + do i = 1,inum + yestim(i) = yint + xarr(i) * slope + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getresid(yarr,yestim,inum,yresid) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the residual values between the +c input y data points and the y-estim predicted y values. +c +c INPUT: +c yarr array of y data points +c yestim array of y pts estimated from regression eqn. +c inum number of input points +c +c OUTPUT: +c yresid array of residuals (ydat(i) - yestim(i)) + + implicit none + + real yarr(inum),yestim(inum),yresid(inum) + integer i,inum +c + do i = 1,inum + yresid(i) = yarr(i) - yestim(i) + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getcorr(yresid,ydiff,inum,R2) +c +c This subroutine is part of the correlation calculation, +c and it does the actual correlation calculation. +c +c INPUT: +c yresid array of residuals (ydat(i) - yestim(i)) +c ydiff array of points for ydat - ymean +c inum number of points in the arrays +c +c OUTPUT: +c R2 R-squared, the coefficient of determination + + USE verbose_output + + implicit none + + real yresid(inum),ydiff(inum) + real R2,sumyresid,sumydiff + integer i,inum +c + sumyresid = 0.0 + sumydiff = 0.0 + + do i = 1,inum + sumyresid = sumyresid + yresid(i) * yresid(i) + sumydiff = sumydiff + ydiff(i) * ydiff(i) + enddo + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,30) 'Sum of y-residuals squared (e2) = ',sumyresid + write (6,30) 'Sum of y-diffs squared (ydiff2) = ',sumydiff + write (6,*) ' ' + 30 format (1x,a35,f10.2) + endif + +c if (sumydiff == 0.0) then +c R2=1.0 +c else +c R2 = 1 - sumyresid / sumydiff +c endif +c PENG 05/14/2018 Bug-fixed for R2 calculation with FENS job crashed. + if (sumyresid .lt. sumydiff) then + if (sumydiff .le. 0.000001) then + R2 = 1.0 + else + R2 = 1 - sumyresid / sumydiff + endif + else + R2=0.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- +c subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +cJ.Peng----2014-10-01------------------------------------------------ + subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag + & ,wcore_mean_val,wcore_point_max,igvpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. Here, we are only looking +c at the mid-to-upper tropospheric warm anomaly at the center of +c the storm. The temperature data that we are searching through in +c the tmean array should be the 300-500 mb mean temperature data. +c The criteria in this algorithm are based loosely on Vitart's +c criteria for warm core checking, but the nuts & bolts of the +c subroutine use algorithms from this tracker, including the barnes +c analysis. First, we locate the warm core with the find_maxmin +c routine. Then we use the check_closed_contour routine to see if +c there is a closed temperature contour surrounding the warm core. +c +c INPUT: +c inp +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c inp contains input date and model number information +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c ist integer storm number (internal to the tracker) +c ifh integer index for lead time +c trkrinfo derived type containing grid info on user boundaries +c fixlon array containing found fix longitudes +c fixlat array containing found fix latitudes +c valid_pt Logical; bitmap indicating if valid data at that pt. +c maxstorm maximum # of storms to be handled +c +c OUTPUT: +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c igvpret Return code for this subroutine. +c +c LOCAL: +c wcore_mean_val barnes-averaged value of the temperature at the +c location where the tracker found the warm core. +c wcore_point_max max temperature found at a gridpoint near the +c location where the tracker found the warm core using +c barnes analysis. + + USE set_max_parms; USE grid_bounds; USE trkrparms; USE contours + USE tracked_parms; USE gen_vitals; USE def_vitals; USE inparms + USE phase + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo,wcore_trkrinfo + type (cint_stuff) wcore_contour_info + type (datecard) inp + + character*1 get_last_contour_flag,wcore_flag + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dx,dy,wcore_mean_val,wcore_mean_lon,wcore_mean_lat + real wcore_point_max,tlastcont,rlastcont,tlastout,rlastout + integer imax,jmax,igvpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer icount,maxstorm,ip,ifmret,ifilret,ifix,jfix,icccret + integer num_check_conts + logical(1) valid_pt(imax,jmax),compflag,wcore_mask(imax,jmax) + logical(1) output_file_open +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of get_vtt_phase *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for warm core at hour ',i4,':',i2.2) + write (6,103) wcore_depth + 103 format (1x,'* Warm core depth threshold (wcore_depth) = ',f7.2) + print *,'*-------------------------------------------------*' + endif + +c ------------------------------------------------------------ +cJ.Peng----2014-10-01-------------------------------- + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + + wcore_mask = .false. + wcore_mean_lon = -999.0 + wcore_mean_lat = -999.0 + wcore_trkrinfo = trkrinfo ! set equal to values from trkrinfo... + wcore_trkrinfo%contint = wcore_depth ! ...except use the warm + ! core contour interval specified by + ! the user in the extrkr.sh script. + +c ------------------------------------------------------------ +c First, call find_maxmin to locate the warm core + + call find_maxmin (imax,jmax,dx,dy,'tmp' + & ,tmean,'max',ist,fixlon(ist,ifh),fixlat(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,compflag + & ,wcore_mean_lon,wcore_mean_lat,wcore_mean_val + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + +c ------------------------------------------------------------ +c Once find_maxmin returns a value and a location for the +c barnes-averaged value of a warm core, then make a call to +c fix_latlon_to_ij to (1) get the actual gridpoint value of the +c temperature (the value stored in wcore_mean_val is an +c area-averaged value coming from the barnes analysis), and +c (2) to get the (i,j) indeces for this gridpoint to be used in +c the call to check_closed_contour below. + + if (wcore_mean_lat > -99.0 .and. wcore_mean_lon > -990.0) then + call fix_latlon_to_ij (imax,jmax,dx,dy,tmean,'max' + & ,valid_pt,wcore_mean_lon,wcore_mean_lat + & ,wcore_mean_val,ifix,jfix,wcore_point_max,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Warm core stats: ' + write (6,105) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_mean_lon,360.-wcore_mean_lon + & ,wcore_mean_lat,wcore_mean_val + write (6,106) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,ifix,jfix,wcore_point_max + endif + + else + ! Search went out of regional grid bounds.... + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN get_vtt_phase. The call to ' + print *,'!!! fix_latlon_to_ij returned a non-zero return ' + print *,'!!! code, which means that the search for the fix' + print *,'!!! i and j went out of bounds for a regional ' + print *,'!!! grid. This should have been caught in a ' + print *,'!!! previous call to find_maxmin for one of the ' + print *,'!!! various fix parms. In any event, we will not' + print *,'!!! search for a warm core for this storm and ' + print *,'!!! lead time.' + print *,' ' + write (6,115) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,'U',-999.99,-9999.99 + endif + + igvpret = 95 + wcore_flag = 'u' + return + endif + endif + + 105 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' mean_lon: ',f7.2,'E' + & ,1x,'(',f7.2,'W)',2x,'mean_lat: ',f7.2,2x + & ,'wcore_mean_val(K): ',f7.3) + 106 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' ifix: ',i5,2x + & ,' jfix: ',i5,2x,'wcore_point_max(K): ',f7.3) + + +c ------------------------------------------------------------ +c The Vitart scheme specifies that the temperature must decrease +c by at least 1.0C in all directions from the warm core center +c within a distance of 8 deg. A rigorous check of this criterion +c is performed here by utilizing the check_closed_contour routine. +c If we have a closed contour in the temperature field +c surrounding the warm core (using a 1 deg K interval), that +c criterion is satisfied. For diagnostic purposes, we set the +c value of num_check_conts to 999 in order to keep searching for +c all contours surrounding the warm core, and this allows us to +c get an idea of the "depth" or magnitude of the warm core when +c the tlastcont and rlastcont values are returned. + + wcore_contour_info%numcont = maxconts + num_check_conts = 999 + + get_last_contour_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,tmean + & ,valid_pt,wcore_mask,wcore_flag,'max',wcore_trkrinfo + & ,num_check_conts,wcore_contour_info,get_last_contour_flag + & ,tlastcont,rlastcont,icccret) + + if (wcore_flag == 'y') then + tlastout = tlastcont + rlastout = rlastcont/0.539638 + else + tlastout = -999.0 + rlastout = -9999.0 + endif + + if ( verb .ge. 3 ) then + write (6,115) storm(ist)%tcv_storm_id,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_flag,tlastout,rlastout + + 115 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2 + & ,' wcore_flag= ',a1,2x,' Temp of last contour(K) = ' + & ,f7.2,2x,'Radius of last contour(km) = ',f8.2) + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_sfc_center (xmeanlon,xmeanlat,clon + & ,clat,ist,ifh,calcparm,xsfclon,xsfclat + & ,maxstorm,igscret) +c +c ABSTRACT: This subroutine computes a modified lat/lon fix position +c to use as the input center position for the subroutines that +c follow which calculate surface-wind related values. The reason +c for this is that since we are concerned with the positioning of +c low-level wind features (e.g., rmax), we want the center position +c to be based solely on low-level features. We'll use mslp and the +c min in the sfc wind speed. If a center fix was unable to be made +c at this forecast hour for mslp and low-level winds, then we will +c stick with just using the mean position we got using all the other +c parameters. +c +c INPUT: +c xmeanlon The mean center longitude computed from all the various +c parameter fixes found in array clon +c xmeanlat The mean center latitude computed from all the various +c parameter fixes found in array clat +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Index for storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c (if a parameter fix could not be made at this forecast +c hour, then calcparm is set to false for this time for +c that parameter). +c maxstorm Maximum number of storms that can be tracked +c +c OUTPUT: +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c igscret Return code from this subroutine + + USE set_max_parms + USE verbose_output + + implicit none + + integer ist,ifh,ipct,igscret,maxstorm + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmeanlon,xmeanlat + real xsfclon,xsfclat,xlonsum,xlatsum + logical(1) calcparm(maxtp,maxstorm) + + ipct = 0 + xlonsum = 0.0 + xlatsum = 0.0 + + ! Do NOT include MSLP for the surface center at this time. +c if (calcparm(9,ist)) then +c ipct = ipct + 1 +c xlonsum = xlonsum + clon(ist,ifh,9) +c xlatsum = xlatsum + clat(ist,ifh,9) +c endif + + if (calcparm(10,ist)) then + ! NOTE: Put double weighting on surface wind center if + ! the tracker was able to find a center for it.... + ipct = ipct + 2 + xlonsum = xlonsum + 2.*clon(ist,ifh,10) + xlatsum = xlatsum + 2.*clat(ist,ifh,10) + endif + + if (calcparm(11,ist)) then + ! This is for the sfc vorticity center.... + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,11) + xlatsum = xlatsum + clat(ist,ifh,11) + endif + + if (ipct > 0) then + xsfclon = xlonsum / float(ipct) + xsfclat = xlatsum / float(ipct) + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In get_fract_wind_cov, CANNOT get modified fix ' + print *,'!!! position because the parameter fixes for mslp' + print *,'!!! and the sfc winds could not be obtained at this' + print *,'!!! forecast hour. ist= ',ist,' ifh= ',ifh + print *,'!!! We will use the fixlon and fixlat values for' + print *,'!!! this forecast hour.' + endif + + xsfclon = xmeanlon + xsfclat = xmeanlat + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ In get_sfc_center, modified fix (mslp + sfc_winds)' + print *,'+++ position follows: ' + print *,'+++ ' + print *,'+++ mslp: lon: ',clon(ist,ifh,9),' lat: ' + & ,clat(ist,ifh,9) + print *,'+++ sfc_winds: lon: ',clon(ist,ifh,10),' lat: ' + & ,clat(ist,ifh,10) + print *,'+++ sfc_vorticity: lon: ',clon(ist,ifh,11),' lat: ' + & ,clat(ist,ifh,11) + print *,'+++ multi-parm mean: lon: ',xmeanlon,' lat: ' + & ,xmeanlat + print *,'+++ sfc-only mean: lon: ',xsfclon,' lat: ',xsfclat + endif + + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,er_wind,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm + & ,trkrinfo,igwsret) +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure of the low level winds of a cyclone. +c The algorithm will search out at specified distances from the +c storm center along 45-degree radials and bilinearly interpolate +c the winds to points along those radials. This will be done +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 radials: NE, SE, +c SW and NW (45,135,225,315). For the storm-relative estimates, +c these radials will be computed at the same relative angles (i.e., +c 45,135,225,315), but with respect (positive clockwise) to the +c direction of storm motion. For example, for a storm moving with +c a heading of 280, the wind structure is evaluated at these +c radials: 325 (front-right; 45 deg CW from heading), 55 (back- +c right; 135 deg CW from heading), 145 (back-left; 225 deg CW from +c heading), 235 (front-left; 315 deg CW from heading). +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated +c er_wind: Quadrant winds in earth-relative framework +c sr_wind: Quadrant winds in storm-relative framework +c er_vr: Quadrant radial winds in earth-relative framework +c sr_vr: Quadrant radial winds in storm-relative framework +c er_vt: Quadrant tangential winds in earth-relative framework +c sr_vt: Quadrant tangential winds in storm-relative framework + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4 + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igvtret,ipct,maxstorm + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,xsfclon,xsfclat + logical(1) valid_pt(imax,jmax) +c + data rdist/10.,25.,50.,75.,100.,125.,150.,200.,250.,300.,350. + & ,400.,450.,500./ + + igwsret = 0 + + er_wind = 0.0 + sr_wind = 0.0 + er_vr = 0.0 + er_vt = 0.0 + sr_vr = 0.0 + sr_vt = 0.0 + +c ----------------------------------------------------------------- +c Now determine the angle that the storm took getting from the +c last position to the current one. If this is the initial time, +c use the observed direction of motion from the TC Vitals. This +c may not match up with the model storm's initial direction of +c motion, but it is all we have available to us in order to get +c a heading estimate for the initial time. This storm heading +c information will be used for the storm-relative profiles. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_wind_structure, fhr= ',fhreal(ifh) + & ,' ',storm(ist)%tcv_storm_id + & ,' ',storm(ist)%tcv_storm_name + print '(a25,a23,f9.3)',' In get_wind_structure, ' + & ,' model storm heading = ',st_heading + print *,' ' + endif + + endif + +c ----------------------------------------------------------------- +c Get the profiles for the earth-relative coordinate system. +c Start with NE, then SE, SW, and NW. +c ----------------------------------------------------------------- + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *****************************************************' + print *,' Wind Structure: distbear bilin interp starts here.' + print *,' *****************************************************' + print *,' ' + endif + + do iquad = 1,4 + + bear = ((iquad-1) * 90.) + 45. + + if ( verb .ge. 3 ) then + print *,'structure iquad= ',iquad,' earth-relative bear= ' + & ,bear + endif + + do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,' ' + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,' ' + print '(5(a10,f7.2))',' sfclat= ',xsfclat + & ,' sfclon= ',xsfclon + & ,' rdist= ',rdist(idist),' targlat= ',targlat + & ,' targlon= ',targlon + print '(19x,a8,f7.2,35x,a9,f7.2)','sfclon= ',360.-xsfclon + & ,'targlon= ',360.-targlon + endif + + call bilin_int_uneven (targlat,targlon,rdist(idist) + & ,dx,dy,imax,jmax,trkrinfo,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon,rdist(idist) + & ,dx,dy,imax,jmax,trkrinfo,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + er_wind(iquad,idist) = sqrt (xintrp_u**2 + xintrp_v**2) + + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,er_vr(iquad,idist) + & ,er_vt(iquad,idist),igvtret) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + er_wind(iquad,idist) = -999.0 + er_vr(iquad,idist) = -999.0 + er_vt(iquad,idist) = -999.0 + else + igwsret = 95 + return + endif + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2)',' intrp wind speed= ' + & ,er_wind(iquad,idist),' (in kts)= ' + & ,er_wind(iquad,idist)*1.9427 + endif + + enddo + + enddo + +c ----------------------------------------------------------------- +c Get the profiles for the storm-relative coordinate system. +c Start with the front-right quadrant and go clockwise through +c back-right, back-left and front-left. +c ----------------------------------------------------------------- + + if ( verb .ge. 3 ) then + print *,' ' + endif + + do iquad = 1,4 + + temp_bear = st_heading + ((iquad-1) * 90.) + 45. + bear = mod(temp_bear,360.) + + if ( verb .ge. 3 ) then + print *,'structure iquad= ',iquad,' storm-relative bear= ' + & ,bear + endif + + do idist = 1,numdist + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + call bilin_int_uneven (targlat,targlon,rdist(idist) + & ,dx,dy,imax,jmax,trkrinfo,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon,rdist(idist) + & ,dx,dy,imax,jmax,trkrinfo,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + sr_wind(iquad,idist) = sqrt (xintrp_u**2 + xintrp_v**2) + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,sr_vr(iquad,idist) + & ,sr_vt(iquad,idist),igvtret) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + sr_wind(iquad,idist) = -999.0 + sr_vr(iquad,idist) = -999.0 + sr_vt(iquad,idist) = -999.0 + else + igwsret = 95 + return + endif + + enddo + + enddo +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,calcparm,wfract_cov,pdf_ct_bin,pdf_ct_tot,maxstorm + & ,trkrinfo,igfwret) +c +c ABSTRACT: This subroutine determines the fractional areal coverage +c of winds exceeding various thresholds within specified arcs +c (e.g., 200 km, 400 km, etc) in each quadrant of a storm. The bins +c that are used go as follows: (1) 0-100; (2) 0-200; (3) 0-300; +c (4) 0-400; (5) 0-500. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real wfract_cov(numquad+1,numbin,numthresh) + real area_total_quad_bin(numquad,numbin) + real area_exceed_quad_bin(numquad,numbin,numthresh) + real xintlon,xintlat + real :: windthresh(numthresh) = (/17.5,25.74,32.94/) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,conv_ms_knots,vmagkts + real rads,ri,dell,vmag,xarea,grdintincr,xsfclon,xsfclat + real sum_exceed_area(numbin,numthresh) + real sum_total_area(numbin,numthresh) + integer pdf_ct_bin(16) + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igfwret,ipct,i,j,numinterp,ixoa,ixaa,iq,ib,it,ii + integer jlatfix,ilonfix,npts,ibeg,iend,jbeg,jend,ngridint,ni,nj + integer itret,igiret,idistbin,ipdfbin,pdf_ct_tot,maxstorm + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) + character got_pdf*6 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*5 :: cbin(5) = + & (/'0-100','0-200','0-300','0-400','0-500'/) + character*2 :: cthresh(3) = (/'34','50','64'/) +c + igfwret = 0 + conv_ms_knots = 1.9427 + rads = 500.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + + wfract_cov = 0.0 + area_total_quad_bin = 0.0 + area_exceed_quad_bin = 0.0 + sum_exceed_area = 0.0 + sum_total_area = 0.0 + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_fract_wind_cov from call to ' + print *,'!!! get_ij_bounds, stopping processing for storm' + print *,'!!! number ',ist + endif + + igfwret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_fract_wind_cov calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igfwret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + +c When evaluating the winds at a gridpoint, keep in mind that each +c gridpoint represents area around it. There are 2 special cases +c we need to watch out for. The first is for cases in which the +c area of a gridpoint straddles across a distance threshold, so +c that some of the gridpoint's area is in the "<200" bin, while +c some is in the "<100" bin. The other is for the case in which +c the area of a gridpoint straddles between 2 adjacent quadrants +c (e.g., a gridpoint exactly to the north of the center would have +c half its area in the NW quadrant and half in the NE quadrant). +c +c To properly "partition" and assign gridpoint areas, we need to +c interpolate the current grid down to a fine resolution. +c +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the guidelines that +c will be used, keeping in mind that we want the final grid spacing +c to be on the order of between 0.05 and 0.10 degree (finer than +c 0.05 deg is superfluous, and coarser than 0.10 deg is too coarse). +c +c Original grid size (deg) # of interps +c ------------------------- ------------ +c 0.8 <= g 4 +c 0.4 <= g < 0.8 3 +c 0.2 <= g < 0.4 2 +c 0.1 <= g < 0.2 1 +c g < 0.1 0 + + + if ((dx+dy)/2. >= 0.8) then + numinterp = 4 + else if ((dx+dy)/2. < 0.8 .and. (dx+dy)/2. >= 0.4) then + numinterp = 3 + else if ((dx+dy)/2. < 0.4 .and. (dx+dy)/2. >= 0.2) then + numinterp = 2 + else if ((dx+dy)/2. < 0.2 .and. (dx+dy)/2. >= 0.1) then + numinterp = 1 + else + numinterp = 0 + endif + + grdintincr = (dx+dy)/2. + do i = 1,numinterp + grdintincr = 0.5 * grdintincr + enddo + +c Now loop through the points in this subdomain, determine if any +c are within 500 km of the center, and then determine what quadrant +c the point is in relative to the center, and then calculate the +c fractional area coverage for winds. + + pdf_ct_tot = 0 + pdf_ct_bin = 0 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_fract_wind_cov, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_fract_wind_cov' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle iloop ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > (rads+(0.75*((dx+dy)/2.)*dtk*cos(glat(j)*dtr)))) + & then + + ! If the distance is greater than "rads" (500 km at initial + ! writing) plus another 3/4 of a gridpoint, then cycle. + ! The extra 3/4 of a gridpoint is to allow for the case of + ! some portion of the area around a gridpoint (whose + ! center point > 500 km) being within the 500 km arc... + ! although that is only factored in for grids with spacing + ! >= 0.1 deg. For smaller grids, where no interpolation is + ! done in this subroutine, then the distance to that point + ! is considered representative and the point is ignored if + ! it is not less than 500 km from the center. + + cycle iloop + + else + + ! First interpolate the area surrounding each grid point to + ! get fine resolution of lats & lons for determining how to + ! partition the area of a gridpoint among quadrants as well + ! as among distance thresholds. + + vmag = sqrt (u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + vmagkts = vmag * conv_ms_knots + + if (numinterp > 0) then + + grdintincr = ((dx+dy)/2.) / 2**numinterp ! "grid spacing" + ! of interpolated grid + ngridint = (2**numinterp) / 2 + + got_pdf = 'notyet' + + njloop: do nj= ngridint,-ngridint,-1 + + xintlat = glat(j) + float(nj) * grdintincr + + niloop: do ni= -ngridint,ngridint + + xintlon = glon(ii) + float(ni) * grdintincr + + call calcdist (xintlon,xintlat,xsfclon + & ,xsfclat,xdist,degrees) + + if (xdist <= 350. .and. got_pdf == 'notyet') then + ! The got_pdf flag is needed because in these loops + ! for niloop & njloop, we are actually looking at + ! tiny areas around the same grid point. So we + ! want to make sure we only count each gridpoint + ! once. + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + got_pdf = 'got_it' + endif + + if (xdist < 500.) then + + ! Compute area of this fraction of a grid box + xarea = (grdintincr * 111195) * + & (grdintincr * 111195 + & * cos(xintlat * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Go through a loop of the bins. The purpose of + ! this is that these "bins" all go from the + ! the center out to a specified radius, they are + ! NOT 100-km wide bins. So if we are dealing with + ! a point at r = 250 km, then that falls in the + ! 0-300 km bin, but it also falls in the 0-400 and + ! 0-500 km bins as well. So we need to run through + ! this binloop multiple times to get the area data + ! into multiple bins. Here are the bins & indices: + ! 1: 0-100 km + ! 2: 0-200 km + ! 3: 0-300 km + ! 4: 0-400 km + ! 5: 0-500 km + + binloop: do ib = idistbin,numbin + + if (xintlon >= xsfclon .and. + & xintlat >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (xintlon >= xsfclon .and. + & xintlat < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop + + endif + + enddo niloop + + enddo njloop + + else + + ! In this else statement is the case for a grid whose + ! resolution is already fine enough that we don't need + ! to interpolate any further. For example, we will have + ! the H*Wind data on a 0.05 degree grid, so that's already + ! fine enough. + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat + & ,xdist,degrees) + + if (xdist <= 350.) then + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + endif + + if (xdist < 500.) then + + ! Compute area of this grid box + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Why the binloop2? See explanation above in the "if" + ! part of this if-then block, where binloop is. + + binloop2: do ib = idistbin,numbin + + if (glon(ii) >= xsfclon .and. + & glat(j) >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (glon(ii) >= xsfclon .and. + & glat(j) < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop2 + + endif + + endif + + endif + + enddo iloop + + enddo jloop + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different quadrants, bins and thresholds... +c ------------------------------------------------- + + if ( verb .ge. 3 ) then + write (6,109) ' ' + & ,' ' + & ,' ' + write (6,109) ' Quadrant Bin Wind_Thresh ' + & ,'Fract_coverage (%) Area_exceeded' + & ,' Area_total' + write (6,109) ' -------- --- ----------- ' + & ,'------------------ -------------' + & ,' ----------' + write (6,109) ' ' + & ,' ' + & ,' ' + + do iq = 1,numquad + do ib = 1,numbin + do it = 1,numthresh + wfract_cov(iq,ib,it) = area_exceed_quad_bin(iq,ib,it) / + & area_total_quad_bin(iq,ib) + write (6,117) cquad(iq),cbin(ib),cthresh(it) + & ,wfract_cov(iq,ib,it)*100.0 + & ,area_exceed_quad_bin(iq,ib,it) + & ,area_total_quad_bin(iq,ib) + enddo + enddo + enddo + endif + + + 109 format (1x,a33,a37,a16) + 117 format (5x,a2,5x,a5,7x,a2,13x,f6.2,10x,f16.1,2x,f16.1) + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different bins and thresholds, but for the +c entire "disc" of the storm, that is, summing all +c quadrants together. +c ------------------------------------------------- + + do it = 1,numthresh + do ib = 1,numbin + do iq = 1,numquad + sum_total_area(ib,it) = sum_total_area(ib,it) + & + area_total_quad_bin(iq,ib) + sum_exceed_area(ib,it) = sum_exceed_area(ib,it) + & + area_exceed_quad_bin(iq,ib,it) + enddo + wfract_cov(5,ib,it) = sum_exceed_area(ib,it) + & / sum_total_area(ib,it) + enddo + enddo + + if ( verb .ge. 3 ) then + do ib = 1,numbin + do it = 1,numthresh + write (6,117) 'TT',cbin(ib),cthresh(it) + & ,wfract_cov(5,ib,it)*100.0 + & ,sum_exceed_area(ib,it) + & ,sum_total_area(ib,it) + enddo + enddo + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_ike_stats (imax,jmax,inp,dx,dy,ist,ifh + & ,fixlon,fixlat,xsfclon,xsfclat,valid_pt,calcparm + & ,ike,sdp,wdp,maxstorm,trkrinfo,igisret) +c +c ABSTRACT: This subroutine computes the Integrated Kinetic Energy +c (IKE) and Storm Surge Damage Potential (SDP) values, based on +c Powell (BAMS, 2007). At this time, we are only computing the IKE +c values for TS threshold (17.5 m/s) and above. We are not yet +c computing wind damage potential (WDP) since, per Mark Powell +c (4/2008), he is currently re-formulating an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c sdp Storm surge damage potential + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4 + integer npts,ipct,igisret,imax,jmax,ist,ifh,ilonfix,jlatfix + integer ibeg,jbeg,iend,jend,igiret,i,j,maxstorm,ii + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real ike(max_ike_cats) + real dx,dy,degrees,rads,ri,dell,xdist,vmag,xarea + real xsfclon,xsfclat,sdp,wdp + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) +c + igisret = 0 + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + rads = 400.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_ike_stats from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for storm ' + print *,'!!! number ',ist + endif + + igisret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_ike_stats calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igisret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + +c Search a grid of points near the storm center, evaluate if the +c storm is within the "rads" distance threshold. If so, compute +c the IKE values for all applicable thresholds (10, 18, 33 m/s). + + do j = jbeg,jend + do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ike_stats, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_ike_stats' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > rads) then + cycle + else + + vmag = sqrt(u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + + if (vmag > 10.0) then + ! Add gridpoint to IKE_10. Compute area first... + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + ike(1) = ike(1) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 18.0) then + ! Add gridpoint to IKE_ts. Area already computed for 10 + ike(2) = ike(2) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 33.0) then + ! Add gridpoint to IKE_h. Area already computed for 10 + ike(3) = ike(3) + (0.5 * (vmag**2) * xarea) + endif + + endif + + enddo + enddo + + ike(1) = ike(1) * 1.e-12 ! Convert from J to TJ + ike(2) = ike(2) * 1.e-12 ! Convert from J to TJ + ike(3) = ike(3) * 1.e-12 ! Convert from J to TJ + +c Compute the storm surge damage potential (sdp) + + if (ike(2) >= 0.0) then + sdp = 0.676 + (0.43 * sqrt(ike(2))) + & - (0.0176 * ((sqrt(ike(2)) - 6.5)**2) ) + else + sdp = -99.0 + endif + +c Print out the IKE and SDP statistics... + + if ( verb .ge. 3 ) then + print *,' IKE_10 (storm energy) = ',ike(1) + print *,' IKE_TS (tropical storm) = ',ike(2) + print *,' IKE_H (hurricane) = ',ike(3) + print *,' SDP = ',sdp + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine distbear (xlatin,xlonin,dist,bear,xlatt,xlont) +c +c ABSTRACT: Given an origin at latitude, longitude=xlato,xlono, +c this subroutine will locate a target point at a distance dist in +c km or nautical miles (depends on what you use for "rad_earth..." +c below), at bearing bear (degrees clockwise from north). +c Returns latitude xlatt and longitude xlont of target point. +c +c *** NOTE *** +c This subroutine was written to handle input lats & lons as this: +c All latitudes are in degrees, north positive and south negative. +c All longitudes are in degrees, west positive and east negative. +c *** **** *** +c +c However, for the longitudes, the rest of the tracker uses all +c 0-360 longitudes. Therefore, we need to convert the input lons +c and then once again convert the lons that are returned back to +c the calling routine. +c +c NOTE-- When origin is at north or south pole, bearing is no +c longer measured from north. Instead, bearing is measured +c clockwise from the longitude opposite that specified in xlono. +c Example-- if xlato=90., xlono=80., the opposite longitude is +c -100 (100 East), and a target at bearing 30. will lie on the +c -70. (70 East) meridian. +c +c AUTHOR: The core of this subroutine was written by Albion +c Taylor, another NOAA employee, in 1981. +c + USE trig_vals + + implicit none +c + real, parameter :: rad_earth_nm = 3440.170 ! radius of earth + real, parameter :: rad_earth_km = 6372.797 ! radius of earth + real xlato,xlono,dist,bear,xlatt,xlont,xlatin,xlonin + real cdist,sdist,clato,slato,clono,slono,cbear,sbear + real z,y,x,r,xlattz,xlontz,ddist,dbear,dxlato,dxlono +c + xlato = xlatin + xlono = xlonin + +cstr print *,' ' +cstr print *,'+++ At top of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlon= ',xlono,'E ',360.-xlono +cstr & ,'W xlat=',xlato +cstr print '(a6,f7.2,a8,f7.2)','dist= ',dist,' bear= ',bear + + if (xlono > 180.) then + ! Longitude input for this subroutine must be positive west + xlono = 360. - xlono + else + ! Longitude input for this subroutine must be negative east + xlono = -1. * xlono + endif + +cstr print '(a31,a8,f8.2)','After conversion for distbear, ' +cstr & ,' xlono= ',xlono + + ddist = dist + dbear = bear + dxlato = xlato + dxlono = xlono + + cdist = cos(ddist/rad_earth_km) + sdist = sin(ddist/rad_earth_km) + clato = cos(dtr*dxlato) + slato = sin(dtr*dxlato) + +cstr print *,'cdist= ',cdist,' sdist= ',sdist,' clato= ',clato +cstr & ,' slato= ',slato + + clono = cos(dtr*dxlono) + slono = sin(dtr*dxlono) + +cstr print *,'dxlono= ',dxlono,' clono= ',clono +cstr & ,' slono= ',slono + + cbear = cos(dtr*dbear) + sbear = sin(dtr*dbear) + +cstr print *,'cbear= ',cbear,' sbear= ',sbear + + z=cdist*slato + clato*sdist*cbear + y=clato*clono*cdist + sdist*(slono*sbear - slato*clono*cbear) + x=clato*slono*cdist - sdist*(clono*sbear + slato*slono*cbear) + +cstr print *,'z= ',z,' y= ',y,' x= ',x + + r = sqrt(x**2 + y**2) + +cstr print *,'r = sqrt(x**2 + y**2) = ',r + + xlattz = atan2(z,r)/dtr + +cstr print *,'xlattz = datan2(z,r)/dtr = ',xlattz + + xlatt = xlattz + + if (r <= 0.) go to 20 + + xlontz = atan2(x,y)/dtr + +cstr print *,'xlontz = atan2(x,y)/dtr = ',xlontz + +c xlont = xlontz + + ! Return the target longitude back to the calling routine + ! as a 0-360 positive east longitude.... + + xlont = mod(360.-xlontz,360.) + +c xlont = mod(360.+xlontz,360.) + +cstr print *,' ' +cstr print *,'At end of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlont= ',xlont,'E ' +cstr ,360.-xlont,'W xlatt=',xlatt + + return + 20 xlont=0. +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_uneven (targlat,targlon,targdist,dx,dy + & ,imax,jmax,trkrinfo,cparm,xintrp_val,ibiret) +c +c ABSTRACT: This subroutine performs a bilinear interpolation to get +c a data value at a given lat/lon that may be anywhere within a box +c defined by the four surrouding grid points. In the diagram below, +c remember that for our grids we are using in the tracker, the +c latitude index starts at the north pole and increases southward. +c The point "X" indicates the target lat/lon location of the value +c for which we are bilinearly interpolating. The values to and ta +c below are ratios that determine how geographically close the +c target location is to the point of origin (pt.1 (i,j)) in terms +c of both longitude (to) and latitude (ta). +c +c +c pt.1 pt.2 +c (i,j) (i+1,j) +c +c +c +c X +c +c pt.4 pt.3 +c (i,j+1) (i+1,j+1) +c + + USE grid_bounds; USE tracked_parms; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + character cparm*1 + real targlat,targlon,targdist,xintrp_val,dx,dy + real to,ta,d1,d2,d3,d4,z,eastlon + integer ie,iw,jn,js,ibiret,imax,jmax + + ibiret = 0 + +c -------------------------------------------------------------- +c For the latitudes and longitudes surrounding our target +c lat/lon location, convert the lat/lon values into i- and +c j-indices. +c -------------------------------------------------------------- + +c Find the j-indices for the points just to the north and the +c south of targlat.... + + if (targlat >= 0.0) then + ! For a northern hemisphere storm, jn is the j-index for the + ! point just to the *NORTH* (poleward) of targlat. + jn = int((glatmax - targlat)/dy + 1.) + js = jn + 1 + else + ! For a southern hemisphere storm, js is the j-index for the + ! point just to the *SOUTH* (poleward) of targlat. + js = ceiling((glatmax - targlat)/dy + 1.) + jn = js - 1 + endif + + ! Check to make sure that points are not being requested beyond + ! the northern or southern boundaries of the grid. This is most + ! likely to happen for a smaller, regional grid. + + if (jn > jmax .or. js > jmax) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jmax exceeded in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + + if (jn < 1 .or. js < 1) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jn < 0 or js < 0 in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + +c Find the i-indices for the points just to the east and the +c west of targlon.... + + ie = int((targlon - glonmin)/dx + 2.) + iw = ie - 1 + + ! Check for GM wrapping. Check ie to see if it is between the + ! most eastward gridpoint and the GM (i.e., on a 1-deg global + ! grid (360x181), it would be if targlon was between 359.0 (i=360) + ! and the GM (i=1, not i=361)). Similarly then, if we adjust ie + ! to then be 1, then we have a problem with iw, + ! since iw = 1 - 1 = 0. + + if (ie > imax) then + if (trkrinfo%gridtype == 'global') then + ie = ie - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ie > imax in subroutine ' + print *,'!!! bilin_int_uneven for a non-global grid. ' + print *,'!!! Returning to calling routine after ' + print *,'!!! assigning missing wind value of -99.' + print *,'!!! ie= ',ie,' imax= ',imax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + + if (iw < 1) then + if (trkrinfo%gridtype == 'global') then + iw = iw + imax + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: iw < 1 in subroutine bilin_int_uneven' + print *,'!!! for a non-global grid. Returning to calling ' + print *,'!!! routine after assigning missing wind value ' + print *,'!!! of -99. iw= ',iw + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + + if ( verb .ge. 3 ) then + print *,' +++ Interpolating winds for cparm= ',cparm + print '(6x,4(a4,i3))','jn= ',jn,' js= ',js,' iw= ',iw,' ie= ',ie + endif + +c ---------------------------------------------------------------- +c Calculate the longitude (to) and latitude (ta) location ratios. +c Check for GM wrapping, as we can run into a problem here if +c interpolating for points that are just west of the GM, since we +c would be interpolating using values of longitude just west of +c GM (say, glon(iw)=359.5) and the GM (glon(ie) = 0.0). This +c makes for an incorrect "to" ratio below, with 0-359.5 in the +c denominator. We have to account for this.... +c ---------------------------------------------------------------- + + if (glon(iw) > 300.0 .and. + & (glon(ie) < 10. .and. glon(ie) >= 0.)) then + eastlon = 360. - glon(ie) + else + eastlon = glon(ie) + endif + + if ( verb .ge. 3 ) then + print *,'glat(js)= ',glat(js),' glat(jn)= ',glat(jn) + endif + + to = (targlon - glon(iw)) / (eastlon - glon(iw)) + ta = (targlat - glat(jn)) / (glat(js) - glat(jn)) + +c -------------------------------------------------------------- +c Copy the data values at the 4 known points into simple scalar +c variables +c -------------------------------------------------------------- + + if (cparm == 'u') then + d1 = u(iw,jn,levsfc) + d2 = u(ie,jn,levsfc) + d3 = u(ie,js,levsfc) + d4 = u(iw,js,levsfc) + else if (cparm == 'v') then + d1 = v(iw,jn,levsfc) + d2 = v(ie,jn,levsfc) + d3 = v(ie,js,levsfc) + d4 = v(iw,js,levsfc) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in bilin_int_uneven.' + print *,'!!! Input cparm not recognized.' + print *,'!!! cparm= ',cparm + print *,'!!! EXITING....' + endif + + stop 95 + endif + + z = 1.9427 + +cstr print '(2x,4(a4,f8.2))',' d1= ',d1*z,' d2= ',d2*z +cstr & ,' d3= ',d3*z,' d4= ',d4*z + +c ------------------------------------------------------------- +c Compute the interpolated value +c ------------------------------------------------------------- + + xintrp_val = (1.-to) * (1.-ta) * d1 + & + to * (1.-ta) * d2 + & + to * ta * d3 + & + (1.-to) * ta * d4 + +cstr print '(2x,2(a11,f8.2))',' xintrp= ',xintrp_val,' (in kts)= ' +cstr & ,xintrp_val*z +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine sort_storms_by_pressure (gridprs,ifh,maxstorm,sortindex + & ,issret) +c +c ABSTRACT: This subroutine sorts storms by mslp. It is called by +c subroutine tracker just before the loop for "stormloop" is done +c for all the storms at a particular forecast hour. It is only +c called for the "midlat" and "tcgen" cases. The end result of +c this sort is an array (prsindex) that contains the indeces of +c the storms, arranged from lowest pressure to highest (and note +c that the "undefined" storms have a pressure of 9999.99 mb and +c thus get sorted to the bottom of the array). The purpose of +c doing this is so that we track the most intense storms first. +c Why go to the trouble? Imagine a scenario in which we are +c tracking a complex system in which there are 2 low pressure +c centers. Let's say that one is becoming dominant and +c intensifying, while the other is weakening. Now, let's assume +c that the weakening one eventually gets absorbed into the +c stronger, more dominant low. Now we only have 1 low, but if in +c the tracker stormloop, we first process the data for the +c weakening low, we will attribute the track to that storm, and +c then when we get to the point in the loop where we are trying +c to get the track for the stronger storm, we will (erroneously) +c stop the tracking for that storm since the storm center has +c already been attributed to the weaker storm. But by using this +c subroutine, we will track the stronger storm first, and thus +c avoid this problem. +c +c NOTE: The pressures used in the sort are those obtained at the +c previous forecast hour. At forecast hour = 0, just use the +c values as they were input to this routine, since they were +c found in first_ges_center from strongest to weakest already. +c +c INPUT: +c gridprs real array of storm mslp values +c ifh integer index for the current forecast hour +c maxstorm max num of storms that can be handled in this run +c +c OUTPUT: +c sortindex contains a sorted array of indeces. The orders +c sort routine does NOT rearrange the data. Rather, it +c returns this array of sorted indeces which point to +c the correct order of data values in the data array. +c issret return code from this subroutine +c + USE set_max_parms + USE verbose_output + + real, allocatable :: iwork(:) + real gridprs(maxstorm,maxtime) + integer ifh,maxstorm + integer sortindex(maxstorm) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: prstemp(:) +c + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iva /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub sort_storms_by_pressure allocating' + print *,'!!! prstemp or iwork arrays: ' + print *,'!!! iva= ',iva,' iwa= ',iwa + endif + + STOP 94 + return + endif + + if (ifh > 1) then + +c print *,' ' +c print *,'--- Before sort, original prs values follow:' +c print *,' ' + + do ist = 1,maxstorm + prstemp(ist) = gridprs(ist,ifh-1) +c write (6,81) ist,prstemp(ist)/100.0 + enddo + + imode = 2 + sortindex = 0 + call qsort (prstemp,sortindex,maxstorm) + +ccccc call orders (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) +ccccc call orders_4byte (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Pressure-sorted storm list:' + print *,' ' + + do ist = 1,maxstorm + if (prstemp(sortindex(ist))/100.0 < 9999.0) then + write (6,82) ist,sortindex(ist) + & ,prstemp(sortindex(ist))/100.0 + endif + enddo + + 81 format (1x,'ist= ',i5,' Original (unsorted) prstemp= ',f7.2) + 82 format (1x,'ist= ',i5,' sortindex(ist)= ',i5 + & ,' prstemp= ',f7.2) + endif + + else + do ist = 1,maxstorm + sortindex(ist) = ist + enddo + endif + + deallocate (prstemp); deallocate (iwork) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getvrvt (centlon,centlat,xlon,xlat + & ,udat,vdat,vr,vt,igvtret) +c +c ABSTRACT: This subroutine takes as input a u-wind and v-wind value +c at an input (xlon,xlat) location and returns the tangential and +c radial wind components relative to the input center lat/lon +c position (centlon,centlat). The only trick to this whole +c subroutine is figuring out the angle from the center point to the +c data point, and we do this by creating a triangle with the leg +c from the center point to the data point being the hypotenuse. +c +c NOTE: All longitudes must be in positive degrees east (0-360) !!! +c +c INPUT: +c centlon Longitude of center point +c centlat Latitude of center point +c xlon Longitude of pt at which vr & vt will be computed +c xlat Latitude of pt at which vr & vt will be computed +c udat u-value of wind at the point (xlon,xlat) +c vdat v-value of wind at the point (xlon,xlat) +c +c OUTPUT: +c vr Radial wind component at (xlon,xlat) wrt (centlon,centlat) +c vt Tang wind component at (xlon,xlat) wrt (centlon,centlat) +c igvtret Return code from this subroutine + + USE trig_vals + USE verbose_output + + real centlon,centlat,xlon,xlat,udat,vdat,vr,vt,degrees +c + call calcdist(centlon,centlat,xlon,xlat,hyp_dist,degrees) + + xlatdiff = abs(centlat - xlat) + xlondiff = abs(centlon - xlon) + + if (xlondiff == 0 .and. xlatdiff > 0) then + + if (centlat > xlat) angle = 180 ! pt directly south of ctr + if (centlat < xlat) angle = 0 ! pt directly north of ctr + + else if (xlondiff > 0 .and. xlatdiff == 0) then + + if (centlon > xlon) angle = 270 ! pt directly west of ctr + if (centlon < xlon) angle = 90 ! pt directly east of ctr + + else + + ! This next part figures out the angle from the center point + ! (centlon,centlat) to the data point (xlon,xlat). It does + ! this by setting up a triangle and then using inverse trig + ! functions to get the angle. Since this is a kludgy way to + ! do it that doesn't account for the curvature of the earth, + ! we'll do it 2 ways, using asin and then acos, then take the + ! average of those 2 for the angle. hyp_dist, calculated just + ! above, is the distance from the center pt to the data pt. + + opp_dist = xlatdiff/360. * ecircum + sin_value = opp_dist / hyp_dist + if (sin_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, sin_value > 1, setting to 1.' + print *,'!!! opp_dist= ',opp_dist,' hyp_dist= ',hyp_dist + print *,'!!! sin_value = ',sin_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! xlon= ',xlon,' xlat= ',xlat + print *,' ' + endif + + sin_value = 0.99999 + endif + sin_angle = asin(sin_value) / dtr + + call calcdist(centlon,centlat,xlon,centlat,adj_dist,degrees) + cos_value = adj_dist / hyp_dist + if (cos_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, cos_value > 1, setting to 1.' + print *,'!!! adj_dist= ',adj_dist,' hyp_dist= ',hyp_dist + print *,'!!! cos_value = ',cos_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! xlon= ',xlon,' xlat= ',xlat + print *,' ' + endif + + cos_value = 0.99999 + endif + cos_angle = acos(cos_value) / dtr + + tmpangle = 0.5 * (sin_angle + cos_angle) + + ! The previous lines of code just calculated an angle between + ! 0 and 90. This next if structure adjusts that angle to + ! instead be between 0 and 360. + + if (centlat <= xlat .and. centlon <= xlon) then + angle = 90 - tmpangle + else if (centlat > xlat .and. centlon <= xlon) then + angle = 90 + tmpangle + else if (centlat >= xlat .and. centlon >= xlon) then + angle = 270 - tmpangle + else if (centlat < xlat .and. centlon >= xlon) then + angle = 270 + tmpangle + endif + + endif + + uvrcomp = udat * sin(angle * dtr) + vvrcomp = vdat * cos(angle * dtr) + vr = uvrcomp + vvrcomp + + uvtcomp = (-udat) * cos(angle * dtr) + vvtcomp = vdat * sin(angle * dtr) + vt = uvtcomp + vvtcomp + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcfunix (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in the new ATCF UNIX format. +c Unlike the old atcf DOS format in which you waited until the +c whole tracking was over to write the output for all forecast +c hours, with this atcfunix format, each time we are calling this +c subroutine, it is to only write out 1 record, which will be the +c fix info for a particular storm at a given time. Also, even +c though we have some data (GFS, NAM) at 6-hour intervals, Jim +c Gross informed me that TPC does not need the positions at such +c frequency, and keeping the reporting at 12 hour intervals is fine. +c +c While this new atcfunix format contains much more information than +c the old 1-line atcf dos message, for our purposes we will use the +c slots for mslp and wind radii. An example set of output records +c will look like the following: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c plastbar pressure of the outermost closed isobar +c rlastbar radius (nm) of the outermost closed isobar +c rmax radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c cps_vals real array with the values for the 3 cyclone phase +c space parameters: (1) is for Parameter B (thermal +c asymmetry); (2) is for lower level (600-900 mb) thermal +c wind; (3) is for upper level (300-600 mb) thermal wind. +c wcore_flag character for value of 300-500 mb warm core: y, n, or +c 'u' for undetermined. +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE phase + USE verbose_output + + type (datecard) inp + type (trackstuff) trkrinfo + + real cps_vals(3) + real outlon,outlat,rmax + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,irmax,output_fhr,ic,iplastbar,irlastbar + integer vradius(3,4),icps_vals(3) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + character comma_fill1*48,comma_fill2*31,comma_filler*79 + + if ( verb .ge. 3 ) then + print *,'TTT top of atcfunix, ist= ',ist,' ifh= ',ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'in output_atcfunix, tcv_storm_id= ' + & ,storm(ist)%tcv_storm_id + print *,'in output_atcfunix, tcv_storm_id(3:3)= ' + & ,storm(ist)%tcv_storm_id(3:3) + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' +!zhang case ('A','a'); basinid = 'NA' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + if (trkrinfo%want_oci) then + if (plastbar > 0.0) then + iplastbar = int(plastbar/100 + 0.5) + else + iplastbar = -99 + endif + if (rlastbar > 0.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -99 + endif + else + iplastbar = -99 + irlastbar = -99 + endif + + if ( verb .ge. 3 ) then + print *, 'output: rlastbar=',rlastbar,' irlastbar=',irlastbar + print *, 'output: plastbar=',plastbar,' iplastbar=',iplastbar + endif + +c Now convert all of the cyclone phase space parameter values from +c real to integer. + + do ic = 1,3 + if (cps_vals(ic) > -9999.0) then + if (cps_vals(ic) >= 0.0) then + icps_vals(ic) = int(cps_vals(ic)*10. + 0.5) + else + icps_vals(ic) = int(cps_vals(ic)*10. - 0.5) + endif + else + icps_vals(ic) = -9999 + endif + enddo + + if (wcore_flag == 'y') wcore_flag = 'Y' + if (wcore_flag == 'n') wcore_flag = 'N' + if (wcore_flag == 'u') wcore_flag = 'U' + + comma_fill1 = ', 0, 0, , 0, , 0, 0, ,' + comma_fill2 = ' , , , 0, 0, 0, 0' + comma_filler = comma_fill1//comma_fill2 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + else + + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,'rmax= ',rmax,' irmax= ',irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,a79,', THERMO PARAMS' + & ,3(', ',i7),', ',a1,', ',i2,', DT, -999') + 91 format (a2,', ',a4,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,2(', ',i3),', ',a3) + +c bug fix for IBM: flush the output stream so it actually writes + flush(64) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each +c storm. This message contains the model identifier, the forecast +c initial date, and the positions for 0, 12, 24, 36, 48, 60 and 72 +c hours. In the case of the regional models (NGM, Eta), which +c only go out to 48h, zeroes are included for forecast hours +c 60 and 72. +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by Steve Lord for plotting purposes, and his +c plotting routines need the longitudes in 0 - 360, increasing +c westward. Thus, a necessary adjustment is made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + print *,'top of output_all' + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + intlon(ifh) = 0 + intlat(ifh) = 0 + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + endif + + enddo ifhloop + + print *,'before select case, atcfname= ' + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(5),intlon(5),intlat(9),intlon(9),intlat(13) + & ,intlon(13),intlat(17),intlon(17),intlat(21),intlon(21) + & ,0,0,storm(ist)%tcv_storm_id + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(3),intlon(3),intlat(5),intlon(5),intlat(7) + & ,intlon(7),intlat(9),intlon(9),intlat(11),intlon(11) + & ,intlat(13),intlon(13),storm(ist)%tcv_storm_id + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NOGAPS, ECMWF Ensemble + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3),intlat(4) + & ,intlon(4),intlat(5),intlon(5),intlat(6),intlon(6) + & ,intlat(7),intlon(7),storm(ist)%tcv_storm_id + + case ('GDA','HDA') ! GDAS, HDAS + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),0,0,0,0,0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case default + print *,' ' +! print *,'!!! Warning from subroutine output_all. ' + print *,'!!! Model name is not identified. ',atcfname +! print *,'!!! Model name = ',atcfname +! print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,'!!! Model is assumed to be parallel GDAS/GFS. ' + + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + 81 format (i2,a4,4i2.2,14i4,1x,a3) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm + & ,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each storm +c in ATCF format. This message contains the model identifier, the +c forecast initial date, and the positions for 12, 24, 36, 48 +c and 72 hours. This message also contains the intensity +c estimates (in knots) for those same hours. The conversion for +c m/s to knots is to multiply m/s by 1.9427 (3.281 ft/m, +c 1 naut mile/6080 ft, 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by the atcf system at TPC for plotting +c purposes, and the atcf plotting routines need the longitudes in +c 0 - 360, increasing westward. Thus, a necessary adjustment is +c made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real xmaxwind(maxstorm,maxtime) + real conv_ms_knots + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4,basinid*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + conv_ms_knots = 1.9427 + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + + intlon(ifh) = 0 + intlat(ifh) = 0 + + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + + endif + + enddo ifhloop + + basinid = ' ' + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid(1:2) = 'AL' + case ('E','e'); basinid(1:2) = 'EP' + case ('C','c'); basinid(1:2) = 'CP' + case ('W','w'); basinid(1:2) = 'WP' + case ('O','o'); basinid(1:2) = 'SC' + case ('T','t'); basinid(1:2) = 'EC' + case ('U','u'); basinid(1:2) = 'AU' + case ('P','p'); basinid(1:2) = 'SP' + case ('S','s'); basinid(1:2) = 'SI' + case ('B','b'); basinid(1:2) = 'BB' + case ('A','a'); basinid(1:2) = 'NA' + case default; basinid(1:2) = '**' + end select + basinid(3:4) = storm(ist)%tcv_storm_id(1:2) + + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(5),intlon(5) + & ,intlat(9),intlon(9),intlat(13),intlon(13),intlat(17) + & ,intlon(17),0,0 + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,17)*conv_ms_knots) + 0.5) + & ,0 + & ,basinid,inp%byy + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NOGAPS, ECMWF Ensemble, ECMWF hi-res + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4),intlat(5) + & ,intlon(5),intlat(7),intlon(7) + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('GDA','HDA') ! GDAS, HDAS + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4) + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,0,0,0,0 + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case default + print *,' ' + print *,'!!! ERROR in subroutine output_atcf. ' + print *,'!!! Model name is not identified.' + print *,'!!! Model name = ',atcfname + print *,'!!! ist = ',ist,' Model number = ',atcfnum + + end select + + enddo stormloop + + 82 format (i2,a4,4i2.2,10i4,5i3,1x,a4,i2.2) +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_hfip (outlon,outlat,inp,ist + & ,ifh,vmaxwind,xminmslp,vradius,rmax,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified ATCF UNIX format. +c The modification is to allow for sub-hourly output. That is, +c instead of just integer output hours, we can have output at +c 10, 15 or 20 past an hour. This necessitates a change in the +c "forecast hour" placeholder in the ATCF format. Instead of it +c being an I3, we'll make it an I5, with something like a lead time +c of 36.25h being rounded and truncated to 03625 for output. +c +c An example set of output records using the standard atcf format +c looks like the following: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c An example set of modified output records will look like the +c following, for the case of a lead time of 36:15 (36.25): +c +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifh index for the lead time array +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c rmax Radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms + USE verbose_output + + type (datecard) inp + + real outlon,outlat + real vmaxwind,conv_ms_knots,xminmslp,rmax + integer intlon,intlat,output_fhr,irmax,ileadtime + integer vradius(3,4) + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + ! ST: ifcsthour does not exist, so output_fhr is always + ! filled with invalid data here. However, output_fhr is + ! never used, so it is safe to remove. + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + ! output_fhr = ifcsthour + 3 + ileadtime = nint((fhreal(ifh) + 3.0) * 100.0) + else + ! output_fhr = ifcsthour + ileadtime = nint(fhreal(ifh) * 100.0) + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4),irmax + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4),irmax + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4),irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i5.5,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', 0, 0, ',i3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(69) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_fract_wind (outlon,outlat,xsfclon,xsfclat + & ,inp,ist,ifcsthour,vmaxwind,xminmslp,wfract_cov + & ,wfract_type,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values for the fractional areal coverage of various wind +c thresholds. In addition, this subroutine also writes out +c records to a file containing data on the PDF of wind magnitudes +c within r=350 km. +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with areal coverage thresholds. +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, NEE, 981, 857, 629, 810 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, NEE, 874, 732, 319, 610 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, NEE, 454, 327, 99, 270 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, AAE, 721, 721, 721, 721 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, AAE, 465, 465, 465, 465 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, AAE, 298, 298, 298, 298 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the pctgs for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind pctg info; all the +c other info is identical for each entry. +c +c Listed after the "XX" in each record is the radius from which +c the coverage is valid (000 km in this case); Next is the radius +c at which the coverage stops (100 km in this case). Next is the +c wind threshold (34, 50, 64). Next is an identifier for which +c quadrant the coverage starts in (first 2 characters are NE, SE, +c SW, NW); the last character indicates if the coverages are +c computed in the quadrants as earth-relative ("E") or +c storm-motion relative ("R"). The ones listed there as "AAE" +c are for the full disc (i.e., 4-quadrant average), earth-relative. +c Next are the wind coverage percentages, listed as percentage * 10 +c (e.g., 981 = 98.1%). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c wfract_cov percent areal coverage for various wind thresholds +c wfract_type 'earth' or 'storm' relative analysis +c pdf_ct_bin array for pdf of wind magnitudes within r=350 km +c pdf_ct_tot total count of pdf points for r < 350 km +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,pdfval + real wfract_cov(numquad+1,numbin,numthresh) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer :: windthresh(numthresh) = (/34,50,64/) + integer pdf_ct_bin(16) + integer intlon,intlat,output_fhr,intlon100,intlat100,pdf_ct_tot + integer maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (wfract_type == 'earth') then + wt = 'E' + else if (wfract_type == 'storm') then + wt = 'R' + else + wt = 'X' + endif + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'NE',wt + & ,int((1000.*wfract_cov(1,ib,it))+0.5) + & ,int((1000.*wfract_cov(2,ib,it))+0.5) + & ,int((1000.*wfract_cov(3,ib,it))+0.5) + & ,int((1000.*wfract_cov(4,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'AA',wt + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a6,i3.3,', ',i3.3,', ' + & ,i3,', ',a2,a1,4(', ',i4),', ',i4,a1,', ',i5,a1) + +c -------------------------------------------------- +c Now compute and write out the pdf values for the +c wind magnitude.... +c -------------------------------------------------- + + do ip = 1,16 + pdfval = float(pdf_ct_bin(ip)) / float(pdf_ct_tot) + write (76,85) atcfymdh,basinid,storm(ist)%tcv_storm_id(1:2) + & ,output_fhr,10*(ip-1),10*ip,pdf_ct_bin(ip) + & ,pdf_ct_tot,pdfval + enddo + + 85 format (1x,i10.10,3x,a2,a2,3x,i3,3x,i3.3,'_',i3.3,3x,i7,2x,i7 + & ,2x,f6.3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(73) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_wind_structure (outlon,outlat,xsfclon + & ,xsfclat,inp,ist,ifcsthour,vmaxwind,xminmslp,er_wind + & ,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values of the winds at specified distances along 45-degree +c radials in each storm quadrant. These are output +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 radials: NE, SE, +c SW and NW (45,135,225,315). For the storm-relative estimates, +c these radials will be computed at the same relative angles (i.e., +c 45,135,225,315), but with respect (positive clockwise) to the +c direction of storm motion. For example, for a storm moving with +c a heading of 280, the wind structure is evaluated at these +c radials: 325 (front-right; 45 deg CW from heading), 55 (back- +c right; 135 deg CW from heading), 145 (back-left; 225 deg CW from +c heading), 235 (front-left; 315 deg CW from heading). +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with wind values at the 13 specified distances +c (10, 25, 50, 75, 100, 150, 200, 250, 300, 350, 400, 450, 500 km) +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NEE, 1137, 1221, 854, 655, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SEE, 947, 982, 474, 396, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SWE, 645, 683, 328, 277, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NWE, 725, 753, 619, 429, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FRR, 1134, 1224, 852, 654, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BRR, 944, 984, 472, 393, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BLR, 649, 686, 321, 272, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FLR, 729, 756, 613, 421, etc., ... out to 500 km +c +c NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text. +c NOTE: These winds are in m/s coming into this routine and will +c be converted to knots*10 for output (e.g., 1221 = 122.1 kts) +c +c The "71" ID indicates earth-relative winds, the "72" ID indicates +c storm-relative winds. Here are the other IDs that will be used: +c 81: Tangential winds, earth-relative (m/s) +c 82: Tangential winds, storm-relative (m/s) +c 91: Radial winds, earth-relative (m/s) +c 92: Radial winds, storm-relative (m/s) +c +c Note that in this example, for this 36h forecast hour, there are +c 8 entries. This is so that we can include the wind values for +c the 4 different quadrants, for both the earth relative analyses +c (NEE, SEE, SWE, NWE) and the storm-relative analyses (FRR, BRR, +c BLR, FLR). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + integer ioutwind(numdist) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real outlon,outlat + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,id,intlon100,intlat100,ir + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*2 :: crel(4) = (/'FR','BR','BL','FL'/) + + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + +c Total wind (converted to knots*10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 71, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Total wind (converted to knots*10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 72, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 81, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 82, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 92, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a10,a2,a1,14(', ',i4) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(72) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_ike (outlon,outlat,xsfclon,xsfclat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,ike,sdp,wdp,maxstorm + & ,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the Integrated Kinetic Energy (IKE) and Storm Surge Damage +c Potential (SDP), based on Powell (BAMS, 2007). At this time, we +c are only computing the IKE values for TS threshold (17.5 m/s) and +c above. We are not yet computing wind damage potential (WDP) +c since, per Mark Powell (4/2008), he is currently re-formulating +c an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with WDP, SDP and IKE values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, 340, 560, 212, 174, 42, 93, 12, 0 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, WDP, SDP, I10, ITS, IH ,I2540,I4154, I55 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Values for WDP and SDP are multiplied by 10 in this routine +c before being written out. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c ike integrated kinetic energy, in units of TJ +c sdp storm surge damage potential +c wdp wind damage potential +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,sdp,wdp + real ike(max_ike_cats) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,intlon100,intlat100,maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (74,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, IKE',int((wdp*10)+0.5),int((sdp*10)+0.5) + & ,int(ike(1)+0.5),int(ike(2)+0.5),int(ike(3)+0.5) + & ,int(ike(4)+0.5),int(ike(5)+0.5),int(ike(6)+0.5) + & ,intlat100,clatns,intlon100,clonew +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a14,8(',',i5) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(74) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_phase (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,paramb,vtl_slope + & ,vtu_slope,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the three parameters that comprise Bob Hart's cyclone phase +c space (CPS). These parameters are his "parameter B", which +c assesses the left-right thermal asymmetry, and the upper +c troposphere (300-600 mb) and lower troposphere (900-600 mb) +c thermal wind values. +c +c LOCAL: +c +c Arrays: +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with paramb, vtl_slope and vtu_slope values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, 340, 560, 212 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, B, VTL, VTU +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c paramb thermal asymmetry +c vtl_slope thermal wind value for lower troposphere (900-600 mb) +c vtu_slope thermal wind value for upper troposphere (600-300 mb) +c +c OUTPUT: +c ioiret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + real outlon,outlat,paramb,vtl_slope,vtu_slope + real vmaxwind,conv_ms_knots,xminmslp + integer intlon,intlat,output_fhr + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (71,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 95, CPS',int(paramb+0.5),int(vtl_slope+0.5) + & ,int(vtu_slope+0.5) +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a14,3(',',i6)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(71) + + return + end +c +c----------------------------------------------------------------------- +cJ.Peng----2014-10-01------------------------------- +c----------------------------------------------------------------------- +c subroutine output_atcf_gen (outlon,outlat,inp,ist +c & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm +c & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax +c & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + subroutine output_atcf_gen (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different. Here's an example of the +c TPC standard atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c be the one that is pulled from the tcvitals or gen_vitals +c record: +c +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 50, NEQ, 155, 000, 000, 000 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 64, NEQ, 000, 000, 000, 000 +c +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd storm translation speed +c istmdir direction of storm movement +c plastbar pressure of last closed isobar +c rlastbar radius of last closed isobar +c rmax radius of max winds +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real outlon,outlat,plastbar,rlastbar,rmax + real vmaxwind,conv_ms_knots,xminmslp + real cps_vals(3) + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar,irmax + integer ivtl,ivtu,iparamb,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + +cJ.Peng----2014-10-01----------------------------- + real wcore_mean_val,wcore_point_max + real imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + + if ( verb .ge. 3) then + print *,'+++ Top of output_atcf_gen, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + + if (xminmslp == 999999.0) xminmslp = 0.0 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/100. + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (rmax > -90.0) then + irmax = int(rmax + 0.5) + else + irmax = -99 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- +c & ,imeanzeta(1),abs(igridzeta(1)),imeanzeta(2),abs(igridzeta(2)) + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- +c & ,imeanzeta(1),abs(igridzeta(1)),imeanzeta(2),abs(igridzeta(2)) + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + endif + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4)) +cJ.Peng----2010-10-01----------------------------------------------- + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4) + & ,8(', ',f8.1)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(66) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf_sink (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta,igridzeta + & ,cps_vals,plastbar,rlastbar,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The "sink" in the subroutine name indicates that this output +c contains the whole kitchen sink of forecast storm info. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different, and the part after the radii +c data is different. Here's an example of the TPC standard +c atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c indicate the lat/lon at which the storm was *first* found in +c the model. The position may be either found within this run +c of the tracker, or that position may have been pulled from the +c tcvitals or gen_vitals record: +c +c 2000092500_F000_206N_0623W_13L, 2000092500, 03, GFSO, 036 +c , 243N, 675W, 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c , PLAS, RLAS, RMX, DIR, SPD, B, VTU, VTL +c , Z8MN, Z8MX, Z7MN, Z7MX +c +c As noted above, there is extra info at the end, after the +c "34, NEQ, 242, 163, 124, 208" radii info. Here is a key +c to indicate what these items are: +c +c PLAS: Pressure (mb) of last closed isobar +c RLAS: Radius of the last closed isobar in nm, 0 - 9999 nm. +c RMX: Radius of max winds, 0 - 999 nm. +c DIR: Direction of storm motion. +c SPD: Speed of storm motion (m/s * 10). +c B: Hart's CPS "Parameter B" thickness asymmetry value (m). +c VTL: Hart's CPS thermal wind (Lower, 900-600) value. +c VTU: Hart's CPS thermal wind (Upper, 600-300) value. +c Z8MN: Mean value of 850 mb zeta surrounding storm. +c Z8MX: Max value of 850 mb zeta near storm. +c Z7MN: Mean value of 700 mb zeta surrounding storm. +c Z7MX: Max value of 700 mb zeta near storm. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd speed of storm translation +c istmdir direction of storm motion +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c plastbar pressure of last closed isobar (pa) +c rlastbar radius of last closed isobar (nm) +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real cps_vals(3) + real outlon,outlat + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar + integer iparamb,ivtl,ivtu,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1 + + if ( verb .ge. 3 ) then + print *,'+++ Top of output_atcf_sink, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/100. + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4)) + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',2(i4,', '),4(i3,', '),2(i5,', '),4(i4,', '),a9) + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',2(i4,', '),i3,', ',2(i4,', '),3(i6,', '),4(i4,', ') + & ,a9) + +c write (68,87) gstm%gv_gen_date,gstm%gv_gen_lat +c & ,gstm%gv_gen_latns,gstm%gv_gen_lon +c & ,gstm%gv_gen_lonew,gstm%gv_gen_type +c & ,inp%bcc,inp%byy,inp%bmm,inp%bdd,inp%bhh +c & ,adjustr(atcfname),ifcsthour,intlat,clatns,intlon,clonew +c & ,int((vmaxwind*conv_ms_knots) + 0.5) +c & ,int(xminmslp/100.0 + 0.5) +c & ,'XX, 34, NEQ' +c & ,istmspd,istmdir,imeanzeta(1),igridzeta(1) +c & ,imeanzeta(2),igridzeta(2) +c +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,6(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(68) + + return + end + +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_tcvitals (xlon,xlat,inp,ist,iovret) +c +c ABSTRACT: This subroutine outputs a tcvitals record. The +c lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE inparms; USE set_max_parms + USE verbose_output + + type (tcvcard) stm + type (datecard) inp + real xlon,xlat +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "storm" +c components for this storm, then we will change the specific +c components that we need to. + + stm = storm(ist) + + stm%tcv_center = 'AEAR' + + stm%tcv_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + stm%tcv_latns = 'S' + else + stm%tcv_latns = 'N' + endif + + if (xlon >= 180.) then + stm%tcv_lon = 3600 - int(xlon * 10. + 0.5) + stm%tcv_lonew = 'W' + else + stm%tcv_lon = int(xlon * 10. + 0.5) + stm%tcv_lonew = 'E' + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) stm + endif + + write (65,21) stm + + 21 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + +c +c bug fix for IBM: flush the output stream so it actually writes + flush(65) + + return + end + +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_gen_vitals (xlon,xlat,inp,ist,istmspd,istmdir + & ,iovret) +c +c ABSTRACT: This subroutine outputs a modified vitals record. +c The lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c The storm identifier is different than that for a standard +c tcvitals. The storm identifier contains the date/time that +c the storm was first identified, and the lat/lon position at +c which it was first identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE gen_vitals; USE inparms; USE set_max_parms + USE verbose_output + + implicit none + + type (gencard) gstm + type (datecard) inp + real xlon,xlat + integer ist,iovret,istmspd,istmdir +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the vitals record. + + if (gstm%gv_gen_date /= 99999) then + + if (gstm%gv_gen_type /= 'FOF') then + ! If this is not a 'FOF' storm (found on the fly storm), then + ! it must be a TC vitals storm, or a tropical cyclone, and we + ! don't want to create a vitals record for a tropical cyclone, + ! since we will rely on reading them from the TC Vitals + ! database instead. + return + endif + + else + + ! This storm is new in this forecast/analysis and was found on + ! the fly in the first time level for this run and there was no + ! previous vitals record for this system + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = 0 + + gstm%gv_gen_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_gen_latns = 'S' + else + gstm%gv_gen_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_gen_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'W' + else + gstm%gv_gen_lon = int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'E' + endif + + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + gstm%gv_obs_ymd = inp%bcc * 1000000 + & + inp%byy * 10000 + & + inp%bmm * 100 + & + inp%bdd + + gstm%gv_obs_hhmm = inp%bhh * 100 + + gstm%gv_obs_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_obs_latns = 'S' + else + gstm%gv_obs_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_obs_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'W' + else + gstm%gv_obs_lon = int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'E' + endif + + gstm%gv_stdir = istmdir + gstm%gv_stspd = istmspd + + gstm%gv_depth = 'U' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) gstm + endif + + write (67,21) gstm + + 21 format (i10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1,'_',a3,1x,i8,1x + & ,i4.4,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x + & ,i3,4(1x,i4),1x,a1) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(67) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- +c subroutine output_tracker_mask (masked_outc,kpds,kgds,lb,ifh +c & ,imax,jmax,iotmret) +c +c ABSTRACT: This subroutine outputs a GRIB record that contains the +c "mask" used to mask out areas surrounding low pressure centers +c that have been found during the search at each forecast hour. This +c mask is written out purely for diagnostic purposes. The GRIB +c identifier given to the mask in the pds is 850 mb height (you can +c make it anything you want). This is only done for the "midlat" +c and "tcgen" cases, since the runs for those cases use a mask while +c the regular "tracker" run (that is, the run which strictly tracks +c only those storms in the TC vitals file) does not. +c +c INPUT: +c masked_outc logical array containing mask +c kpds GRIB pds array +c kgds GRIB gds array +c ifh integer counter index for current forecast hour +c imax num points is i-direction of input grid +c jmax num points is j-direction of input grid +c +c OUTPUT: +c iotmret return code from this subroutine + +c implicit none +cc +c integer ifh,imax,jmax,iotmret,kf,igoret,iix,jjx,ipret +c integer kpds(200),kgds(200) +c logical(1) masked_outc(imax,jmax),lb(imax,jmax) +c real xmask(imax,jmax) +cc +c if (ifh == 1) then +c call baopenw (77,"fort.77",igoret) +c print *,'baopenw: igoret= ',igoret +c +c if (igoret /= 0) then +c print *,' ' +c print *,'!!! ERROR in sub output_tracker_mask opening' +c print *,'!!! **OUTPUT** grib files. baopenw return codes:' +c print *,'!!! grib file 1 return code = igoret = ',igoret +c STOP 95 +c return +c endif +c endif +c +c xmask = 0.0 +c do jjx = 1,jmax +c do iix = 1,imax +c if (masked_outc(iix,jjx)) then +c xmask(iix,jjx) = 1.0 +c else +c xmask(iix,jjx) = 0.0 +c endif +c enddo +c enddo +c +c kf = imax * jmax +c +cc kpds(1) = 7 ; kpds(2) = 80 +cc kpds(3) = 2 ; kpds(4) = 192 +c +c kpds(5) = 7 +c kpds(6) = 100 +c kpds(7) = 850 +c kpds(22) = 0 +c +ccc kpds(7) = 850 ; kpds(8) = 02 +ccc kpds(9) = 11 ; kpds(10) = 4 +ccc kpds(11) = 0 ; kpds(12) = 0 +ccc kpds(13) = 1 ; kpds(14) = ifcsthour +ccc kpds(15) = 0 ; kpds(16) = 10 +ccc kpds(17) = 0 ; kpds(18) = 1 +ccc kpds(19) = 2 ; kpds(20) = 0 +ccc kpds(21) = 21 ; kpds(22) = 0 +ccc kpds(23) = 0 ; kpds(24) = 0 +ccc kpds(25) = 0 +ccc kgds(1) = 0 ; kgds(2) = imax +ccc kgds(3) = jmax ; kgds(4) = 90000 +ccc kgds(5) = 0 ; kgds(6) = 128 +ccc kgds(7) = -90000 ; kgds(8) = -1000 +ccc kgds(9) = 1000 ; kgds(10) = 1000 +ccc kgds(11) = 0 ; kgds(12) = 0 +ccc kgds(13) = 0 ; kgds(14) = 0 +ccc kgds(15) = 0 ; kgds(16) = 0 +ccc kgds(17) = 0 ; kgds(18) = 0 +ccc kgds(19) = 0 ; kgds(20) = 255 +cc +cc write(*,980) kpds(1),kpds(2) +cc write(*,981) kpds(3),kpds(4) +cc write(*,982) kpds(5),kpds(6) +cc write(*,983) kpds(7),kpds(8) +cc write(*,984) kpds(9),kpds(10) +cc write(*,985) kpds(11),kpds(12) +cc write(*,986) kpds(13),kpds(14) +cc write(*,987) kpds(15),kpds(16) +cc write(*,988) kpds(17),kpds(18) +cc write(*,989) kpds(19),kpds(20) +cc write(*,990) kpds(21),kpds(22) +cc write(*,991) kpds(23),kpds(24) +cc write(*,992) kpds(25) +cc write(*,880) kgds(1),kgds(2) +cc write(*,881) kgds(3),kgds(4) +cc write(*,882) kgds(5),kgds(6) +cc write(*,883) kgds(7),kgds(8) +cc write(*,884) kgds(9),kgds(10) +cc write(*,885) kgds(11),kgds(12) +cc write(*,886) kgds(13),kgds(14) +cc write(*,887) kgds(15),kgds(16) +cc write(*,888) kgds(17),kgds(18) +cc write(*,889) kgds(19),kgds(20) +cc write(*,890) kgds(21),kgds(22) +ccc +cc 980 format(' kpds(1) = ',i7,' kpds(2) = ',i7) +cc 981 format(' kpds(3) = ',i7,' kpds(4) = ',i7) +ccc 982 format(' kpds(5) = ',i7,' kpds(6) = ',i7) +cc 983 format(' kpds(7) = ',i7,' kpds(8) = ',i7) +cc 984 format(' kpds(9) = ',i7,' kpds(10) = ',i7) +cc 985 format(' kpds(11) = ',i7,' kpds(12) = ',i7) +cc 986 format(' kpds(13) = ',i7,' kpds(14) = ',i7) +cc 987 format(' kpds(15) = ',i7,' kpds(16) = ',i7) +cc 988 format(' kpds(17) = ',i7,' kpds(18) = ',i7) +cc 989 format(' kpds(19) = ',i7,' kpds(20) = ',i7) +cc 990 format(' kpds(21) = ',i7,' kpds(22) = ',i7) +cc 991 format(' kpds(23) = ',i7,' kpds(24) = ',i7) +cc 992 format(' kpds(25) = ',i7) +cc 880 format(' kgds(1) = ',i7,' kgds(2) = ',i7) +cc 881 format(' kgds(3) = ',i7,' kgds(4) = ',i7) +cc 882 format(' kgds(5) = ',i7,' kgds(6) = ',i7) +cc 883 format(' kgds(7) = ',i7,' kgds(8) = ',i7) +cc 884 format(' kgds(9) = ',i7,' kgds(10) = ',i7) +cc 885 format(' kgds(11) = ',i7,' kgds(12) = ',i7) +cc 886 format(' kgds(13) = ',i7,' kgds(14) = ',i7) +cc 887 format(' kgds(15) = ',i7,' kgds(16) = ',i7) +cc 888 format(' kgds(17) = ',i7,' kgds(18) = ',i7) +cc 889 format(' kgds(19) = ',i7,' kgds(20) = ',i7) +cc 890 format(' kgds(20) = ',i7,' kgds(22) = ',i7) +c +c print *,'just before call to putgb, kf= ',kf +c call putgb (77,kf,kpds,kgds,lb,xmask,ipret) +c print *,'just after call to putgb, kf= ',kf +c if (ipret == 0) then +c print *,' ' +c print *,'+++ IPRET = 0 after call to putgb' +c print *,' ' +c else +c print *,' ' +c print *,'!!!!!! ERROR: IPRET NE 0 AFTER CALL TO PUTGB !!!' +c print *,' ' +c endif +cc +cc bug fix for IBM: flush the output stream so it actually writes +c flush(6) +c +c return +c end +c +cc +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_next_ges (fixlon,fixlat,ist,ifh,imax,jmax + & ,dx,dy,modelid,valid_pt,readflag,maxstorm,istmspd + & ,istmdir,ctype,trkrinfo,ignret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. It does this by using two different +c methods and averaging the results from those two. The +c first method is a simple linear extrapolation made by +c basically drawing a line from the previous position +c through the current fix position and assuming straight +c line motion. The second method is to do a barnes +c smoothing of u & v in the vicinity of the storm at 850, +c 700 & 500 mb to get an average environmental wind +c vector at each level, and then move the storm according +c to the vector at each level. Then a weighted average is +c taken of all these positions from methods 1 & 2 to get +c the consensus for the guess position. NOTE: For a +c regional model and a storm that is relatively close to +c the model boundary, there is a strong possibility that +c the barnes analysis subroutine will fail due to trying +c to access grid points beyond the model's lateral +c boundary. In this case, the redlm & ridlm are halved +c and barnes is called again. If it still fails, then +c just use the result from method 1 as a default. +c +c INPUT: +c fixlon Array with longitudes of fix positions +c fixlat Array with latitudes of fix positions +c ist Storm number currently being processed +c ifh Forecast hour currently being processed +c imax Max number of pts in x-direction for this grid +c jmax Max number of pts in y-direction for this grid +c dx grid-spacing of the model in the i-direction +c dy grid-spacing of the model in the j-direction +c modelid Integer indicating what model's data is being processed +c valid_pt Logical; bitmap indicating if valid data at that pt. +c readflag Logical; Tells whether or not a variable was read in +c for this model +c maxstorm Max # of storms that can be handled in this run +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, eventually +c in the barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c istmspd The speed that the storm would have to move to get from +c the current position to the next guess position +c istmdir The direction in which the storm would have to move to +c get from the current position to the next guess position +c +c LOCAL: +c dt Number of seconds between successive forecast times +c for this particular model. +c dtkm Distance in meters of 1 degree latitude +c icutmax Max number of times to cut the ridlm and redlm in half, +c for use in calling barnes. If you're using a regional +c model and on the first call to barnes you try to access +c a point that's outside the model grid boundary, we'll +c call barnes again, but not before cutting the redlm and +c ridlm in half. icutmax says how many times to allow +c this cutting in half before giving up and just going +c with the extrapolation method. At first writing, we'll +c set icutmax to 2, so that it will allow the ridlm to +c get down to 500 km (originally 2000 km) and the redlm +c to 125 km (originally 500 km). +c *** NOTE: After testing the system, it was found that if +c we cut these radii, the u and v values that are +c calculated from barnes are too strongly influenced by +c the near-storm environment and, especially for asymmetric +c systems, resulted in u and v values being much too strong. +c As such, we will not allow these values to be cut, and if +c we hit the boundaries in barnes, we'll just use the +c extrapolation method, which has seemed to work just fine. +c +c OTHER: (slonfg, slatfg & storm defined in module def_vitals) +c slonfg Array containing first guess longitude positions +c slatfg Array containing first guess latitude positions +c storm Contains tcvitals information +c + USE radii; USE def_vitals; USE set_max_parms; USE grid_bounds + USE tracked_parms; USE level_parms; USE trig_vals; USE trkrparms + USE gen_vitals + USE verbose_output + + type (trackstuff) trkrinfo + integer icutmax,istmspd,istmdir,bskip + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + character*1 :: in_grid, extrap_flag, barnes_flag + character(*) ctype +c logical(1) valid_pt(imax,jmax),readflag(14) +cJ.Peng----2014-10-01-------------------------------------- + logical(1) valid_pt(imax,jmax),readflag(16) +c + in_grid = 'n' + extrap_flag = 'y' +c +c For updating the first guess, if Method 1 and Method 2 are both +c able to be done, give the following weights to the 2 methods. +c + data barneswt /0.50/, extrapwt /0.50/ +c +c ------------------------------- +c METHOD 1: LINEAR EXTRAPOLATION +c ------------------------------- +c First, just do a simple linear extrapolation from the previous +c fix position through the current fix position. If it's the +c first time (vt=0), then use the storm motion vector and storm +c speed information from the TC Vitals card. +c + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(ist)%tcv_stdir == -99 .or. + & storm(ist)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GET_NEXT_GES, at fcst hour = 0, either ' + print *,'!!! storm motion or storm speed = -99 on TCV card.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(ist)%tcv_stdir + print *,'!!! storm motion speed= ',storm(ist)%tcv_stspd + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + extrap_flag = 'n' + else + ucomp = sin(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + vcomp = cos(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(ist,ifh) + ydeg + avglat = 0.5 * (extraplat + fixlat(ist,ifh)) + if (avglat > 89.5) avglat = 89.0 + if (avglat < -89.5) avglat = -89.0 + cosfac = cos(avglat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(ist,ifh) + xdeg + endif + else + +c Do a simple linear extrapolation of the current motion of the +c storm. Follow a line from the fix position from the last fix +c through the current fix and extrapolate out. To figure out the +c new latitude, just see how many deg lat the storm moved since +c last time and add it to the current fix latitude. To calculate +c the new fix longitude, though, we need to see how many deg lon +c the storm moved since the last time, convert that to the +c distance (km) the storm travelled in the x-direction (at an +c average latitude between the current and previous latitudes), +c and then add that distance on to the current longitude and +c convert that distance to the num of degrees the storm has +c travelled in the x-direction (at an average latitude between +c the current and next(extrap) latitudes). +c +c UPDATE Feb 2009: To account for the possibility of using +c irregularly spaced forecast hours (e.g., 6,10,10.5,...etc), +c I had to modify this linear extrapolation. + + + if (fixlat(ist,ifh-1) > -900.0 .and. + & fixlon(ist,ifh-1) > -900.0) then + + ylatdegmove_last = fixlat(ist,ifh) - fixlat(ist,ifh-1) + xlondegmove_last = fixlon(ist,ifh) - fixlon(ist,ifh-1) + + xnumh_last = fhreal(ifh) - fhreal(ifh-1) + + ylatdegmove_last_perhour = ylatdegmove_last / xnumh_last + xlondegmove_last_perhour = xlondegmove_last / xnumh_last + + xnumh_next = fhreal(ifh+1) - fhreal(ifh) + + extraplat = fixlat(ist,ifh) + & + (ylatdegmove_last_perhour * xnumh_next) + + yoldavglat = 0.5 * (fixlat(ist,ifh) + fixlat(ist,ifh-1)) + yoldcosfac = cos (dtr * yoldavglat) + xdistmove_last = xlondegmove_last * dtk * yoldcosfac + + xdistmove_last_perhour = xdistmove_last / xnumh_last + + ynewavglat = 0.5 * (extraplat + fixlat(ist,ifh)) + ynewcosfac = cos(dtr * ynewavglat) + xdegnew = (xdistmove_last_perhour * xnumh_next) + & / (dtk * ynewcosfac) + extraplon = fixlon(ist,ifh) + xdegnew + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GET_NEXT_GES, at fcst hour = ',ifcsthr + print *,'!!! the lon and lat positions for the previous' + print *,'!!! forecast hour are -999, meaning that this is a' + print *,'!!! new storm, so we cannot use the extrap method.' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + extrap_flag = 'n' + + endif + + endif + +c ------------------------------- +c METHOD 2: Barnes analysis +c ------------------------------- +c Do a barnes analysis on the u & v components of the wind near the +c storm to get an average u & v, then advect the storm according to +c the average wind vector obtained. The call to get_ij_bounds is +c needed in order to restrict the number of grid points that are +c searched in the barnes subroutine. See Abstract from this +c subroutine for further details. + + npts = ceiling(ridlm/(dtk*((dx+dy)/2))) + + call get_ij_bounds (npts,0,ridlm,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for ' + print *,'!!! storm number ',ist + endif + + ignret = 92 + return + endif + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if ((dx+dy)/2 > 0.20) then + bskip = 1 + else if ((dx+dy)/2 > 0.10 .and. (dx+dy)/2 <= 0.20) then + bskip = 2 + else if ((dx+dy)/2 > 0.05 .and. (dx+dy)/2 <= 0.10) then + bskip = 3 + else if ((dx+dy)/2 > 0.03 .and. (dx+dy)/2 <= 0.05) then + bskip = 5 + else if ((dx+dy)/2 <= 0.03) then + bskip = 10 + endif + +c Calculate average wind at each level (currently: 850, 700 & 500) + + re = redlm + ri = ridlm + icut = 0 + + if (trkrinfo%type == 'midlat') then + icutmax = 2 + else + icutmax = 1 + endif + + radmaxloop: do while (icut <= icutmax .and. in_grid == 'n') + + ubar = 0.0; vbar = 0.0 + iuret = 0; ivret = 0 + wgttot = 0.0 + ibarnct = 0 + barnes_flag = 'n' + + levelloop: do n=1,nlevg + + select case (n) + case (1); ix1=3; ix2=4 ! For 850 mb readflags + case (2); ix1=5; ix2=6 ! For 700 mb readflags + case (3); ix1=12; ix2=13 ! For 500 mb readflags + end select + + if (readflag(ix1) .and. readflag(ix2)) then + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,u(1,1,n),valid_pt + & ,bskip,re,ri,uavg,icount,ctype,trkrinfo,iuret) + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,v(1,1,n),valid_pt + & ,bskip,re,ri,vavg,icount,ctype,trkrinfo,ivret) + + if (iuret /= 0 .or. ivret /= 0) then + +c ...barnes probably tried to access a pt outside the grid +c domain. So, reduce by half the distance from the center +c of the farthest pt that barnes tries to access, exit this +c loop, and try it again with the smaller re and ri. + + iuret = 96; ivret = 96 + reold = re + riold = ri + re = 0.5 * re + ri = 0.5 * ri + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: While attempting to use the barnes ' + print *,'method to update the first guess, the ' + print *,'algorithm tried to access a grid point that ' + print *,'does not have valid data, meaning that too ' + print *,'large a radius is being searched. So, the 2 ' + print *,'radii, re and ri, are being halved and, if the' + print *,'value of icutmax > 0, the algorithm will be ' + print *,'run again. Otherwise, if icutmax = 0, only ' + print *,'the extrapolation method will be used.' + print *,'iuret= ',iuret,' ivret= ',ivret,' icut= ',icut + print *,'Old re = ',reold,' New re = ',re + print *,'Old ri = ',riold,' New ri = ',ri + endif + + exit levelloop + + else + ubar = ubar + wgts(n) * uavg + vbar = vbar + wgts(n) * vavg + wgttot = wgttot + wgts(n) + ibarnct = ibarnct + 1 + endif + + endif + + enddo levelloop + + if (ibarnct > 0 .and. wgttot > 0.0) then + barnes_flag = 'y' + in_grid = 'y' + ubar = ubar / wgttot + vbar = vbar / wgttot + barnlat = fixlat(ist,ifh) + (vbar * dt)/dtkm + cosfac = cos (dtr * 0.5 * (fixlat(ist,ifh) + barnlat)) + barnlon = fixlon(ist,ifh) + (ubar * dt)/(dtkm * cosfac) + +c This next if statement says that if we've had to reduce the +c size of the barnes analysis domain twice already, then we've +c only done the analysis on a much smaller area, and this +c doesn't give us as good a picture of the average winds in the +c area of the storm, so reduce the emphasis we place on the +c barnes method. + + if (icut >= 2) barneswt = barneswt / 2. + + else + barnes_flag = 'n' + endif + + icut = icut + 1 + + enddo radmaxloop + +c --------------------- +c Average the results +c --------------------- +c Now do a weighted average of the positions obtained from the +c linear extrapolation and the barnes analysis methods. + + if (extrap_flag == 'y' .and. barnes_flag == 'y') then + wt_total = barneswt + extrapwt + slatfg(ist,ifh+1) = (barneswt * barnlat + extrapwt * extraplat) + & / wt_total + + ! Note that in any of these statements just below, in order for + ! any of these to be > 360, the original fixlon must be close + ! to 360, i.e., in the far eastern part of the grid, as opposed + ! to being in the far western part (e.g., 0-2 deg East or so). + ! Conversely, for any of these to be < 0, the original fixlon + ! must be close to 0, i.e., in the far *western* part of the + ! grid. + + if (fixlon(ist,ifh) > 330.0) then + + if (extraplon > 360. .or. barnlon > 360.) then + + if (trkrinfo%gridtype == 'global') then + + continue ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon > 360 or' + print *,'!!! barnlon > 360 for a non-global grid. We ' + print *,'!!! only do GM wrapping for global grids.' + print *,'!!! extraplon= ',extraplon,' barnlon= ',barnlon + endif + + ignret = 95 + return + + endif + + endif + + elseif (fixlon(ist,ifh) < 30.0) then + + if (extraplon < 0. .or. barnlon < 0.) then + + if (trkrinfo%gridtype == 'global') then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon < 0 or ' + print *,'!!! barnlon < 0 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + + endif + + endif + + else + + continue ! extraplon and barnlon do not need to be modified + ! since there should be no way that a storm + ! currently east of 30E and west of 30W could make + ! it to the Greenwich Mer in one forecast interval + + endif + + slonfg(ist,ifh+1) = (barneswt * barnlon + extrapwt * extraplon) + & / wt_total + + if (slonfg(ist,ifh+1) > 360.) then + ! If we've GM-wrapped past 360, adjust it to be 0-360... + slonfg(ist,ifh+1) = slonfg(ist,ifh+1) - 360. + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 360.-barnlon,barnlat + write (6,43) 360.-extraplon,extraplat + endif + + ignret = 0 + else if (extrap_flag == 'y' .and. barnes_flag == 'n') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_next_ges, barnes method was not ' + print *,'!!! done for updating the first guess for this ' + print *,'!!! storm. Only the linear extrapolation method ' + print *,'!!! was used.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + slatfg(ist,ifh+1) = extraplat + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 0.0,0.0 + write (6,43) 360.-extraplon,extraplat + endif + + ignret = 0 + else if (extrap_flag == 'n' .and. barnes_flag == 'y') then + slatfg(ist,ifh+1) = barnlat + if (barnlon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (barnlon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = barnlon + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 360.-barnlon,barnlat + write (6,43) 0.0,0.0 + endif + + ignret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges, new position guess not' + print *,'!!! made. Could not get guess using either barnes' + print *,'!!! method or extrapolation method.' + print *,'!!! extrap_flag = ',extrap_flag + print *,'!!! barnes_flag = ',barnes_flag + print *,'!!! Storm number = ',ist,' ifh = ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + write (6,41) 0.0,0.0 + write (6,43) 0.0,0.0 + endif + + ignret = 95 + endif + + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| Current fix & updated fix positions |' + print *,'-------------------------------------------------- ' + print *,'| In get_next_ges, current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',ist + print *,'| Return code from get_next_ges = ',ignret + print *,'| Storm Name = ',storm(ist)%tcv_storm_name + print *,'| Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + write (6,21) fixlat(ist,ifh) + write (6,23) 360.-fixlon(ist,ifh),fixlon(ist,ifh) + write (6,25) slatfg(ist,ifh+1) + write (6,27) 360.-slonfg(ist,ifh+1),slonfg(ist,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current fix lat is ',f7.2) + 23 format (' | Current fix lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') + 41 format (' --- barnlon= ',f7.2,'W barnlat= ',f7.2) + 43 format (' --- extraplon= ',f7.2,'W extraplat= ',f7.2) + +c Now calculate the speed that the storm would have to move at in +c order to make it to the next forecast position. We will use +c this information in writing out the "gen_vitals" record, if this +c is requested. + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh) + & ,slonfg(ist,ifh+1),slatfg(ist,ifh+1),dist,degrees) + + ! convert distance from km to meters, then get speed in m/s. + + distm = dist * 1000. + stmspd = distm / dt + istmspd = int ((stmspd * 10) + 0.5) + + xincr = slonfg(ist,ifh+1) - fixlon(ist,ifh) + yincr = slatfg(ist,ifh+1) - fixlat(ist,ifh) + + if ( verb .ge. 3 ) then + print *,'iocheck, dist= ',dist,' distm= ',distm + print *,'iocheck, stmspd= ',stmspd,' istmspd= ',istmspd + print *,'iocheck, xincr= ',xincr,' yincr= ',yincr + endif + + if (xincr < 0.0 .and. slonfg(ist,ifh+1) < 30.0 .and. + & fixlon(ist,ifh) > 300.0) then + ! This means we have a storm moving east across the GM, and + ! so we are subtracting, for example, something like + ! 0.5 - 359.5, so redo xincr, but add 360 to slonfg first... + xincr = (slonfg(ist,ifh+1) + 360.0) - fixlon(ist,ifh) + else if (xincr > 300.0) then + ! This means we have a storm moving west across the GM, and + ! so we are subtracting, for example, something like + ! 359.5 - 0.5, so redo xincr, but add 360 to fixlon first... + xincr = slonfg(ist,ifh+1) - (fixlon(ist,ifh) + 360.0) + endif + + if (xincr == 0.0) then + if (yincr == 0.0) then + stmdir = 0.0 + else if (yincr > 0) then + stmdir = 360.0 + else if (yincr < 0) then + stmdir = 180.0 + endif + else if (xincr > 0.0) then + if (yincr == 0.0) then + stmdir = 90.0 + else + arct = atan(yincr/xincr) + stmdir = 90. - arct / dtr + endif + else if (xincr < 0.0) then + if (yincr == 0.0) then + stmdir = 270.0 + else + arct = atan(yincr/xincr) + stmdir = 270. - arct / dtr + endif + endif + + istmdir = int (stmdir + 0.5) + if (istmdir > 360) then + istmdir = 360 + else if (istmdir < 0) then + istmdir = 0 + endif + + if ( verb .ge. 3 ) then + print *,'iocheck, stmdir= ',stmdir,' istmdir= ',istmdir + endif + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getradii (xcenlon,xcenlat,imax,jmax,dx,dy,valid_pt + & ,cstormid,ifcsthr,vradius,trkrinfo + & ,need_to_expand_r34,radmax,igrret) +c +c ABSTRACT: This subroutine looks through the wind data near an +c input storm center (fixlon,fixlat) and gets the radii of various +c surface winds in each of the 4 storm quadrants (NE,NW,SE,SW). +c The wind thresholds that are sought are gale force (34kt|17.5m/s), +c storm force (50kt|25.7m/s), and hurricane force (64kt|32.9m/s). +c This subroutine calls the Cray subroutine orders, which is a +c Cray-optimized sort routine. +c +c UPDATE (AUG 2001): The Cray subroutine orders was ported to the +c SP by NCEP personnel. On the SP version, some changes were +c apparently made so that the size of the arrays for calling +c arguments 2, 3 and 4 (iwork, dtemp and isortix in my calling +c routine) must be the same. This was not the case on the Crays, +c and this was causing the tracker to crash for cases far north +c on fine grids (GFDL 1/3 grid). +c +c UPDATE (AUG 2012): The call to the Cray subroutine orders was +c replaced with a call to qsort, which uses a quicksort sorting +c algorithm. While this is not the fastest sorting routine out +c there, we don't do a lot of sorting here, and qsort is simple +c and it is portable. +c +c UPDATE (April 2013): For the radii, we encountered a problem with +c radmax being too small. It was set at 650 km. Hurricane Sandy +c exceeded this in the models, so the values returned from getradii +c were close to the default radmax value of 650 km (350 nm), instead +c of much higher as they should have been. To fix it, we now use an +c iterative technique, where we start with radmax as a small value +c (450 km). If getradii returns a value for R34 in a quadrant that +c does not exceed 0.97*radmax, then that value is ok. If it does +c exceed 0.97*radmax, then we bump up radmax by 50 km and call +c getradii again, looking to diagnose radii only in those quadrants +c where the need_to_expand_r34 flag = 'n'. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c cstormid 3-character storm ATCF ID (e.g., 03L, 11E, etc) +c ifcsthr integer value for current forecast hour +c trkrinfo derived type containing various info on the storm +c need_to_expand_r34 1-character array that specifies which of the +c 4 quadrants still need to be expanded on this time +c through getradii in order to get an R34 value that is +c not right at the outermost boundary. +c radmax input max radius (km) that will be used for this +c iteration of getradii. +c +c OUTPUT: +c +c igrret return code from this subroutine +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c +c LOCAL: +c +c radmax the maximum radius to look for winds for the various +c thresholds. +c quadinfo This array contains the magnitude of the near-surface +c winds and the distance from the gridpoint to the fix +c position for each point in each quadrant that is within +c the maximum allowed radius, radmax. quadinfo is +c allocated within this subroutine, and is allocated as +c (quadrant, num_pts_in_quadrant, data_type), where +c data_type is either windspeed(1) or distance(2) from +c storm center to grid point. +c quadmax This array contains the max surface wind in each +c quadrant, plus the location of it and the distance from +c the storm center. This information is critical to +c identifying when this subroutine is malfunctioning. + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE level_parms + USE trkrparms + USE verbose_output + +c + type (trackstuff) trkrinfo +c + logical(1) valid_pt(imax,jmax) +c dimension iwork(257) + real, allocatable :: quadinfo(:,:,:),iwork(:) + real quadmax(4,4) + real exactdistnm,exactdistkm,radmax,degrees,cosarg + real rlonb,rlonc,rlatb,rlatc + real pt_heading_rad,pt_heading,d + integer, allocatable :: isortix(:) + integer iwindix,ipoint,ifcsthr + integer quadct(4),vradius(3,4) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: dtemp(:) + real :: windthresh(3) = (/17.5,25.7,32.9/) + character cstormid*3 + character :: need_to_expand_r34(4)*1 + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *************************************************** ' + print *,' AT BEGINNING OF GETRADII, input radmax= ',radmax + print *,' *************************************************** ' + print *,' ' + print *,'xcenlon= ',xcenlon,' xcenlat= ',xcenlat + print *,'imax= ',imax,' jmax= ',jmax,' dx= ',dx,' dy= ',dy + endif + + igrret = 0 + +c ----------------------------------------------------------- +c PART 1: Define the maximum radius for which you'll search +c for the wind values, and then get the beginning and ending +c i and j points for that sub-region to search. Define this +c maximum radius (radmax) in terms of km. +c ----------------------------------------------------------- + +c radmax = 650.0 ! This value is in units of km. With April 2013 +c ! update, this is now defined in calling routine + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + igrret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine getradii' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + igrret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmax is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmax/(dtk*dx))/cosfac) + numjpts = ceiling(radmax/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (ibeg < 1) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + igrret = 99 + return + endif + + endif + + if (jbeg < 1) jbeg = 1 + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in getradii calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Wind radii will not be calculated for this time.' + endif + + igrret = 99 + return + endif + + if (iend > imax) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + igrret = 99 + return + endif + + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getradii, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + +c ----------------------------------------------------------- +c PART 2: Within the area of grid points defined by jbeg, +c jend, ibeg and iend, (1) calculate all the wind speeds at +c each grid point, (2) calculate all of the distances from +c each grid point to the storm center, (3) assign each grid +c point to one of the 4 quadrants (NE,NW,SE,SW), (4) in each +c quadrant, sort the points, based on windspeed. +c ----------------------------------------------------------- + + jnum = jend - jbeg + 1 + inum = iend - ibeg + 1 +c numalloc = ((jnum * inum) / 2) + inum/2 + jnum/2 + numalloc = jnum * inum + inum/2 + jnum/2 + + if ( verb .ge. 3 ) then + print *,'in getradii, numalloc= ',numalloc,' radmax= ',radmax + endif + + allocate (quadinfo(4,numalloc,2),stat=iqa) + + if (iqa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub getradii allocating quadinfo array.' + print *,'!!! iqa = ',iqa + endif + + igrret = 94 + return + endif + + quadct = 0 + +c Calculate the distances and wind speeds at each grid point. If +c the distance is < radmax, include that wind info in the +c appropriate quadinfo array location for that quadrant. + + quadmax = 0.0 + + jloop: do j=jbeg,jend + iloop: do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point in question = ',i + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + igrret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub getradii' + print *,'!!! for a non-global grid. i= ',i + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + igrret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + if (dist > radmax) cycle iloop + + if (valid_pt(ip,j)) then + + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + +cc print *,'i= ',i,' j= ',j,' dist= ',dist,' vmag= ',vmag + + ! Calculate the angle from the center point to this point + ! and then assign this point to the appropriate quadrant bin + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-xcenlon) * dtr + rlatb = xcenlat * dtr + d = degrees * dtr + +c write (6,59) 360.-xcenlon,xcenlat,360.-glon(ip),glat +c +c write (6,61) d/dtr,rlatc/dtr,360.-(rlonc/dtr),rlatb/dtr +c & ,360.-(rlonb/dtr),sin(rlatc),sin(rlatb),cos(d) +c & ,sin(d),cos(rlatb) +c +c +c 59 format (1x,'+++ gr, xcenlon= ',f8.3,'W xcenlat= ' +c & ,f8.3,' glon= ',f8.3,'W glat= ',f8.3) +c +c 61 format (1x,'+++ gr, d rlatc rlonc rlatb rlonb= ',5f9.4 +c & ,' sin(rlatc)= ',f8.6,' sin(rlatb)= ',f8.6 +c & ,' cos(d)= ',f8.6,' sin(d)= ',f8.6 +c & ,' cos(rlatb)= ',f8.6) + + if (d == 0.0) then + + pt_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d)) / + & (sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_heading_rad = acos(cosarg) + else + pt_heading_rad = 2*pi - acos(cosarg) + endif + + pt_heading = pt_heading_rad / dtr + + endif + + if (pt_heading >= 0.0 .and. pt_heading < 90.) then + ! NE quadrant + iq = 1 + else if (pt_heading >= 90.0 .and. pt_heading < 180.) then + ! SE quadrant + iq = 2 + else if (pt_heading >= 180.0 .and. pt_heading < 270.) then + ! SW quadrant + iq = 3 + else if (pt_heading >= 270.0 .and. pt_heading <= 360.) then + ! NW quadrant + iq = 4 + endif + +c write (6,73) xcenlat,360.-xcenlon,j,i,ip,glat(j) +c & ,360.-glon(ip),pt_heading,iq + + 73 format (1x,'+++ getradii clat clon: ',f6.2,' ',f7.2,'W',3i4 + & ,' plat plon: ',f6.2,' ',f7.2,'W Dir: ',f7.2 + & ,' Quad: ',i2) + + quadct(iq) = quadct(iq) + 1 + quadinfo(iq,quadct(iq),1) = vmag + quadinfo(iq,quadct(iq),2) = dist + if (vmag > quadmax(iq,4)) then + quadmax(iq,1) = glon(ip) + quadmax(iq,2) = glat(j) + quadmax(iq,3) = dist + quadmax(iq,4) = vmag + endif + + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After loop, quadct(1)= ',quadct(1),' quadct(2)= ' + & ,quadct(2) + print *,' quadct(3)= ',quadct(3),' quadct(4)= ' + & ,quadct(4) + print *,' ' + + write (6,110) cstormid,ifcsthr,'NE',quadmax(1,1),quadmax(1,2) + & ,quadmax(1,3)*0.539638,quadmax(1,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SE',quadmax(2,1),quadmax(2,2) + & ,quadmax(2,3)*0.539638,quadmax(2,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SW',quadmax(3,1),quadmax(3,2) + & ,quadmax(3,3)*0.539638,quadmax(3,4)*1.9427 + write (6,110) cstormid,ifcsthr,'NW',quadmax(4,1),quadmax(4,2) + & ,quadmax(4,3)*0.539638,quadmax(4,4)*1.9427 + print *,' ' + + 110 format (' quadmax: ',a3,1x,i3.3,1x,a2,1x,' lon: ',f6.2,'E',1x + & ,' lat: ',f6.2,' radius: ',f7.2,' nm',2x,' vmag: ' + & ,f6.2,' kts') + endif + +c Now go through each quadrant and put the wind speed distance info +c into a temporary array (dtemp), sort that array, and then scan +c through that array to find the various thresholds. + + quadrantloop: do k=1,4 + + if (need_to_expand_r34(k) == 'y') then + print *,'---> R34 search underway for quadrant ',k + & ,' radmax= ',radmax + continue + else + print *,'+ R34 okay for quadrant ',k,'... skipping...' + cycle quadrantloop + endif + + if (allocated(isortix)) deallocate (isortix) + if (allocated(dtemp)) deallocate (dtemp) + if (allocated(iwork)) deallocate (iwork) + allocate (isortix(quadct(k)),stat=iisa) + allocate (dtemp(quadct(k)),stat=idta) + allocate (iwork(quadct(k)),stat=iwa) + if (iisa /= 0 .or. idta /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii allocating isortix or dtemp' + print *,'!!! array for quadrant= ',k,' iisa = ',iisa + print *,'!!! idta= ',idta,' iwa= ',iwa + endif + + itret = 94 + return + endif + +c ------------------- + + do m=1,quadct(k) + dtemp(m) = quadinfo(k,m,2) + enddo + + imode = 2 + isortix = 0 + + call qsort (dtemp,isortix,quadct(k)) + +ccccc call orders (imode,iwork,dtemp,isortix,quadct(k),1,8,1) +cccc call orders_4byte (imode,iwork,dtemp,isortix +cccc & ,quadct(k),1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' +c ************************************************************** +c--- mf 20100609 +c CAUSE OF SEG FAULT!!!!!!!! -- not sure still an issue if dtemp +c properly allocated +c + !print *,' dtemp(isortix(1)) = ',dtemp(isortix(1)) + print *,' dtemp(isortix(quadct(k)))= ' + & ,dtemp(isortix(quadct(k))) + print *,' isortix(1) = ',isortix(1) + print *,' isortix(quadct(k)) = ',isortix(quadct(k)) + endif + +c ! Uncomment these next lines to see a listing in the output of +c ! all wind values & distances in this quadrant less than radmax +c do iqq = 1,quadct(k) +c print *,' iqq= ',iqq,' vmag= ',quadinfo(k,isortix(iqq),1) +c & ,' dist= ',quadinfo(k,isortix(iqq),2) +c enddo + +c ------------------- + + if (quadct(k) < 2) then ! not enough members in array + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GETRADII, NOT ENOUGH MEMBERS IN ARRAY FOR' + print *,'!!! QUADRANT #',k,' .... # members = quadct(k)= ' + & ,quadct(k) + print *,'!!! SETTING ALL VRADII = 0 for quadrant = ',k + endif + + vradius(1,k) = 0 + vradius(2,k) = 0 + vradius(3,k) = 0 + cycle quadrantloop + endif + +c Within this quadrant, go through the sorted array of wind +c magnitudes and compare those wind values against the set +c wind thresholds to get the wind radii. The array has +c been sorted by distance from the storm center in order of +c closest (ipoint=1) to farthest (ipoint=quadct(k)). We +c analyze these wind values by starting at the farthest +c point and moving inward until we hit a point that has a +c wind value of at least 34-knot winds (17.5 m/s). When +c we find that point, we interpolate between that point and +c the next farthest out point to get the distance that would +c be for the exact 17.5 m/s value. We then continue searching +c through the wind values down closer to the storm center to +c see if we can find values for the 50- and 64-knot winds. + + iwindix = 1 + ipoint = quadct(k) + 1 + + threshloop: do while (iwindix <= 3 .and. ipoint > 1) + + ipoint = ipoint - 1 + + if (quadinfo(k,isortix(ipoint),1) < windthresh(iwindix)) then + cycle threshloop + else + if (ipoint == quadct(k)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In getradii, a max wind radius was' + print *,'!!! found at the maximum radius checked, so ' + print *,'!!! you may want to make sure that you are' + print *,'!!! checking at a far enough distance from ' + print *,'!!! the fix position, that is, you may want to' + print *,'!!! increase the value of radmax in subroutine' + print *,'!!! getradii. Currently, radmax (km) = ',radmax + print *,'!!! iwindix = ',iwindix,' quadrant= ',k + endif + + vradius(iwindix,k) = int( ((quadinfo(k,isortix(ipoint),2) + & * 0.5396) / 5.0) + 0.5) * 5 + else + +c Interpolate between the 2 closest distances to each wind +c threshold to get "exact" distance to that wind threshold +c radius, convert from km to nm, and then round to the +c nearest 5 nm (since TPC uses this precision). +c 7/23/98 UPDATE: Jim Gross has asked that values not be +c rounded to the nearest 5 nm, but rather only to the +c nearest 1 nm. + + exactdistkm = quadinfo(k,isortix(ipoint),2) + + & ( (quadinfo(k,isortix(ipoint),1) - windthresh(iwindix)) / + & (quadinfo(k,isortix(ipoint),1) - + & quadinfo(k,isortix(ipoint+1),1)) * + & ( (quadinfo(k,isortix(ipoint+1),2) - + & quadinfo(k,isortix(ipoint),2)) ) ) + + exactdistnm = exactdistkm * 0.5396 ! Convert km to nm + vradius(iwindix,k) = int(exactdistnm + 0.5) + +cc vradius(iwindix,k) = int( (exactdistnm / 5.0) + 0.5) * 5 + + + if ( verb .ge. 3 ) then + print *,'iwindix= ',iwindix,' exactdistnm = ' + & ,exactdistnm + print *,'vradius(iwindix,k) =',vradius(iwindix,k) + endif + + endif + +c The possibility exists, especially for coarse output +c grids, that there could be a jump over more than 1 wind- +c thresh category when going from 1 grid point to the next, so +c we need to account for this. For example, if 1 point has +c vmag = 15 m/s and the next point closer in has vmag = 28 +c m/s, then between those 2 points you have the thresholds +c for gale force AND storm force winds, so to be safe, we +c actually need to add 1 to ipoint and re-check the current +c point, if the wind value at that point is found to be +c greater than a wind threshold value (which it has if you've +c gotten to this point in threshloop). + + ipoint = ipoint + 1 + + iwindix = iwindix + 1 + + endif + + enddo threshloop + + deallocate (dtemp,stat=idta) + deallocate (isortix,stat=iisa) + deallocate (iwork,stat=iwa) + if (idta /= 0 .or. iisa /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating isortix or' + print *,'!!! dtemp or work for quadrant= ',k + print *,'!!! idta= ',idta,' iisa= ',iisa,' iwa= ',iwa + endif + + itret = 94 + return + endif + + enddo quadrantloop + + deallocate (quadinfo,stat=iqa) + if (iqa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating quadinfo array.' + print *,'!!! iqa= ',iqa + endif + + itret = 94 + return + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_max_wind (xcenlon,xcenlat,imax,jmax,dx,dy + & ,valid_pt,levsfc,vmax,trkrinfo,rmax,igmwret) +c +c ABSTRACT: This subroutine looks for the maximum near-surface wind +c near the storm center. Because different fcst Centers give us +c different parms, we will look at: 10m winds for GFS, MRF, GDAS, +c NGM and NAM; 10 m winds for NOGAPS; and surface winds for UKMET. +c ECMWF does not send us any near-surface wind parameters. By the +c way, this subroutine is only concerned with the value of the max +c wind, NOT where it's located radially with respect to the center. +c The value that's returned in vmax is the max wind speed in m/s, +c which are the units the data are stored in. However, when the +c max wind values are output in output_atcf, they will be +c converted from m/s to knots. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c levsfc integer holding the value of the array member that holds +c the near-surface winds in the u and v arrays (at orig +c writing, it's = 4). +c +c OUTPUT: +c +c vmax value of maximum near-surface wind near the storm ctr +c rmax radius of max winds +c igmwret return code from this subroutine +c +c LOCAL: +c +c radmaxwind the maximum radius to look for a max wind near the +c storm center. You have to allow this to be bigger for +c model grids with coarse resolution (ECMWF 2.5 degree). + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real radmaxwind,degrees,dx,dy,rmax + logical(1) valid_pt(imax,jmax) +c + igmwret = 0 + rmax = -99.0 + + if ((dx+dy)/2. <= 1.25) then + if ((dx+dy)/2. <= 0.25) then + radmaxwind = 300.0 + else + radmaxwind = 300.0 + endif + else + radmaxwind = 500.0 + endif + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmaxwind is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmaxwind/(dtk*dx))/cosfac) + numjpts = ceiling(radmaxwind/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_max_wind calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Value of vmax will be set to 0 for this time.' + endif + + vmax = 0.0 + igmwret = 99 + return + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + + print *,' ' + print *,'In get_max_wind, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + + vmax = 0.0 + do j=jbeg,jend + do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point = ',i + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + + if (dist > radmaxwind) cycle + + if (valid_pt(ip,j)) then + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + if (vmag > vmax) then + vmax = vmag + rmax = dist * 0.539638 ! convert from km to nm + endif + endif + + enddo + enddo + + if ( verb .ge. 3 ) then + print *,'At end of get_max_wind, vmax= ',vmax,' rmax= ',rmax + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine fixcenter (clon,clat,ist,ifh,calcparm,geslon,geslat + & ,inp,stderr,fixlon,fixlat,xvalues,maxstorm,ifret) +c +c ABSTRACT: This subroutine loops through the different parameters +c for the input storm number (ist) and calculates the +c center position of the storm by taking an average of +c the center positions obtained for those parameters. +c First we check to see which parameters are within a +c max error range (errmax), and we discard those that are +c not within that range. Of the remaining parms, we get +c a mean position, and then we re-calculate the position +c by giving more weight to those estimates that are closer +c to this mean first-guess position estimate. +c +c INPUT: +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c geslon Initial guess longitude for this storm at this fcst hour +c geslat Initial guess latitude for this storm at this fcst hour +c inp contains the input date and model number information +c xvalues The actual max or min data values for each parameter +c maxstorm max # of storms to be handled in this run +c +c INPUT/OUTPUT: +c stderr Standard deviation of the position "error" of the parms +c relative to the guess storm position. As long as the +c distance of a parm center to the guess center is <= +c errpmax, it is included in the std dev calculation. +c +c OUTPUT: +c fixlon Best approximation of storm center's longitude +c fixlat Best approximation of storm center's latitude +c ifret Return code from this subroutine +c +c LOCAL: +c storm Contains tcvitals info for the storms (def_vitals) +c trkerr_avg Sum/avg of the track errors for all parms for this +c fcst hour, regardless of whether or not the error was +c > errmax. It's used for getting the std deviation of +c the position error for this forecast time, to be used +c as part of the errmax calculation for the next fcst +c time. +c iclose Number of parameters whose position estimates are +c found to be within a distance errmax of the guess pos +c wtpos The weight given to each position estimate. It's +c based on the distance from the average position. +c errdist The "error" of the parameter center position relative +c to the storm's guess position. +c avgerr Average "error" of the parameter center positions +c relative to the storm's guess position. +c use4next Logical; If a parm center has been calculated but its +c distance from the guess position is > errmax, we don't +c use this center in calculating the new guess position, +c however we will use this position in calculating the +c standard deviation of the current time's guess +c positions, to be used in calculating the new errmax +c for the next forecast time. So in this subroutine, +c calcparm may be set to FALSE if errdist > errmax, but +c use4next will not be set to FALSE (Actually, it is +c only set to FALSE if errdist > errpmax, which is +c defined in error_parms and is roughly 600km). +c stderr_close Standard deviation of position errors for parms that +c have center estimates that are within a distance +c errmax of the guess position. +c clon_fguess These are the first-guess mean position estimates, +c clat_fguess which are the means of the position estimates that +c are within a distance errmax. These first-guess mean +c positions will be refined by giving more weight to +c individual parameter estimates that are closer to +c this first-guess mean position. +c dist_from_mean Contains the "error" distance of each parameter +c from the first-guess mean position (clon_fguess, +c clat_fguess). NOTE: If a parameter is not within +c a distance errmax of the guess position for this +c time (geslon,geslat), then there will be NO +c dist_from_mean calculated for that parm. +c + USE error_parms; USE set_max_parms; USE inparms; USE def_vitals + USE atcf; USE gen_vitals; USE tracked_parms + USE verbose_output + + type (datecard) inp + + real clon(maxstorm,maxtime,maxtp),temp_clon(maxtp) + real clat(maxstorm,maxtime,maxtp),temp_clat(maxtp) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real trkerr(maxtp),errdist(maxtp),xvalues(maxtp) + real stderr(maxstorm,maxtime),devia(maxtp),wtpos(maxtp) + real dist_from_mean(maxtp) + real degrees,errtmp + integer gt345_ct,lt15_ct + logical(1) calcparm(maxtp,maxstorm),use4next(maxtp) + character charparm(11)*8,charmaxmin(11)*8 +c + data charparm /'zeta 850','zeta 700','vmag 850','NOT USED' + & ,'vmag 700','NOT USED',' gph 850',' gph 700',' MSLP' + & ,'vmag sfc','zeta sfc'/ + data charmaxmin /' Max ',' Max ',' Min ','NOT USED' + & ,' Min ','NOT USED',' Min ',' Min ',' Min ' + & ,' Min ',' Max '/ +c + ifret=0 +c +c We need to judge whether each parameter position is reasonable, +c so we'll check to make sure that the dist from each parameter's +c estimate to the guess position is less than a maximum allowable +c error. If it's the first forecast time, use the initial error max +c (defined as errinit in error_parms) as errmax. Otherwise, the +c max error criterion is that the distance error must not exceed 3 +c times the previous forecast time's standard deviation (after a +c small growth factor has been applied). +c UPDATE 3/5/98: During testing, it was found that just using the +c previous time's stdev made errmax too "jumpy" (i.e., at vt=48h, +c errmax could = 380, and then at vt=54h, errmax could jump down +c to 190, so we've changed it so that it uses an average of the +c stdev's from the 3 previous forecast times to maintain some +c continuity between successive forecast times). +c + if (ifh == 1) then + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR' ) then + errmax = err_gfs_init + errinit = err_gfs_init + else if (atcfname == 'EMX ') then + errmax = err_ecm_max + errinit = err_ecm_max + else + errmax = err_reg_init + errinit = err_reg_init + endif + else + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR') then + errinit = err_gfs_init + else if (atcfname == 'EMX ') then + errinit = err_ecm_max + else + errinit = err_reg_max + endif + + if (ifh >= 4) then + xavg_stderr = (stderr(ist,ifh-3) + stderr(ist,ifh-2) + & + stderr(ist,ifh-1)) / 3.0 + else if (ifh == 3) then + xavg_stderr = (stderr(ist,ifh-2) + stderr(ist,ifh-1)) / 2.0 + else if (ifh == 2) then + xavg_stderr = stderr(ist,ifh-1) + endif + +c Following errmax statement replaced by the ensuing 4 lines +c due to a compiler bug on some other platforms.... +c errmax = amin1(amax1(3.0*xavg_stderr*errpgro,errinit) +c & ,errpmax) + + errtmp = 3.0*xavg_stderr*errpgro + errmax = max(errtmp,errinit) + errtmp = errpmax + errmax = min(errmax,errtmp) + + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (ifh > 1) then + print '(a42,f8.2,a15,f8.2)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = ' + & ,stderr(ist,ifh-1),' xavg_stderr= ',xavg_stderr + else + print '(a45,a18)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = N/A' + & ,' xavg_stderr= N/A' + endif + print *,'At beg of fixcenter, errpgro = ',errpgro + print *,'At beg of fixcenter, errinit = ',errinit + print *,'At beg of fixcenter, errpmax = ',errpmax + print *,'At beg of fixcenter, ifh= ',ifh,' errmax= ',errmax + endif + + trkerr_avg = 0.0 + iclose = 0; itot4next = 0 + clonsum = 0.0; clatsum = 0.0 + errdist = 0.0 + use4next = .FALSE. + gt345_ct = 0 + lt15_ct = 0 + +c For each parm, check to see if the estimated center is within +c distance errmax of the guess center. If it's within errmax, +c then use that parm for locating the center. If it's NOT +c within errmax, but IS within errpmax, then we still use this +c in calculating the standard deviation of the parameters for +c helping to determine the errmax for the next forecast hour. +c NOTE: For calculating the std dev to be used for the next +c forecast hour, do NOT use vmag 850, vmag 700 or vmag sfc, since +c those parms are always guaranteed to be within a short range of +c the guess, due to the nature of the algorithm (see subroutine +c get_uv_center for further details on that). + + do ip=1,maxtp + + if (ip == 4 .or. ip == 6) then ! Parms 4 & 6 not defined. + calcparm(ip,ist) = .FALSE. + cycle + endif + if (calcparm(ip,ist)) then + call calcdist (geslon,geslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + errdist(ip) = dist + if (dist <= errpmax) then + if (ip == 3 .or. ip == 5 .or. ip == 10) then + use4next(ip) = .FALSE. + else + use4next(ip) = .TRUE. + trkerr_avg = trkerr_avg + dist + itot4next = itot4next + 1 + endif + endif + if (dist <= errmax) then + iclose = iclose + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 + endif + clonsum = clonsum + clon(ist,ifh,ip) + clatsum = clatsum + clat(ist,ifh,ip) + else + calcparm(ip,ist) = .FALSE. + endif + endif + + enddo + + if (iclose > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15ct) to the sum of the lons (clonsum) + clon_fguess = (clonsum + (360.*float(lt15ct)))/ iclose + else + clon_fguess = clonsum / float(iclose) + endif + if (clon_fguess >= 360.0) then + clon_fguess = clon_fguess - 360. + endif + clat_fguess = clatsum / float(iclose) + endif + +c Print out a table listing of the locations of the fixes for +c the individual parameters. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'--------------------------------------------------' + write (6,95) 'Individual fixes follow..., fhr= ',ifhours(ifh) + & ,ifclockmins(ifh),' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + write (6,97) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,'Model name = ',atcfname + print *,'Values of -99.99 indicate that a fix was unable to be' + print *,'made for that paramater. Parameters 4 & 6 are not' + print *,'used. Vorticity data values are scaled by 1e5.' + print *,'errdist is the distance that the position estimate is' + print *,'from the guess position for this time. MSLP value ' + print *,'here may differ from that in the atcfunix file since ' + print *,'the one here is that derived from the area-averaged ' + print *,'barnes analysis, while that in the atcfunix file is ' + print *,'from a specific gridpoint.' + write (6,21) geslon,360.-geslon,geslat + write (6,*) ' ' + write (6,23) + write (6,25) + endif + + if (geslat > 0.0) then + charmaxmin(1) = ' Max ' + charmaxmin(2) = ' Max ' + charmaxmin(11) = ' Max ' + else + charmaxmin(1) = ' Min ' + charmaxmin(2) = ' Min ' + charmaxmin(11) = ' Min ' + endif + + do ip=1,maxtp + if (ip == 1 .or. ip == 2 .or. ip == 11) then + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + else + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + endif + enddo + + 21 format (' Guess location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 23 format (' parm# parm Max/Min Lon_fix(E) Lon_fix(W)' + & ,' Lat_fix Max/Min_value calcparm errdist(km)') + 25 format (' ----- ---- ------- ---------- ----------' + & ,' ------- ------------- -------- ----------') + 27 format (2x,i2,4x,a8,2x,a8,3x,f7.2,5x,f7.2,4x,f7.2,7x,f9.2 + & ,6x,L2,7x,f7.2) + 95 format (1x,a33,1x,i4,':',i2.2,a2,a4,a1,a9) + 97 format (' Gen ID (if available): ',i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3) + + +c If number of parameter centers close enough (iclose) > 0, then +c calculate the center by taking an average of all the parameter +c center positions that are within distance errmax from the guess +c position (geslon,geslat). Get a first-guess mean position, and +c then re-calculate the position estimate by giving more weight +c to those positions that are closer to the first-guess mean +c position. + + dist_from_mean = 0.0 + + if (iclose > 0) then + +c Get distances from first-guess mean position.... + + do ip=1,maxtp + if (calcparm(ip,ist)) then + call calcdist (clon_fguess,clat_fguess,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + dist_from_mean(ip) = dist + endif + enddo + +c Get the mean distance of each parameter estimate from +c the first-guess mean position + + call avgcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,iaret) + + if (iaret == 0) then + + call stdevcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,stderr_close,isret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After stdevcalc, xmn_dist_from_mean= ' + & ,xmn_dist_from_mean,' stderr_close= ' + & ,stderr_close,' isret= ',isret + endif + + endif + if (iaret /= 0 .or. isret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER -- Error occurred in either' + print *,'!!! avgcalc or stdevcalc. Storm number = ',ist + print *,'!!! RCC from avgcalc = ',iaret + print *,'!!! RCC from stdevcalc = ',isret + print *,'!!! Center fix will NOT be made, and processing' + print *,'!!! for this storm is ending. The probable cause' + print *,'!!! is that no calcparms were valid for this storm' + print *,'!!! at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + if (calcparm(1,ist) .or. calcparm(2,ist) .or. calcparm(7,ist) + & .or. calcparm(8,ist) .or. calcparm(9,ist) + & .or. calcparm(11,ist)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In fixcenter, STOPPING PROCESSING for this' + print *,'!!! storm. The reason is that none of the fix' + print *,'!!! locations for parms z850, z700, zeta 850,' + print *,'!!! zeta 700, MSLP or sfc zeta were within a ' + print *,'!!! reasonable distance of the guess location. As' + print *,'!!! such, no attempt will be made to fix the vmag' + print *,'!!! 850 or vmag 700 minima since, by the nature of' + print *,'!!! the algorithm for these 2 parms, a fix ' + print *,'!!! location WILL ALWAYS be returned that is ' + print *,'!!! within a reasonable distance of the center' + print *,'!!! guess position (the other 5 parameters may' + print *,'!!! or may not do so). So if the other 5 parms' + print *,'!!! insist that the guess is too far away, we ' + print *,'!!! do not want to grab a false center with ' + print *,'!!! the vmag minima.' + print *,'!!! ist= ',ist,' ifh= ',ifh + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'!!! Forecast hour: ',i4,':',i2.2) + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now re-calculate the mean position by giving more weight +c to those position estimates that are closer to the first +c guess mean position. Note that if stderr_close < 5.0, we +c force it to be 5.0; we do this to avoid getting very +c large numbers for devia values, which could make the +c weights (wtpos) equal to 0. This occurred during testing +c when only 2 parameters were valid, and so, of course, the +c standard deviation from the mean of those 2 parameters +c was close to 0, which gave devia values around 6000, and +c then wtpos values of 0, leading to a divide by 0 crash +c later on in subroutine wtavrg. + + kprm=0 + + if (stderr_close > 0.0) then + if (stderr_close < 5.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Since stderr_close had a value less than' + print *,'5, stderr_close has been forced to be equal' + print *,'to 5 in order to avoid dividing by zero later' + print *,'on in subroutine wtavrg.' + endif + + stderr_close = 5.0 + endif + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + devia(kprm) = dist_from_mean(ip) / stderr_close + wtpos(kprm) = exp(-devia(kprm)/3.) + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + + if ( verb .ge. 3 ) then + write (6,113) ip,kprm,dist_from_mean(ip),devia(kprm) + & ,wtpos(kprm),temp_clon(kprm) + & ,360.-temp_clon(kprm),temp_clat(kprm) + endif + + endif + enddo + 113 format (1x,'ip= ',i2,' kprm= ',i2,' dist_from_mean= ',f7.3 + & ,' devia= ',f7.3,' wtpos= ',f8.5,2x,3(2x,f7.2)) + else +c +c This next if statement is for the case in which only 1 +c parameter is valid, for which the stderr_close will = 0 +c (obviously), but as long as we have 1 valid parameter, +c continue processing, and set the weight for that parm = 1. +c The else portion is for the case in which stderr_close +c = 0 with NO parms being close. +c + if (iclose == 1) then + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + wtpos(kprm) = 1 + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + endif + enddo + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, stderr_close not > 0' + print *,'!!! stderr_close = ',stderr_close + print *,'!!! The probable cause is that no calcparms were' + print *,'!!! valid for this storm at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + endif +c + if (kprm > 0) then + call wtavrg_lon (temp_clon,wtpos,kprm,fixlon(ist,ifh),iwtret1) + call wtavrg (temp_clat,wtpos,kprm,fixlat(ist,ifh),iwtret2) + if (iwtret1 > 0 .or. iwtret2 > 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER in call to wtavrg.' + print *,'!!! Return Codes from wtavrg calls follow: ' + print *,'!!! RCC from wtavrg for long fix: ',iwtret1 + print *,'!!! RCC from wtavrg for lat fix: ',iwtret2 + print *,'!!! This means a divide by zero would have ' + print *,'!!! been attempted, which means that the ' + print *,'!!! weights in wtpos are not > 0. Check in' + print *,'!!! subroutine fixcenter where devia values' + print *,'!!! are calculated to see if something is ' + print *,'!!! wrong there. Values of wtpos array follow:' + print *,'!!! ',wtpos + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + print *,' ' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, kprm NOT > 0' + print *,'!!! This means that, for whatever reason, the ' + print *,'!!! calcparm logical flag was set to .FALSE. for' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: IN FIXCENTER, No storms are within errmax ' + print *,'!!! OR the calcparm logical flag was set to .FALSE. ' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now calculate the average error of all the parms that are within +c a radius errpmax (defined in error_parms, ~600km), and the std +c dev of those errors. This standard deviation will be used in +c calculating the maximum allowable error for the next forecast +c time. + + if (itot4next > 0 .and. ifret /= 95) then + trkerr_avg = trkerr_avg / float(itot4next) + call stdevcalc (errdist,maxtp,use4next,trkerr_avg + & ,stderr(ist,ifh),isret) + if (isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in FIXCENTER calculating std deviation ' + print *,'!!! for use in next forecast hours errmax.' + print *,'!!! ist= ',ist,' ifh= ',ifh,' itot4next= ' + & ,itot4next + endif + + ifret = 95 + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine avgcalc (xdat,kmax,valid,xavg,iaret) +c +c ABSTRACT: This subroutine just calculates a straight average of +c the parameters in the input array (xdat). The logical array +c (valid) indicates whether or not to include a particular array +c member or not in the calculation. + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) +c + iaret = 0 +c + xsum = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + xsum = xsum + xdat(i) + ict = ict + 1 + endif + enddo +c + if (ict > 0) then + xavg = xsum / float(ict) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in avgcalc, ict NOT > 0' + endif + + xavg = xdat(1) + iaret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg (xdat,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xdat) using the input weights +c in the input array (wt). It is used to calculate the center lat +c and lon fix positions. +c + USE verbose_output + + real xdat(kmax),wt(kmax) +c + iwtret = 0 +c + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xdat(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg, wtot NOT > 0' + endif + + iwtret = 95 + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg_lon (xlon,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xlon) using the input weights +c in the input array (wt). This subroutine is specifically used +c to find the center lon fix positions. It contains code to +c account for wrapping around the Greenwich Meridian. +c + + USE verbose_output + + real xlon(kmax),wt(kmax) + integer gt345_ct,lt15_ct +c + iwtret = 0 + gt345_ct = 0 + lt15_ct = 0 + +c First check to see if we have lons that are both to the left +c and the right of the greenwich meridian + + do i = 1,kmax + if (xlon(i) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (xlon(i) < 15.) then + lt15_ct = lt15_ct + 1 + endif + enddo + + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some lons that are in the 300's (west of the GM), and + ! some that are in the 0's (east of the GM). We need to + ! standardize these if we want to get a meaningful average. + do i = 1,kmax + if (xlon(i) < 15.) then + xlon(i) = xlon(i) + 360.0 + endif + enddo + endif + + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xlon(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg_lon, wtot NOT > 0' + endif + + iwtret = 95 + endif + + if (xwtavg >= 360.0) then + xwtavg = xwtavg - 360.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine stdevcalc (xdat,kmax,valid,xavg,stdx,isret) + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) + + isret = 0 + + stdx = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + stdx = stdx + (xdat(i) - xavg)**2 + ict = ict + 1 + endif + enddo + + if (ict > 0) then + stdx = sqrt(stdx/float(ict)) + if (stdx == 0.0) then +c This can happen if you have just 2 points; The mean position +c will be exactly in the middle of the 2 points and so the +c standard deviation around that mean point will be 0. And +c since the calling routine will quit if the returned standard +c deviation is 0, we must force it to be 1 so the program +c continues running. Theoretically, it could also happen with +c 3 or more points, but the likelihood of the distances working +c out to exactly equidistant for 3 points is not that good. + stdx = 1.0 + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in stdevcalc, ict NOT > 0' + endif + + isret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,xval,trkrinfo,igucret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the minimum in the wind speed near the storm center. This center +c fix is done differently than for the other parms. With this fix, +c we severely limit the area that is searched, because we do not +c want to confuse a wind minimum out on the periphery of a storm +c with the center wind minimum. Therefore, this subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the guess +c position for this time and the 5 other parm fixes. That modified +c guess position is passed into this subroutine as uvgeslon and +c uvgeslat, and that's where the searching for the wind minimum +c is done. To get the wind minimum, the u and v data are first +c interpolated down to a fine grid (see details below for exact +c figures), and then a single-pass barnes analysis is done on that +c fine grid. The reason that we first interpolate the data (which +c is different from how we do the other parms) is that if we just +c use the original grid resolution, we may not be able to +c accurately pick out a minimum in the wind field at the center. +c + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real, allocatable :: uold(:,:),vold(:,:),unew(:,:),vnew(:,:) + real, allocatable :: rlonold(:),rlatold(:),rlonnew(:),rlatnew(:) + real, allocatable :: vmag(:,:) + real :: dx,dy + real :: grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + character*1 :: gotlat + logical(1) cflag, valid_pt(imax,jmax) + logical(1), allocatable :: lbi(:,:) +c + gotlat = 'n' +c +c ----------------------------------------------------------------- +c INTERPOLATE INPUT GRID TO SMALLER GRID +c ----------------------------------------------------------------- +c +c Get beginning and ending j points (on the input grid) for a +c smaller array that surrounds the storm. It is this smaller array +c that we will interpolate to a finer grid. +c +c Calculate number of pts to either side of this j to search +c + npts = ceiling(rads_vmag/(dtk*((dx+dy)/2.))) +c + call get_ij_bounds (npts,0,ritrk_vmag,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij D, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij D, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center from call to ' + print *,'!!! get_ij_bounds, stopping processing for ' + print *,'!!! storm number ',ist + endif + + igucret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, and our gridtype is NOT' + print *,'!!! global, so we are going to redefine ibeg to 1.' + print *,' ' + endif + + ibeg = 1 + endif + endif + + if (jbeg < 1) jbeg = 1 + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center calculating ibeg, iend, jbeg' + print *,'or jend. ibeg= ',ibeg,' iend= ',iend,' jbeg= ',jbeg + print *,'jend= ',jend + print *,'uv center will not be calculated for this time.' + endif + + igrret = 99 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, and our gridtype is' + print *,'!!! NOT global, so we will redefine iend to imax.' + print *,' ' + endif + + iend = imax + endif + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif +c + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the grid sizes for +c some of the typical grids that will be used: +c +c Original grid size # of interps Final grid size +c -------------------- ------------ --------------------- +c 1.00 deg (111.19 km) 3 0.125 deg (13.9 km) +c 1.25 deg (138.99 km) 3 0.156 deg (17.4 km) +c 2.50 deg (277.99 km) 4 0.156 deg (17.4 km) + + if ((dx+dy)/2. > 1.2) then + numinterp = 4 + else if ((dx+dy)/2. > 0.50 .and. (dx+dy)/2. <= 1.2) then + numinterp = 3 + else if ((dx+dy)/2. > 0.25 .and. (dx+dy)/2. <= 0.50) then +ctpm6/14 numinterp = 2 + numinterp = 3 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.25) then +ctpm6/14 numinterp = 1 + numinterp = 3 + else if ((dx+dy)/2. <= 0.10) then + numinterp = 0 + endif + + dell = (dx+dy)/2. + imxold = iend - ibeg + 1 + jmxold = jend - jbeg + 1 + +c -------------------------------------------------------------- +c Before interpolating, make sure that all the original +c points have valid data. If they don't then exit the +c subroutine. NOTE: This is NOT checking to see if ALL the pts +c on the complete & full input grid have valid data; it only +c checks those points that are within the box returned from +c get_ij_bounds. + + do i=ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_uv_center, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! PROCESSING WILL STOP. ' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + stop 94 + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_uv_center' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + do j=jbeg,jend + if (.not. valid_pt(ip,j)) goto 975 + enddo + + enddo + +c ------------------------------------ +c Now begin the interpolation process + + allocate (uold(imxold,jmxold),stat=iuo) + allocate (vold(imxold,jmxold),stat=ivo) + allocate (rlonold(imxold),stat=iloo) + allocate (rlatold(jmxold),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. iloo /= 0 .or. ilao /= 0) goto 970 + + do intnum = 1,numinterp + + if (intnum == 1) then + + do i=ibeg,iend + + ik = i + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ik = i + imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i < 1' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i + endif + + igucret = 92 + return + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ik = i - imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i > imax' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i,' imax= ',imax + endif + + igucret = 92 + return + endif + endif + + rlonold(i-ibeg+1) = glon(ik) + do j=jbeg,jend + uold(i-ibeg+1,j-jbeg+1) = u(ik,j,nlev) + vold(i-ibeg+1,j-jbeg+1) = v(ik,j,nlev) + if (gotlat == 'n') then + rlatold(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatold once + enddo + + else + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate (rlatold) + allocate (uold(imxnew,jmxnew),stat=iuo) + allocate (vold(imxnew,jmxnew),stat=ivo) + allocate (rlonold(imxnew),stat=iloo) + allocate (rlatold(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 970 + + gotlat = 'n' + do i=1,imxnew + rlonold(i) = rlonnew(i) + do j=1,jmxnew + uold(i,j) = unew(i,j) + vold(i,j) = vnew(i,j) + if (gotlat == 'n') then + rlatold(j) = rlatnew(j) + endif + enddo + gotlat = 'y' + enddo + + imxold = imxnew + jmxold = jmxnew + deallocate (unew); deallocate (vnew) + deallocate (rlonnew); deallocate (rlatnew) + + endif + + dell = 0.5 * dell + imxnew = 2 * imxold - 1 + jmxnew = 2 * jmxold - 1 + + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + + call bilin_int_even (imxold,jmxold,uold + & ,imxnew,jmxnew,unew,ibiret) + call bilin_int_even (imxold,jmxold,vold + & ,imxnew,jmxnew,vnew,ibiret) +c call lin_int (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int_lon (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int (jmxold,jmxnew,rlatold,rlatnew,iliret) + + chk_lonspc_old = rlonold(imxold) - rlonold(imxold - 1) + chk_latspc_old = rlatold(jmxold) - rlatold(jmxold - 1) + chk_lonspc_new = rlonnew(imxnew) - rlonnew(imxnew - 1) + chk_latspc_new = rlatnew(jmxnew) - rlatnew(jmxnew - 1) + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, intnum= ',intnum + print *,'imxold= ',imxold,' imxnew= ',imxnew + print *,'jmxold= ',jmxold,' jmxnew= ',jmxnew + print *,'Grid boundaries of modified uv grid: ' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ' + & ,grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ' + & ,grid_minlon + endif + + enddo + +c ------------------ + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate(rlatold) + + if (numinterp == 0) then + + ! No interpolations were done for this fine mesh grid, but we + ! need to fill some of these arrays and define variables for + ! subsequent subroutine calls just below here that require + ! the variables imxnew, jmxnew, and the arrays unew and vnew. + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional ' + print *,'grid; iend should not > imax here !!!' + endif + + igucret = 99 + return + endif + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional' + print *,'grid; ibeg should not < 1 here !!!' + endif + + igucret = 99 + return + endif + endif + + imxnew = iend - ibeg + 1 + jmxnew = jend - jbeg + 1 + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + gotlat = 'n' + + do i=ibeg,iend + + ip = i + + if (i > imax) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i - imax ! Wrapping past GM + endif + + if (i < 1) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i + imax ! Wrapping past GM + endif + + rlonnew(i-ibeg+1) = glon(ip) + do j=jbeg,jend + unew(i-ibeg+1,j-jbeg+1) = u(i,j,nlev) + vnew(i-ibeg+1,j-jbeg+1) = v(i,j,nlev) + if (gotlat == 'n') then + rlatnew(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatnew once + enddo + + endif + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,'Grid boundaries of modified uv grid in get_uv_center:' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ',grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ',grid_minlon + endif + + allocate (vmag(imxnew,jmxnew),stat=ivm) + allocate (lbi(imxnew,jmxnew),stat=ilb) + if (ivm /= 0 .or. ilb /= 0) goto 972 + call calc_vmag (unew,vnew,imxnew,jmxnew,vmag,icvret) + deallocate (unew); deallocate (vnew) + + lbi = .TRUE. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to find_maxmin, imxnew= ',imxnew + & ,'jmxnew= ',jmxnew,' ist= ',ist + write (6,171) dell,uvgeslon,360.-uvgeslon,uvgeslat + 171 format (' dell= ',f7.3,' uvgeslon= ',f8.3,'E (',f8.3,'W)' + & ,' uvgeslat= ',f8.3) + endif + +c Note that in the next call, I pass the 'global' argument to +c find_maxmin. This defines what type of grid it is, so that the +c proper grid_buffer can be chosen. This grid_buffer is designed +c to avoid having a center be chosen too close to the grid +c boundary. However, in the case of vmag here, we are only using +c a small subgrid, and we want to make sure we use *all* points +c in that subgrid for searching, and that will occur if we set that +c calling argument to 'global' as opposed to 'regional'. + + call find_maxmin (imxnew,jmxnew,dell,dell,'vmag' + & ,vmag,'min',ist,uvgeslon,uvgeslat,rlonnew,rlatnew,lbi + & ,trkrinfo,cflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,'global',ifmret) + deallocate (vmag); deallocate (lbi) + deallocate (rlonnew); deallocate (rlatnew) +c + if (ifmret == 0) then + goto 995 + else + igucret = ifmret + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center in call to find_maxmin' + print *,'!!! storm num = ',ist,' igucret = ',igucret + endif + + goto 998 + endif +c + 970 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either uold, vold,' + print *,'!!! rlonold or rlatold in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 971 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either unew, vnew,' + print *,'!!! rlonnew or rlatnew in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 972 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either vmag or lbi in ' + print *,'!!! subroutine get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! ivm= ',ivm,' ilb= ',ilb + endif + + igucret = 97 + goto 998 + + 975 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Inside get_uv_center, at least one of the points' + print *,'!!! is not a valid data point. This point may be ' + print *,'!!! outside the valid data bounds of a regional grid' + print *,'!!! i= ',i,' j= ',j + print *,'!!! Storm number = ',ist + endif + + igucret = 98 + goto 998 +c + 995 continue + igucret = 0 +c + 998 continue + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_guess (guesslon,guesslat,clon,clat + & ,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) +c +c ABSTRACT: The purpose of this subroutine is to get a modified +c first guess lat/lon position before searching for the +c minimum in the wind field. The reason for doing this is +c to better refine the guess and avoid picking up a wind +c wind minimum far away from the center. So, use the +c first guess position (and give it strong weighting), and +c then also use the fix positions for the current time +c (give the vorticity centers stronger weighting as well), +c and then take the average of these positions. +c +c INPUT: +c guesslon guess longitude for this forecast time +c guesslat guess latitude for this forecast time +c clon array with center longitude fixes for the various parms +c clat array with center latitude fixes for the various parms +c calcparm logical; tells whether or not a parm has a valid fix +c at this forecast hour +c ist index for current storm +c ifh index for current forecast hour +c maxstorm max # of storms that can be handled +c +c OUTPUT: +c uvgeslon contains modified guess longitude position at which to +c look for the wind minimum +c uvgeslat contains modified guess latitude position at which to +c look for the wind minimum +c igugret return code for this subroutine (0=normal) +c---- +c + USE set_max_parms; USE level_parms; USE error_parms + USE verbose_output + + logical(1) calcparm(maxtp,maxstorm) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real uvgeslon, uvgeslat + real guesslon,guesslat,degrees + integer gt345_ct,lt15_ct + + sumlon = 0.0 + sumlat = 0.0 + ict = 0 + gt345_ct = 0 + lt15_ct = 0 + +c NOTE: We need to be careful in this routine when averaging +c the longitudes together, in case we cross the greenwich +c meridian, because then we may be averaging 345+ lons with +c lons that are less than 15, giving incorrect results. +c Therefore, check for this, and if it occurs, add 360 onto +c any of the <15 lons (add it twice for those lons being +c counted twice (guesslon and the vorticity centers)). + +c Weight the uv guess position by counting the storm's guess +c position twice. + + sumlon = sumlon + 2.*guesslon + sumlat = sumlat + 2.*guesslat + ict = ict + 2 + + if (guesslon > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (guesslon < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct.... + endif + + do ip = 1,maxtp + if ((ip > 2 .and. ip < 7) .or. ip == 10) then + cycle ! because 3-6 are for 850 & 700 u & v and 10 is + ! for surface wind magnitude. + else + if (calcparm(ip,ist)) then + call calcdist (guesslon,guesslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + + if (dist < uverrmax) then +c +c Give the vorticity centers 2x weighting as well +c + if (ip == 1 .or. ip == 2 .or. ip == 11) then + sumlon = sumlon + 2.*clon(ist,ifh,ip) + sumlat = sumlat + 2.*clat(ist,ifh,ip) + ict = ict + 2 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct... + endif + else + sumlon = sumlon + clon(ist,ifh,ip) + sumlat = sumlat + clat(ist,ifh,ip) + ict = ict + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 ! Only 1 for non-zeta parms + endif + endif + + endif + + endif + endif + enddo +c + if (ict > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15ct) to the sum of the lons (sumlon) + uvgeslon = (sumlon + (360.*float(lt15ct)))/ ict + else + uvgeslon = sumlon / ict + endif + if (uvgeslon >= 360.0) then + uvgeslon = uvgeslon - 360. + endif + uvgeslat = sumlat / ict + igugret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_guess, ict not > 0, ict= ',ict + print *,'!!! vmag center will not be calculated for this' + print *,'!!! storm -- at least not at this level' + print *,'!!! Storm number = ',ist + endif + + igugret = 91 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calc_vmag (xu,xv,imx,jmx,wspeed,icvret) +c +c ABSTRACT: This subroutine calculates the magnitude of the wind +c speed for an array of points, given real u and real v arrays. +c + real xu(imx,jmx),xv(imx,jmx),wspeed(imx,jmx) +c + do i=1,imx + do j=1,jmx + wspeed(i,j) = sqrt( xu(i,j)*xu(i,j) + xv(i,j)*xv(i,j) ) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_even (imxold,jmxold,xold + & ,imxnew,jmxnew,xnew,ibiret) +c +c ABSTRACT: This subroutine does a bilinear interpolation on a +c grid of evenly spaced data. Do NOT attempt to use this subroutine +c with data that are not evenly spaced or you will get unpredictable +c results. +c + real xold(imxold,jmxold), xnew(imxnew,jmxnew) +c +c +c --------------------------------------------------------------------- +c Latitude ----> | +c | +c L O e O e O e O e O | O: original point from input array +c o | +c n e 1 2 1 2 1 2 1 e | 1: interpolated, primary inter. pt +c g | +c i O 2 O 2 O 2 O 2 O | e: interpolated edge point +c t | +c u e 1 2 1 2 1 2 1 e | 2: interpolated, secondary inter. pt +c d | +c e O 2 O 2 O 2 O 2 O | Interpolations are done in the order +c | as indicated above; First, the input +c | e 1 2 1 2 1 2 1 e | 'O' pts are placed onto the new, +c | | larger grid. From that, the '1' pts +c | O 2 O 2 O 2 O 2 O | can be interpolated. Next, the edge +c | | (e) pts are interpolated using an +c v e 1 2 1 2 1 2 1 e | interpolation of two 'O' pts and one +c | '1' pt. Finally, the '2' pts are +c O e O e O e O e O | done using the 2 surrounding '0' and +c | '1' pts. Bilinear interpolation is +c | made incredibly easier by the fact +c | that the grid is evenly spaced. +c --------------------------------------------------------------------- +c NOTE: Remember that the arrays that are read in are indexed as +c (lon,lat), so that in the diagram above, pt (1,1) is at the upper +c left and pt (imax,jmax) is at the lower right, and each column is +c a new latitude and each row is a new longitude. +c +c ----------------------------------------------------------------- +c Put original (O) values from input array into new, expanded array +c ----------------------------------------------------------------- +c + do i=1,imxold + do j=1,jmxold + xnew(2*i-1,2*j-1) = xold(i,j) + enddo + enddo +c +c ---------------------------------------------- +c Interpolate to get primary interior (1) points +c ---------------------------------------------- +c + do i=1,imxold-1 + do j=1,jmxold-1 + xnew(2*i,2*j) = 0.25 * (xnew(2*i-1,2*j-1) + xnew(2*i+1,2*j-1) + & + xnew(2*i+1,2*j+1) + xnew(2*i-1,2*j+1)) + enddo + enddo +c +c --------------------------- +c Interpolate edge (e) points +c --------------------------- +c +c ... Northernmost 'e' points ... +c + j=1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,2)) + enddo +c +c ... Southernmost 'e' points ... +c + j = 2*jmxold - 1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,j-1)) + enddo +c +c ... Westernmost 'e' points ... +c + i=1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(2,2*j)) + enddo +c +c ... Easternmost 'e' points ... +c + i = 2*imxold - 1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(i-1,2*j)) + enddo +c +c ------------------------------------------------ +c Interpolate to get secondary interior (2) points +c ------------------------------------------------ +c + do j=2,2*jmxold-2 + istep = mod(j+1,2) + do i=istep+2,2*imxold-2,2 + xnew(i,j) = 0.25 * (xnew(i-1,j) + xnew(i,j-1) + xnew(i+1,j) + & + xnew(i,j+1)) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points +c + do i=1,ioldmax-1 + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int_lon (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. This particular +c routine is specifically used for interpolating +c longitudes, and it factors in the possibility of +c interpolating across the greenwich meridian. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points, and make the +c necessary adjustment when interpolating a longitude between, +c for example, 359.5 and 0.0. +c + do i=1,ioldmax-1 + if (xnew(2*i-1) > 350. .and. xnew(2*i+1) < 10.) then + xnew(2*i) = 0.5 * (xnew(2*i-1) + (360. + xnew(2*i+1))) + else + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + endif + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) +c +c ABSTRACT: This subroutine finds the maximum and mean zeta values +c at 850 & 700 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms + USE trkrparms; USE level_parms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cJ.Peng----2014-10-01------------------------------ + logical(1) readflag(16),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanzeta,dx,dy,re,ri,parmlon,parmlat + integer igridzeta(nlevm1),imeanzeta(nlevm1) + integer n,ix1,ix2,ilev,npts,imax,jmax,igzvret,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_zeta_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_zeta_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max zeta values at 850 and 700 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_zeta_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_zeta_loop: do n=1,2 + + gridpoint_maxmin = -99.0 + xmeanzeta = -99.0 + compflag = .true. + + select case (n) + case (1); ilev=850 ! For 850 mb + case (2); ilev=700 ! For 700 mb + end select + + if (zeta(ilonfix,jlatfix,n) > -9990.0) then + + ! ------------------------------------------- + ! We have valid zeta data for this level, so + ! we first call barnes now to get the mean zeta + ! surrounding our found center position. + ! ------------------------------------------- + + if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,n),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanzeta + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds + imeanzeta(n) = int ((xmeanzeta * 1e6) + 0.5) + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_zeta_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for zeta values will not be done.') + exit report_zeta_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + exit report_zeta_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) n,ilev,xmeanzeta,imeanzeta(n) + 621 format (1x,'+++ RPT_MEAN_ZETA: n= ',i2,' lev= ',i4 + & ,' xmeanzeta= ',f9.6,' imeanzeta= ',i4) + write (6,*) ' --- mean zeta raw = ',xmeanzeta + endif + + ! ----------------------------------------------- + ! Call fix_latlon_to_ij to get the nearest actual + ! raw (grid) zeta data value, not the mean value. + ! ----------------------------------------------- + + call fix_latlon_to_ij (imax,jmax,dx,dy + & ,zeta(1,1,n),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanzeta,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + igridzeta(n) = int ((gridpoint_maxmin * 1e6) + 0.5) + else + igridzeta(n) = -99 + endif + + if ( verb .ge. 3 ) then + write (6,623) n,ilev,gridpoint_maxmin,igridzeta(n),ifilret + 623 format (1x,'+++ RPT_GRID_ZETA: n= ',i2,' lev= ',i4 + & ,' grid zeta= ',f9.6,' igrid zeta= ',i4,' ifilret= ',i3) + write (6,*) ' --- grid zeta raw= ',gridpoint_maxmin + endif + + enddo report_zeta_loop + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get 850 & 700 zeta for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine find_maxmin (imax,jmax,dx,dy,cparm,fxy,maxmin,ist + & ,guesslon,guesslat,rlonv,rlatv,valid_pt,trkrinfo + & ,compflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,cmodel_type,ifmret) +c +c This routine finds the location (clon,clat) of and value of the +c the max or min of fxy in the vicinity of slon,slat. The value of +c the input flag maxmin determines whether to look for a max or a +c min value. The max/min is determined by finding the point which +c gives the max/min value of a single point barnes analysis of fxy +c with e-folding radius re (km) and influence radius ri (km). The +c initial search is restricted to a radius rads around the point +c (slon,slat) on a grid with lon,lat spacing dx and dy. The location +c is refined by reducing the spacing of the search grid by a factor +c of two, nhalf times. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c maxmin Char string indicating whether to search for a max or min +c ist Number of the storm being processed +c guesslon Guess longitude of the storm +c guesslat Guess latitude of the storm +c rlonv Array containing longitude values of input grid points +c rlatv Array containing latitude values of input grid points +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c trkrinfo derived type detailing user-specified grid info +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c +c INPUT/OUTPUT: +c compflag Logical; continue processing this storm or not (would be +c set to FALSE if, for example, the guess position is +c outside the domain of a regional grid) +c +c OUTPUT: +c ctlon Center longitude of storm found for this parameter +c ctlat Center latitude of storm found for this parameter +c xval Max or Min value found at the (ctlon,ctlat) +c ifmret Return code from this subroutine +c +c UPDATE DEC 2009: For the HFIP HRH testing, it was found that +c due to the very limited domain size of some of the models, the +c barnes scheme was allowing points close to the grid boundaries +c to erroneously be selected as the center point. We add in a +c buffer (grid_buffer) here to prevent this from occurring. + + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + character(*) maxmin,cparm,cmodel_type + logical(1) compflag, valid_pt(imax,jmax) + real fxy(imax,jmax),rlonv(imax),rlatv(jmax) + real ctlon,ctlat,degrees,dx,dy,guesslon,guesslat,xval + real rads,re,ri,dell,fmax,fmin,rlatt,rlont,dist,ftemp,ritmp + real vmag_latmax,vmag_latmin,vmag_lonmax,vmag_lonmin,retmp + real tlon,tlat,grid_buffer,temp_grid_minlon,temp_guesslon + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + integer imax,jmax,ist,bskip1,bskip2,iskip,ifmret,npts,maxvgrid + integer ibeg,iend,jbeg,jend,ilonfix,jlatfix,igiret,icount,iret + integer ibct,ibarnes_loopct,i,j,k,iix,jix,jvlatfix,ivlonfix + integer nhalf,icvpret + integer date_time(8) + character (len=10) big_ben(3) +c + ifmret = 0 + nhalf = 5 +c +c ----------------------------------------------------------- +c Set initial parms for use in find_maxmin. +c Different radii used for V magnitude than for other parms, +c see discussion in module radii for more details. +c + if (cparm == 'vmag') then + +c NOTE: The maxvgrid variable determines what size grid to send +c to subroutine barnes. e.g., maxvgrid = 8 means send an +c 8x8 grid; maxvgrid = 12 means send a 12x12 grid. For +c ultra-fine mesh grids (finer than 0.04 deg, or 1/25 deg), +c we expand to 12 in order to sample a few more points +c around each grid point. + + if ((dx+dy)/2. > 0.04) then + maxvgrid = 8 + else + maxvgrid = 12 + endif + + rads = rads_vmag; re = retrk_vmag; ri = ritrk_vmag + re = (float(maxvgrid)/4) * ((dx+dy)/2. * dtk) ! Basically, this +c sets re equal to half the distance from the gridpoint +c in question to the farthest point that will be +c sampled when the (maxvgrid x maxvgrid) grid is passed +c on to subroutine barnes. Thus, just ignore the +c parameter retrk_vmag, and use this instead. + else if ((dx+dy)/2. < 1.26 .and. (dx+dy)/2. >= 0.40) then + rads = rads_most; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.40 .and. (dx+dy)/2. >= 0.10) then +ctpm6/14 rads = rads_fine; re = retrk_most; ri = ritrk_most + rads = rads_most; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.10) then + rads = rads_hres; re = retrk_hres; ri = ritrk_most + else + rads = rads_coarse; re = retrk_coarse; ri = ritrk_coarse + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of find_maxmin, rads= ',rads,' re= ',re + & ,' ri= ',ri,' cparm= ',cparm,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+15; fmin = 1.0e+15 + ctlon = 0.0; ctlat = 0.0 + + if (npts == 0) npts = 1 + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if (dell > 0.20) then +ctpm6/14 bskip1 = 2 + bskip1 = 1 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 4 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 6 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 10 + bskip2 = 5 + else if (dell <= 0.03) then + bskip1 = 15 + bskip2 = 5 + endif + + if (cparm == 'vmag') then + bskip1 = 1 + bskip2 = 1 + endif + +c If input parm is vmag, we've already done the minimizing by +c interpolating to the fine mesh grid, so we'll simply send the +c bounds that were input to this subroutine to barnes +c as boundaries for the array to search. For all other parms, +c however, no minimizing has been done yet, so we need to call +c get_ij_bounds to set the boundaries for a much smaller grid that +c surrounds the storm (as opposed to having subroutine barnes +c search the entire global grid). + + if (cparm == 'vmag') then + + if ( verb .ge. 3 ) then + print *,'In find_maxmin, jmax= ',jmax,' imax= ',imax + endif + + ibeg=1; jbeg=1; iend=imax; jend=jmax + vmag_latmax = rlatv(1) ! N-most lat of vmag subgrid + vmag_latmin = rlatv(jmax) ! S-most lat of vmag subgrid + vmag_lonmin = rlonv(1) ! W-most lon of vmag subgrid + vmag_lonmax = rlonv(imax) ! E-most lon of vmag subgrid + + if ( verb .ge. 3 ) then + write (6,44) vmag_latmax,vmag_lonmin,360.-vmag_lonmin + & ,imax,jmax + write (6,46) vmag_latmin,vmag_lonmax,360.-vmag_lonmax + endif + + 44 format (' vmag_latmax= ',f8.3,' vmag_lonmin= ',f8.3 + & ,'E (',f8.3,'W) imax= ',i4,' jmax= ',i4) + 46 format (' vmag_latmin= ',f8.3,' vmag_lonmax= ',f8.3 + & ,'E (',f8.3,'W)') + + if (vmag_lonmin > 330. .and. vmag_lonmax < 30.) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: For a case of find_maxmin, our vmag' + print *,'!!! subgrid is straddling the GM. The code should' + print *,'!!! be able to handle this, but if strange errors' + print *,'!!! are occurring, check into the code either here' + print *,'!!! in find_maxmin or get_uv_ctr.' + print *,' ' + endif + endif + + npts = ceiling(rads/(dtk*dell)) + + else + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,guesslon,guesslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to ' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + ifmret = 92 + return + endif + + endif + +c +c --------------------------------------------------------------- +c + if ( verb .ge. 3 ) then + print *,' ' + write (6,39) guesslon,360.-guesslon,guesslat + 39 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + if (cparm == 'vmag') then + print *,'ilonfix= (unused) jlatfix= (unused)' + & ,' npts= ',npts + print *,'ilonfix and jlatfix are meaningless for computing' + print *,'vmag, so ignore the large values you see for them.' + else + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + endif + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + + if ( verb .ge. 3 ) then + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: find_maxmin 1 ',i2.2,':',i2.2,':',i2.2) + endif + + ibct=0 + ibarnes_loopct = 0 + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (guesslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = guesslon - 360. + else + temp_guesslon = guesslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = guesslon + endif + + jix = 0 + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + jloop: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = guesslat + dell*float(j) + + iix = 0 + +c vlat(jix) = rlatt + + iloop: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c if (cparm == 'vmag') then +c print *,' ' +c print '(a16,i6,a4,i6,2(a8,f8.3),a12,f8.3)' +c & ,'in find_max, i= ',i +c & ,' j= ',j,' rlatt= ',rlatt,' rlont= ',rlont +c & ,' 360-rlont= ',360.-rlont +c endif + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT: icvpret= ',icvpret + endif + + cycle iloop + endif + + call calcdist(rlont,rlatt,temp_guesslon,guesslat,dist,degrees) + if (dist .gt. rads) cycle iloop + + if (cparm == 'vmag') then + +c This next bit of code gets the ij coordinates for an 8x8 +c box around the current point under consideration. These ij +c coordinates are sent to barnes so that barnes only loops +c 64 times, as opposed to nearly 10,000 if the whole 97x97 +c array were sent. So, fix rlatt to the grid point just +c northward of rlatt and fix rlont to the grid point just +c eastward of rlont. Note that this makes for a modified +c barnes analysis in that we're sort of specifying ahead of +c time exactly which grid points will be included and we'll +c be excluding some points that would be near the periphery +c of each (rlont,rlatt)'s range, but as long as we're consis- +c tent and do it this way for each point, it's well worth the +c trade-off in cpu time. Parameter maxvgrid determines what +c size array to send to barnes (maxvgrid=8 means 8x8) + + jvlatfix = int((vmag_latmax - rlatt)/dy + 1.) + ivlonfix = int((rlont - temp_grid_minlon)/dx + 2.) +c ivlonfix = int((rlont - vmag_lonmin)/dx + 2.) + + ibeg = ivlonfix - (maxvgrid/2) + iend = ivlonfix + (maxvgrid/2 - 1) + jbeg = jvlatfix - (maxvgrid/2 - 1) + jend = jvlatfix + (maxvgrid/2) + + if (ibeg < 1 .or. jbeg < 1 .or. + & iend > imax .or. jend > jmax) then + + ! DO NOT quit if we find a boundary outside the grid + ! bounds. Rather, just set the J violating bound(s) to + ! the min or max limit, and for I bounds, allow the + ! program to continue down to subsequent code below, + ! provided it's a global grid. + +c print *,'!!! ' +c print *,'!!! Before vmag adjustments, boundaries are: ' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt,' dx= ',dx +c print *,'!!! temp_grid_minlon= ',temp_grid_minlon +c print *,'!!! vmag_latmax= ',vmag_latmax +c print *,'!!! ivlonfix = ',ivlonfix,' jvlatfix = ',jvlatfix +c print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax +c print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Vmag will not be computed for' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Vmag will not be computed for ' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,'!!! ' + print *,'!!! *AFTER* vmag adjustments, boundaries are: ' + print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax + print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + endif + + endif + + endif + +ctpm6/14 if (cparm == 'vmag') then +ctpm6/14 ri = re * 3 +c print '(a36,f10.4,a6,f10.4)' +c & ,' + before call to vmag barnes, re= ',re,' ri= ',ri +ctpm6/14 endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,bskip1,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes...' + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After 1st findmax loop, # calls to barnes = ',ibct + print *,'Total # of barnes loop iterations = ',ibarnes_loopct + endif + +c + 55 format ('i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ',f7.3 + & ,' barnval= ',f11.5) + 56 format ('k= ',i3,' i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ' + & ,f7.3,' barnval= ',f11.5) + + if (ctlon < 0.) then + ! We have grid-wrapped to find the ctlon, which was found to be + ! < 0, so for reporting purposes and for the start of the next + ! loop, set ctlon to positive degress east. + ctlon = ctlon + 360. + endif + + if (cparm == 'zeta') then + + if ( verb .ge. 3 ) then + print *,'!!! Zeta check, fmax= ',fmax,' fmin= ',fmin + write (6,61) 360.-ctlon,ctlat,fmax*100000.,fmin*100000. + endif + + else + + if ( verb .ge. 3 ) then + write (6,63) 360.-ctlon,ctlat,fmin + endif + + endif + 61 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax (x10e5) = ',f10.3,' fmin (x10e5) = ',e15.3) + 63 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmin = ',e15.3) + 111 format (i2,' rlont= ',f7.2,'W rlatt= ',f7.2,' zeta= ',f13.8) + +c Through interpolation, the grid for vmag has already been +c minimized considerably, we don't need to go through the 2nd part +c of this subroutine, which halves the grid spacing. + + if (nhalf < 1 .or. cparm == 'vmag') then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c ------------------------------------------------------------- +c If the grid spacing is +c fine enough (I've chosen 0.2-deg as a min threshold), there is +c no need to halve the grid more than 3 times, as halving a +c 0.2-deg grid 3 times gives a resolution of 0.025-deg (2.7 km), +c or a max error in the position estimate of 2.7/2 = 1.35 km. + + if ((dx+dy)/2. <= 0.2) then + if ((dx+dy)/2. <= 0.05) then + nhalf = 1 + else + nhalf = 2 + endif + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +ctpm6/14 ctpm npts = 3 +ctpm6/14 npts = npts/2 +ctpm6/14 npts = max(npts,1) + npts = 3 + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only +c do this once for this grid-refinement (even though the grid is +c redefined 3 times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to get_ij_bounds' + print *,'!!! just before nhalf loop. Stopping processing' + print *,'!!! for storm number ',ist + endif + + ifmret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + + if ( verb .ge. 3 ) then + print *,' ' + endif + + if ((dx+dy)/2. <= 1.25 .and. ri >= 300 .and. re >= 150) then + retmp = re + ritmp = ri + re = re * 0.5 + ri = ri * 0.5 + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re has been reduced' + print *,'from ',retmp,' to ',re,', and ri has been reduced ' + print *,'from ',ritmp,' to ',ri + endif + + else + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re and ri have NOT ' + print *,'been changed. re = ',re,' ri = ',ri + endif + + endif + + ibct=0 + ibarnes_loopct = 0 + do k=1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: find_maxmin kloop, k= ',i2,' ',i2.2,':' + & ,i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat + fmax = -1.0e+15; fmin = 1.0e+15 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'find_maxmin nhalf loop, cparm= ',cparm,' k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,iskip,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes, k= ',k + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop2 + enddo jloop2 + + if ( verb .ge. 3 ) then + if (cparm == 'zeta') then + write (6,71) k,360.-ctlon,ctlat,fmax*100000.,fmin*100000. + else + write (6,73) k,360.-ctlon,ctlat,fmax,fmin + endif + endif + + enddo + + 71 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax (x10e5) = ',f10.3,' fmin (x10e5) = ',e15.3) + 73 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax = ',e15.3,' fmin = ',e15.3) + + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ppp after 2nd findmax loop, # calls to barnes = ' + & ,ibct + print *,'ppp Total # of barnes loop iterations = ' + & ,ibarnes_loopct + endif + + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine barnes(flon,flat,rlon,rlat,iimax,jjmax,iibeg,jjbeg + & ,iiend,jjend,fxy,defined_pt,bskip,re,ri,favg,icount,ctype + & ,trkrinfo,iret) +c +c ABSTRACT: This routine performs a single-pass barnes anaylsis +c of fxy at the point (flon,flat). The e-folding radius (km) +c and influence radius (km) are re and ri, respectively. +c +c NOTE: The input grid that is searched in this subroutine is most +c likely NOT the model's full, original grid. Instead, a smaller +c subgrid of the original grid is searched. The upper left and +c lower right grid point indices are passed into this subroutine +c (iibeg, jjbeg, iiend, jjend) for this subgrid. These indices are +c determined in the subroutine get_ij_bounds, and the purpose of +c doing it this way is to limit the number of points for which the +c subroutine has to calculate distances (for a global 1 deg grid, +c the number of loop iterations is reduced from 65160 to somewhere +c around 600). +c +c NOTE: This subroutine will immediately exit with a non-zero +c return code if it tries to access a grid point that does not have +c valid data. This would happen in the case of a regional grid, if +c you try to access a point near the edge of the grid (remember that +c because of the interpolation for the regional grids, there will be +c areas around the edges that have no valid data). +c +c INPUT: +c flon Lon value for center point about which barnes anl is done +c flat Lat value for center point about which barnes anl is done +c rlon Array of lon values for each grid point +c rlat Array of lat values for each grid point +c iimax Max number of pts in x-direction on input grid +c jjmax Max number of pts in y-direction on input grid +c iibeg i index for grid point to start barnes anlysis (upp left) +c jjbeg j index for grid point to start barnes anlysis (upp left) +c iiend i index for last grid point in barnes anlysis (low right) +c jjend j index for last grid point in barnes anlysis (low right) +c fxy Real array of data on which to perform barnes analysis +c defined_pt Logical; bitmap array used for regional grids +c bskip integer to indicate number of grid points to skip during +c a barnes loop, in order to speed processing +c re input e-folding radius for barnes analysis +c ri input influence radius for searching for min/max +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, in +c this barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c favg Average value about the point (flon,flat) +c iret Return code from this subroutine +c + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real fxy(iimax,jjmax), rlon(iimax), rlat(jjmax) + real degrees + integer bskip + logical(1) defined_pt(iimax,jjmax) + character(*) ctype + +c -------------------------- + + res = re*re + wts = 0.0 + favg = 0.0 + + icount = 0 + + do jix=jjbeg,jjend,bskip + do iix=iibeg,iiend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > iimax) then + if (trkrinfo%gridtype == 'global') then + i = iix - iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i,' imax= ',iimax + print *,' ' + endif + + stop 97 + endif + endif + + icount = icount + 1 + + call calcdist(flon,flat,rlon(i),rlat(j),dist,degrees) + + if (dist .gt. ri) cycle + + if (defined_pt(i,j)) then + if (fxy(i,j) >-999.01 .and. fxy(i,j) <-998.99) then + ! This is a patch. Even though this (i,j) is a valid + ! point, its zeta value has been set to -999 because a + ! neighboring point in subroutine rvcal was found + ! to be out of the grid boundaries. + cycle + endif + wt = exp(-1.0*dist*dist/res) + wts = wts + wt + favg = favg + wt*fxy(i,j) + else + if (ctype == 'vitals') then + continue + else +carw print *,' ' +carw print *,'!!! UNDEFINED PT OUTSIDE OF GRID IN BARNES....' +carw print *,'!!! i= ',i,' j= ',j +carw print *,'!!! flon= ',flon,' flat= ',flat +carw print *,'!!! rlon= ',rlon(i),' rlat= ',rlat(j) +carw print *,'!!! re= ',re,' ri= ',ri +carw print *,'!!! EXITING BARNES....' +carw print *,' ' +carw iret = 95 +carw return + endif + endif + + enddo + enddo + + if (wts > 1.0E-5) then + favg = favg/wts + else + favg = 0.0 + endif + iret = 0 +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,rglatmax,rglatmin,rglonmax,rglonmin,geslon,geslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) +c +c ----------------------------------------------------------- +c ABSTRACT: This subroutine figures out, based on ri, dx and dy and +c the guess latitude and longitude positions, the farthest reaching +c grid points that are searchable by an analysis subroutine. The +c purpose is to return indices for a subgrid that is much smaller +c than the original, full grid. This smaller subgrid can then be +c passed to a subsequent analysis or interpolation subroutine, and +c work can be done on this smaller array. This can help save time, +c especially in the barnes analysis subroutine, as work will only +c be done on, say, a 20 x 20 array (400 pts) instead of on a +c 360 x 181 array (65160 pts). It's crucial, however, to make sure +c that the ibeg, jbeg, iend and jend are extended far enough out to +c fully encompass any search that would be done. Below is a +c diagram showing the different grids.... +c +c Full Global or Regional Model Grid (Grid F) -----------> +c ---------------------------------------------------------------- +c | | (ibeg,jbeg) | +c | | x = ij position that the | (Grid R) | +c | | geslat/geslon is fixed to. ._______________. | +c | | | | | +c | | Even though only the points | (Grid B) | | +c | | within Grid B will be checked | . . . . k | | +c v | later on for a max/min (in the | . . . . . | | +c | case of a subsequent call to | . . x . e | | +c | find_maxmin), the barnes anal- | . . . . . | | +c | ysis will include all pts sur- | . . . . . | | +c | rounding these Grid B points | | | +c | that are within a radius of ri. ._______________. | +c | So in the case of pt. k, that ri | +c | radius may extend all the way to the Grid R | | +c | boundary, thus we need to include those (iend,jend) | +c | points within our ibeg-jbeg-iend-jend bounds. | +c | | +c ---------------------------------------------------------------- +c +c Remember that the grids we deal with start north and increase +c south, so the northernmost latitude on the input grid will have +c a j index of 1. +c +c INPUT: +c npts Num pts from x to edge of max/min search grid (Grid B) +c (i.e., You define the size of Grid B by the value of +c npts that you pass into this subroutine). +c nhalf Number of times the grid spacing will be halved +c ri Radius of influence (for use in barnes analysis) +c imax Number of points in x-direction on original grid +c jmax Number of points in y-direction on original grid +c dx Input grid spacing in i-direction on original grid +c dy Input grid spacing in j-direction on original grid +c rglatmax Value of northern-most latitude on original grid +c rglatmin Value of southern-most latitude on original grid +c rglonmax Value of eastern-most longitude on original grid +c rglonmin Value of western-most longitude on original grid +c geslat Value of latitude of guess position of storm +c geslon Value of longitude of guess position of storm +c +c OUTPUT: +c ilonfix i index on full, input grid that storm is fixed to +c jlatfix j index on full, input grid that storm is fixed to +c ibeg i index for top left of sub-array (Grid R) of input grid +c iend i index for bot right of sub-array (Grid R) of input grid +c jbeg j index for top left of sub-array (Grid R) of input grid +c jend j index for bot right of sub-array (Grid R) of input grid +c igiret Return code from this subroutine +c + USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + real tmpangle +c + igiret = 0 +c +c -------------------------------------- +c GET BEGINNING AND ENDING J POINTS.... +c +c (1) Calculate number of searchable, max/min pts, that is, the pts +c from x to the edge of Grid B. +c (2) Calculate number of pts beyond the last search point in Grid +c B, but are within the bounds of Grid R and thus can be +c included in the barnes analysis. +c (3) Add (1) and (2) to get the max number of pts to subtract/add +c to x to get jbeg and jend. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Beginning of get_ij_bounds...' + print *,' geslat= ',geslat,' geslon= ',geslon + print *,' ' + endif + + +c If nhalf > 0: This occurs in the case of a call from fmax, when +c the grid spacing is halved nhalf times. In this case, we have to +c do extra work to figure out the maximum possible grid point. For +c this case: +c jhlatpts = # of grid pts to last possible search pt (from x to +c edge of Grid B in above diagram), plus a cushion. +c jripts = # of searchable grid points within radius ri of last +c possible search pt (num pts between edge of Grid B +c and edge of Grid R in above diagram), plus a cushion +c jbmaxlatpts = # of pts (in j direction) from x to the edge of +c Grid R to include in this subgrid. +c +c If nhalf = 0: In this case, the grid spacing will not be reduced, +c so the number of pts in j direction from x to the edge of Grid +c B will be the input parameter npts, and just multiply it by 2, +c and add 2 for a cushion to get jmaxlatpts. Typically, this sub +c is called from find_maxmin, and in that sub, the first time that +c this sub is called, nhalf will = 0. Then, after a first-shot +c center is found, the grid spacing will be cut in order to rerun +c barnes on a smaller grid, and that's when nhalf will be sent +c here as 3. +c + if (nhalf > 0) then + rdeg = 0.0 + do i = 1,nhalf + rdeg = rdeg + float(npts) * (1./(float(i)*2)) * (dx+dy)/2 + enddo + jhlatpts = ceiling(rdeg/dy) + 1 + jripts = ceiling((ri + 1.)/(dtk*dx)) + 1 + jbmaxlatpts = jhlatpts + jripts + else + jbmaxlatpts = npts * 2 + 2 + endif +c +c +c Roughly fix geslat to the grid point just poleward of geslat. +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' +++ Near top of get_ij_bounds, ' + print *,' +++ geslat= ',geslat,' geslon= ',geslon + print *,' +++ rglatmax= ',rglatmax,' rglatmin= ',rglatmin + print *,' +++ rglonmax= ',rglonmax,' rglonmin= ',rglonmin + print *,' +++ imax= ',imax,' jmax= ',jmax + print *,' +++ dx= ',dx,' dy= ',dy,' nhalf= ',nhalf + print *,' +++ npts= ',npts + if(nhalf>0) then + print *,' +++ jhlatpts= ',jhlatpts,' jripts= ',jripts + else + print *,' +++ nhalf<=0 so jhlatpts and jripts unused' + endif + print *,' +++ jbmaxlatpts= ',jbmaxlatpts + endif + + if (geslat >= 0.0) then + jlatfix = int((rglatmax - geslat)/dy + 1.) + else + jlatfix = ceiling((rglatmax - geslat)/dy + 1.) + endif + + if ( verb .ge. 3 ) then + print *,' +++ jlatfix= ',jlatfix + endif + + jbeg = jlatfix - jbmaxlatpts + jend = jlatfix + jbmaxlatpts + if (jbeg > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jbeg > jmax' + print *,'!!! jbeg = ',jbeg,' jmax= ',jmax + endif + + igiret = igiret + 1 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jend < 1, jend = ',jend + endif + + igiret = igiret + 1 + return + endif + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' +++ jbeg= ',jbeg,' jend= ',jend + endif + + ! If using a global grid, avoid using the pole points, or else + ! you'll get a cosfac = 0 and then a divide by zero!!! + + if (jend == jmax .and. rglatmin == -90.0) then + jend = jmax - 2 + endif + if (jbeg == 1 .and. rglatmax == 90.0) then + jbeg = 3 + endif + +c ----------------------------------------- +c NOW GET BEGINNING AND ENDING I POINTS.... +c +c Using the map factor (cos lat), figure out, based on ri, the +c max distance beyond the last search point in x-direction (in +c degrees) that could be searched at this guess latitude (geslat) +c (i.e., in the diagram above, the max num pts from pt. e eastward +c to the edge of Grid R). Calculate how many grid points that is, +c add 2 to it for a cushion, & add the number of points (npts) +c within the defined search grid (Grid B) to get ibmaxlonpts. +c +c April, 2007: A min statement was put on the calculation to +c derive dlon, since with that cosine in there, the values of +c of dlon could get pretty ridiculous as you approach the poles. +c Also, the cosine factor (cosfac) used to be computed at the +c most poleward latitude possible given the jend here. For +c similar concerns with cosines near the poles, I've scrapped +c this to instead compute the cosine factor at the input +c guess latitude. - tpm + + cosfac = cos (geslat * dtr) + tmpangle = cosfac * dtk + dlon = min((ri /tmpangle ),20.0) +c dlon = min((ri / (cosfac * dtk)),20.0) +c + if (nhalf > 0) then + ihlonpts = ceiling(rdeg/dx) + 1 + ibmaxlonpts = ihlonpts + ceiling(dlon/dx) + 2 + else + ibmaxlonpts = npts + ceiling(dlon/dx) + 2 + endif + + if ( verb .ge. 3 ) then + if(nhalf>0) then + print *,' +++ rdeg= ',rdeg,' ri= ',ri,' cosfac= ',cosfac + print *,' +++ dtr= ',dtr,' dtk= ',dtk,' dlon= ',dlon + else + print*,' +++ nhalf<=0 so rdeg,ri,cosfac,dtr,dtk,dlon unused' + endif + print *,' +++ ibmaxlonpts= ',ibmaxlonpts,' dx= ',dx,' dy= ',dy + endif + +c Roughly fix geslon to the grid point just EASTward of geslon. + + ilonfix = int((geslon - rglonmin)/dx + 2.) + + ibeg = ilonfix - ibmaxlonpts + iend = ilonfix + ibmaxlonpts + + if ( verb .ge. 3 ) then + print *,' +++ (orig) ilonfix= ',ilonfix + print *,' +++ (orig) ibeg= ',ibeg,' iend= ',iend + print *,' +++ ' + endif + + if (ibeg > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 1 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, ibeg > imax' + print *,'!!! for a non-global grid' + print *,'!!! ibeg = ',ibeg,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + ! For a regional grid, just set iend to be imax + iend = imax + endif + endif + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + ! For a regional grid, just set ibeg to be 1 + ibeg = 1 + endif + endif + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + + if ( verb .ge. 3 ) then + print *,'!!! ERROR in get_ij_bounds, iend < 1' + print *,'!!! for a non-global grid' + print *,'!!! iend = ',iend,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine check_bounds (guesslon,guesslat,ist,ifh,trkrinfo + & ,icbret) +c +c ABSTRACT: This subroutine checks to make sure that the requested +c storm is in fact within the model's grid boundaries; +c this is only a concern for the regional models. +c + USE def_vitals; USE grid_bounds; USE set_max_parms + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + if (guesslon > glonmax .or. guesslon < glonmin .or. + & guesslat > glatmax .or. guesslat < glatmin) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is outside of grid' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + goto 125 + else + icbret = 0 + endif + + ! We have encountered problems with global grids where we + ! continue tracking almost the whole way to the pole. While + ! that's nice to do that, it creates problems for array + ! indices, especially in subroutine getradii. So we will cut + ! our losses and eliminate tracking of storms within + ! 5 degrees of the pole for global grids. + + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'global')then + if (guesslat > 85.0 .or. guesslat < -85.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is too close to the' + print *,'!!! N or S Pole for global tracking.' + print *,'!!! STOPPING TRACKING FOR THIS STORM DUE TO POLE' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + else + icbret = 0 + endif + endif + + 125 continue +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calcdist(rlonb,rlatb,rlonc,rlatc,xdist,degrees) +c +c ABSTRACT: This subroutine computes the distance between two +c lat/lon points by using spherical coordinates to +c calculate the great circle distance between the points. +c Figure out the angle (a) between pt.B and pt.C, +c N. Pole then figure out how much of a % of a great +c x circle distance that angle represents. +c / \ +c b/ \ cos(a) = (cos b)(cos c) + (sin b)(sin c)(cos A) +c / \ +c pt./<--A-->\c NOTE: The latitude arguments passed to the +c B / \ subr are the actual lat vals, but in +c \ the calculation we use 90-lat. +c a \ +c \pt. NOTE: You may get strange results if you: +c C (1) use positive values for SH lats AND +c you try computing distances across the +c equator, or (2) use lon values of 0 to +c -180 for WH lons AND you try computing +c distances across the 180E meridian. +c +c NOTE: In the diagram above, (a) is the angle between pt. B and +c pt. C (with pt. x as the vertex), and (A) is the difference in +c longitude (in degrees, absolute value) between pt. B and pt. C. +c +c !!! NOTE !!! -- THE PARAMETER ecircum IS DEFINED (AS OF THE +c ORIGINAL WRITING OF THIS SYSTEM) IN KM, NOT M, SO BE AWARE THAT +c THE DISTANCE RETURNED FROM THIS SUBROUTINE IS ALSO IN KM. +c + USE trig_vals + + real degrees +c + if (rlatb < 0.0 .or. rlatc < 0.0) then + pole = -90. + else + pole = 90. + endif +c + distlatb = (pole - rlatb) * dtr + distlatc = (pole - rlatc) * dtr + difflon = abs( (rlonb - rlonc)*dtr ) +c + cosanga = ( cos(distlatb) * cos(distlatc) + + & sin(distlatb) * sin(distlatc) * cos(difflon)) + +c This next check of cosanga is needed since I have had ACOS crash +c when calculating the distance between 2 identical points (should +c = 0), but the input for ACOS was just slightly over 1 +c (e.g., 1.00000000007), due to (I'm guessing) rounding errors. + + if (cosanga > 1.0) then + cosanga = 1.0 + endif +c added on 04/28/2016-------------------------- + if (cosanga < -1.0) then + cosanga = -1.0 + endif +c added on 04/28/2016-------------------------- + + degrees = acos(cosanga) / dtr + circ_fract = degrees / 360. + xdist = circ_fract * ecircum +c +c NOTE: whether this subroutine returns the value of the distance +c in km or m depends on the scale of the parameter ecircum. +c At the original writing of this subroutine (7/97), ecircum +c was given in km. +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine subtract_cor (imax,jmax,dy,level) +c +c ABSTRACT: This subroutine subtracts out the coriolis parameter +c from the vorticity values. It is needed because at the original +c writing of this system, all of the forecast centers who included +c vorticity included only absolute vorticity. +c + USE tracked_parms; USE trig_vals; USE grid_bounds + + implicit none + + integer :: i,j,imax,jmax,level + real :: dy,coriolis,rlat +c + do j=1,jmax + rlat = glatmax - ((j-1) * dy) + coriolis = 2. * omega * sin(rlat*dtr) + do i=1,imax + zeta(i,j,level) = zeta(i,j,level) - coriolis + enddo + enddo +c + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_grib_file_name (ifh,gfilename,ifilename) + +c ABSTRACT: This subroutine uses various input regarding the model +c and forecast hour and generates the name of the input grib file +c for this particular forecast hour. Remember that the lead time +c is in minutes and that 5 spaces must be reserved for the lead +c time (e.g., f00360). File name should be something that looks +c like either, e.g., "gfdl.6thdeg.katrina12l.2005082818.f00720", +c or "gfdl.6thdeg.2005082818.f00720" (the part in there with the +c storm name & ID is optional). The grib index file name should +c be exactly the same as the grib data file itself, but with the +c character string ".ix" added onto the end of the name. +c +c NOTE: Array iftotalmins is brought in via module tracked_parms. +c +C INPUT: +c ifh integer array index for current lead time +c +c OUTPUT: +c gfilename GRIB file name +c ifilename GRIB index file name + + USE gfilename_info; USE tracked_parms; USE atcf + USE verbose_output + + implicit none + + character(*) gfilename,ifilename + character cfmin*5,cymdh*10 + integer ifh,nlen1,nlen2,nlen3,nlen4,nlen5 + +c Convert integer minutes to 5-position character, with +c leading zeroes, and convert 10-digit integer date into +c 10-position character. Then trim the various input variables +c and combine all into the file name. + + write (cfmin,'(i5.5)') iftotalmins(ifh) + write (cymdh,'(i10.10)') atcfymdh + + nlen1 = len_trim(gmodname) + gfilename = trim(gmodname(1:nlen1)) + + nlen2 = len_trim(rundescr) + + gfilename = trim(gfilename(1:nlen1))//'.'//trim(rundescr(1:nlen2)) + + nlen3 = len_trim(atcfdescr) + nlen4 = len_trim(gfilename) + +c If an extension to the name with the ATCF or storm name descriptor +c was included, then add it to the name now. Otherwise, just add +c the starting date and the lead time in minutes. + + if (nlen3 > 0) then + gfilename = trim(gfilename(1:nlen4))//'.' + & //trim(atcfdescr(1:nlen3))//'.'//cymdh//'.f'//cfmin + else + gfilename = trim(gfilename(1:nlen4))//'.'//cymdh//'.f'//cfmin + endif + +c Create the name for the grib index file, which is just the name of +c the grib file, with "ix" added to the end of it. + + nlen5 = len_trim(gfilename) + ifilename = trim(gfilename(1:nlen5))//'.ix' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,72) 'gfilename',gfilename + write (6,72) 'ifilename',ifilename + endif + + 72 format (1x,'In get_grib_file_name, file name for ',a9 + & ,' is ',a120) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata (readflag,valid_pt,imax,jmax,ifh + & ,need_to_flip_lats,need_to_flip_lons,inp,lugb,lugi + & ,trkrinfo) +c +c ABSTRACT: This subroutine reads the input GRIB file for the +c tracked parameters. It then calls subroutines to convert the +c data from a 1-d array into a 2-d array if the read was successful. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature (I jerry-rigged this by storing +c the data as being at the 401 mb level.) +cJ.Peng----2014-10-01----- +c 15. Ushear 200-850hPa (501hPa level) +c 16. 500hPa relative humidity +c +c INPUT: +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c inp of a derived type, contains user-input info +c lugb integer unit number of input grib file +c lugi integer unit number of input grib index file +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE verbose_output; USE params; USE grib_mod; USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + type (datecard) inp + type (gribfield) :: gfld,prevfld,holdgfld +c + integer, parameter :: jf=40000000 +c integer, parameter :: nparms=14 +cJ.Peng----2014-10-01----- + integer, parameter :: nparms=16 + + real, allocatable :: f(:) + real :: dmin,dmax,firstval,lastval + logical(1), allocatable :: lb(:) + logical(1) valid_pt(imax,jmax),readflag(nparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1) file_open + logical :: unpack=.true. + logical :: open_grb=.false. + character*1 :: lbrdflag + character*8 :: chparm(nparms) + CHARACTER(len=8) :: pabbrev + character (len=10) big_ben(3) + integer date_time(8) + integer,dimension(200) :: jids,jpdt,jgdt + integer :: listsec1(13) + integer, intent(in) :: imax,jmax + integer igparm(nparms),iglev(nparms),iglevtyp(nparms) + integer ig2_parm_cat(nparms),ig2_parm_num(nparms) + integer ig2_parm_cat_cmcd(nparms),ig2_parm_num_cmcd(nparms) +cJ.Peng---2014-10-20-------------------------------------- + integer ig2_lev_10(nparms) + integer ig2_lev_11(nparms),ig2_lev_12(nparms) + integer ig2_lev_11_cmc(nparms),ig2_lev_12_cmc(nparms) + integer ig2_lev_11_cmcd(nparms),ig2_lev_12_cmcd(nparms) + + integer cpsig2_parm_cat(nlevs_cps),cpsig2_parm_num(nlevs_cps) +cJ.Peng---2014-10-20-------------------------------------- + integer cpsig2_lev_10(nlevs_cps) + integer cpsig2_lev_11(nlevs_cps),cpsig2_lev_12(nlevs_cps) + + integer ec_igparm(nparms),ec_iglev(nparms),ec_iglevtyp(nparms) + integer cpsgparm(nlevs_cps),cpsglev(nlevs_cps) + integer cpsglevtyp(nlevs_cps) + integer ec_cpsgparm(nlevs_cps) + integer jpds(200),jgds(200),kpds(200),kgds(200) + integer igvret,ifa,ila,ip,ifh,i,j,k,kj,iret,kf,lugb,lugi + integer jskp,jdisc,np + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer pdt_4p0_vert_level,pdt_4p0_vtime + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 +c + lbrdflag = 'n' + +c The following data statements contain the parameters that will be +c used to search the grib files. The first 9 parameters will all be +c used to locate the storm position. The last 4 parameters (500 mb +c u- and v-components and 10 m u- and v- components) will not be +c used for tracking, but only for helping to estimate the next first +c guess position (500 mb winds) and for estimating the max near- +c surface wind speeds in the vicinity of the storm (10 m winds). +c +c ** NOTE: iglevtyp(12 & 13) and iglev(12 & 13) are initialized to +c 0 just to satisfy the IBM xlf compiler, which barks about +c there being too few initial values in the list when I +c only had 11 values there -- even though the real +c initialization for these variables is done just about +c 10 lines below. +c +c ** NOTE: The new ECMWF hi-res data uses the ECMWF GRIB parameter +c ID table, which has different values than the NCEP +c table. Therefore, we needed to add the variables and +c data values for ec_igparm, ec_iglevtyp and ec_iglev. +c +c July 2007: Read statements added for GP height for cyclone +c phase space (CPS) algorithm. + +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 +c & ,100/ +c data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500,401/ +cJ.Peng---2014-10-01---------------------------------------------- + data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,33,52/ + data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 + & ,100,100,100/ + data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500, + & 401,501,500/ + + data cpsgparm /13*7/ + data ec_cpsgparm /13*156/ + data cpsglevtyp /13*100/ + data cpsglev /900,850,800,750,700,650,600,550,500,450,400 + & ,350,300/ + +c data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 +c & ,131,132,130/ +c data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 +c & ,100/ +c data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 +c & ,401/ +c +c data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' +c & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' +c & ,'vgrid','temp'/ +cJ.Peng---2014-10-01-ECMWF data parameter has been changed----- + data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 + & ,131,132,130,131,157/ + data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 + & ,100,100,100/ + data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 + & ,401,501,500/ + data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' + & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' + & ,'vgrid','temp','ushe','rhum'/ + +cJ.Peng---2014-10-01-add GRIB2 data for ushe and rhum-- + data ig2_parm_cat /2,2,2,2,2,2,3,3,3,2,2,2,2,0,2,1/ + data ig2_parm_num /10,10,2,3,2,3,5,5,1,2,3,2,3,0,2,1/ + data ig2_parm_cat_cmcd /2,2,2,2,2,2,3,3,3,2,2,2,2,0,2,1/ + data ig2_parm_num_cmcd /10,10,2,3,2,3,5,5,1,2,3,2,3,0,2,1/ + + data ig2_lev_10 /100,100,100,100,100,100,100,100,101,103,103 + & ,100,100,100,100,100/ + data cpsig2_parm_cat /13*3/ + data cpsig2_parm_num /13*5/ + data cpsig2_lev_10 /13*100/ + +cJ.Peng---for gfs/gefs NEVGEM/FENS ensemble--------------------- + data ig2_lev_11 /0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0/ + data ig2_lev_12 /85000,70000,85000,85000,70000,70000 + & ,85000,70000,0,10,10,50000,50000 + & ,40100,50100,50000/ + data cpsig2_lev_11 /13*0/ + data cpsig2_lev_12 /90000,85000,80000,75000,70000,65000 + & ,60000,55000,50000,45000,40000 + & ,35000,30000/ +cJ.Peng---for CMC ensemble---------------------------------------- + data ig2_lev_11_cmc /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0 + & ,-4,-4,0,0,-4/ + data ig2_lev_12_cmc /85,7,85,85,7,7,85,7,0,10,10,5,5 + & ,40100,50100,5/ + +cJ.Peng---for CMC deterministic---------------------------------------- + data ig2_lev_11_cmcd /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0 + & ,-4,-4,0,0,0/ + data ig2_lev_12_cmcd /85,7,85,85,7,7,85,7,0,10,10,5,5 + & ,40100,50100,50000/ + +c This next bit is needed because we need to read the near-surface +c winds, and while several models provide us with 10m winds, the +c UKMET gives us surface winds, while nogaps gives us 10m winds. +c For GFDL, we have 35m winds. + +c Model numbers used: (1) AVN, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) Early Eta, (7) NOGAPS, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble, +c (13) SREF Ensemble, +c (14) NCEP Ensemble (from ensstat mean fields), +c (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) NCEP Ensemble RELOCATION +c (21) UKMET hi-res (from NHC) + + if (trkrinfo%gribver == 2) then + +c So far, for GRIB v2, we have all the same IDs for 10m winds for +c all models, so no need to break out by model like we do for +c GRIB v1 in the else portion of this if statement. However, we +c do need to check to see if the input model = 1 or 8 (which is +c the GFS and GDAS). If so, then we want to look for the +c so-called membrane MSLP, which has a GRIB2 param num of 192. + + if (inp%model == 1 .or. inp%model == 10) then + ig2_parm_num(9) = 192 ! Membrane MSLP for GFS/gefs only + endif + + if ( verb .ge. 3 ) then + print *,' ' + endif + + else + + if (inp%model == 1 .or. inp%model == 2 .or. + & inp%model == 5 .or. inp%model == 6 .or. inp%model == 8 .or. + & inp%model == 10 .or. inp%model == 13 .or. + & inp%model == 17 .or. + & inp%model == 14 .or. inp%model == 19 .or. + & inp%model == 20 .or. inp%model == 16 .or. + & inp%model == 22 .or. inp%model == 15) then + iglevtyp(10) = 105 + iglevtyp(11) = 105 + iglev(10) = 10 + iglev(11) = 10 + else if (inp%model == 3) then ! UKMET: "surface" winds + iglevtyp(10) = 1 + iglevtyp(11) = 1 + iglev(10) = 0 + iglev(11) = 0 + else if (inp%model == 4) then ! ECMWF hi-res: "surface" winds + ec_iglevtyp(10) = 1 + ec_iglevtyp(11) = 1 + ec_iglev(10) = 0 + ec_iglev(11) = 0 + else if (inp%model == 7) then ! NOGAPS: 10m winds + iglevtyp(10) = 105 + iglevtyp(11) = 105 + iglev(10) = 10 + iglev(11) = 10 + else if (inp%model == 9) then ! GFDL: 35m winds + iglevtyp(10) = 105 + iglevtyp(11) = 105 + iglev(10) = 10 + iglev(11) = 10 + else if (inp%model == 21) then ! ECMWF Ensemble - 10m winds and +c pmsl level ID + iglevtyp(10) = 105 + iglevtyp(11) = 105 + iglev(10) = 10 + iglev(11) = 10 + iglevtyp(9) = 1 + endif + + if (inp%model == 1 .or. inp%model == 8 .or. inp%model == 22) + & then + ! For GFS & GDAS models, use the membrane (Eta reduction) MSLP, + ! also known by the GRIB abbreviation of MSLET + igparm(9) = 130 + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata. A return' + print *,'code (iret) not equal to zero indicates that ' + print *,'subroutine getgb was unable to find the requested ' + print *,'parameter. This could be simply because the parm is ' + print *,'not included in the grib file (this is likely for ' + print *,'ECMWF data, as they limit what they send us), or it ' + print *,'could indicate a problem with the grib index file.' + endif + + + if (allocated(f)) deallocate(f) + if (allocated(lb)) deallocate(lb) + allocate (f(imax*jmax),stat=ifa) + allocate (lb(imax*jmax),stat=ila) + if (ifa /= 0 .or. ila /= 0) then + print *,' ' + print *,'!!! ERROR in getdata allocating f or lb array.' + print *,'!!! ifa = ',ifa,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + if (trkrinfo%gribver == 2) then + + ! Reading from a GRIB v2 file.... + + grib2_standard_parm_read_loop: do ip = 1,nparms + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 ! meteorological products + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for input parameter by production template 4.0. This +c tave program is used primarily for temperature, but still we +c will leave that as a variable and not-hard wire it in case we +c choose to average something else in the future. + + ! We are looking for Temperature or GP Height here. This + ! block of code, or even the smaller subset block of code that + ! contains the JPDT(1) and JPDT(2) assignments, can of course + ! be modified if this program is to be used for interpolating + ! other variables.... + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + if(inp%model == 15) then + JPDT(1) = ig2_parm_cat_cmcd(ip) + JPDT(2) = ig2_parm_num_cmcd(ip) + else + JPDT(1) = ig2_parm_cat(ip) + JPDT(2) = ig2_parm_num(ip) + endif + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + + JPDT(10) = ig2_lev_10(ip) + +cJ.Peng---------------------------------------------- + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 ) then + JPDT(11) = ig2_lev_11(ip) + JPDT(12) = ig2_lev_12(ip) + else if(inp%model == 16) then + JPDT(11) = ig2_lev_11_cmc(ip) + JPDT(12) = ig2_lev_12_cmc(ip) + else if(inp%model == 15) then + JPDT(11) = ig2_lev_11_cmcd(ip) + JPDT(12) = ig2_lev_12_cmcd(ip) + endif + + if ( verb .ge. 3 ) then + print *,'before getgb2 call, value of unpack = ',unpack + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is CLOSED' + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,531) date_time(5),date_time(6),date_time(7) + 531 format (1x,'TIMING: before getgb2-1',i2.2,':',i2.2,':',i2.2) + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,532) date_time(5),date_time(6),date_time(7) + 532 format (1x,'TIMING: after getgb2-1',i2.2,':',i2.2,':',i2.2) + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 in getdata = ',iret + print *,'after getgb2 call, value of unpacked = ' + & ,gfld%unpacked + print *,'after getgb2 call, gfld%ndpts = ',gfld%ndpts + print *,'after getgb2 call, gfld%ibmap = ',gfld%ibmap + endif + + if ( iret == 0) then + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + print *,' gfld%idrtnum = ', gfld%idrtnum + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb .ge. 3 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ndpts ! Number of gridpoints returned from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if ( verb .ge. 3 ) then + + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + + pdt_4p0_vtime = gfld%ipdtmpl(9) + +cJ.Peng--------------------------------------------------- + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ndpts,firstval,lastval,dmin,dmax +c & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + + endif + + select case (chparm(ip)) + case ('absv') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) +cJ.Peng----2014-10-01----------- + case ('ushe') + call conv1d2d_real (imax,jmax,f,ushear + & ,need_to_flip_lats) + case ('rhum') + call conv1d2d_real (imax,jmax,f,rhumid + & ,need_to_flip_lats) + + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb2 could not find parm: ' + & ,chparm(ip) + print *,'!!! at level = ',ig2_lev_12(ip) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib2_standard_parm_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c This is the GRIB2 reading section. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + grib2_cps_parm_lev_loop: do ip = 1,nlevs_cps + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + + jpds = -1 + jgds = -1 + j=0 + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = cpsig2_parm_cat(ip) + JPDT(2) = cpsig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + + JPDT(10) = cpsig2_lev_10(ip) + +cJ.Peng---------------------------------------------- + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15) then + JPDT(11) = cpsig2_lev_11(ip) + JPDT(12) = cpsig2_lev_12(ip) + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,731) date_time(5),date_time(6),date_time(7) + 731 format (1x,'TIMING: before getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn + & ,jgdt,unpack,krec,gfld,iret) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,732) date_time(5),date_time(6),date_time(7) + 732 format (1x,'TIMING: after getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 (PHASE) in getdata = ',iret + print *,'after getgb2 call(PHASE),' + & ,' value of unpacked = ',gfld%unpacked + print *,'after getgb2 (PHASE) call, gfld%ndpts = ' + & ,gfld%ndpts + print *,'after getgb2 (PHASE) call, gfld%ibmap = ' + & ,gfld%ibmap + + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb2 (PHASE) call, j= ',j,' k= ',k + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb2 (PHASE) call, j= ',j,' k= ',k + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + +c Determine packing information from GRIB2 file. +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb .ge. 3) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) + & then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb .ge. 3 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ' + & ,gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ' + & ,gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ndpts ! Number of gridpoints returned + ! from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do +c this once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb .ge. 3) then + + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) + & then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.' + & ,gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ' + & ,gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ' + & ,gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.' + & ,gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + + pdt_4p0_vtime = gfld%ipdtmpl(9) + +cJ.Peng------------------------------------------------------- + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline + & ,gfld%ipdtmpl(1),gfld%ipdtmpl(2)) + + print *,' ' + write (6,231) + 231 format (' rec# param level byy bmm bdd ' + & ,'bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ndpts,firstval,lastval,dmin + & ,dmax +c & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo grib2_cps_parm_lev_loop + + endif + + endif + + else + + ! Reading from a GRIB v1 file.... + + grib1_read_loop: do ip = 1,nparms + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then ! ECMWF hi-res data uses ECMWF table + jpds(5) = ec_igparm(ip) + jpds(6) = ec_iglevtyp(ip) + jpds(7) = ec_iglev(ip) + else ! All other models use NCEP-standard GRIB table + jpds(5) = igparm(ip) + jpds(6) = iglevtyp(ip) + jpds(7) = iglev(ip) + endif + + if (jpds(5) == 999) then + cycle + endif + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,831) date_time(5),date_time(6),date_time(7) + 831 format (1x,'TIMING: before getgb-1',i2.2,':',i2.2,':',i2.2) + + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,832) date_time(5),date_time(6),date_time(7) + 832 format (1x,'TIMING: after getgb-1',i2.2,':',i2.2,':',i2.2) + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb call, j= ',j,' k= ',k + & ,' iftotalmins= ' + & ,iftotalmins(ifh),' parm # (ip) = ',ip,' iret= ',iret + else + print *,'After getgb call, j= ',j,' k= ',k,' ifhours= ' + & ,ifhours(ifh),' parm # (ip) = ',ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,29) + else + write (6,31) + endif + 29 format (' rec# parm# levt lev byy bmm bdd bhh fmin' + & ,' npts minval maxval') + 31 format (' rec# parm# levt lev byy bmm bdd bhh fhr ' + & ,' npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + + select case (chparm(ip)) + case ('absv') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) +cJ.Peng----2014-10-01----------- + case ('ushe') + call conv1d2d_real (imax,jmax,f,ushear + & ,need_to_flip_lats) + case ('rhum') + call conv1d2d_real (imax,jmax,f,rhumid + & ,need_to_flip_lats) + + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb could not find parm: ',chparm(ip) + print *,'!!! at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib1_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + cps_grib1_lev_loop: do ip = 1,nlevs_cps + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then + ! Use different grib parm id for ECMWF GP height + jpds(5) = ec_cpsgparm(ip) + else + jpds(5) = cpsgparm(ip) + endif + jpds(6) = cpsglevtyp(ip) + jpds(7) = cpsglev(ip) + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,841) date_time(5),date_time(6),date_time(7) + 841 format (1x,'TIMING: before getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,842) date_time(5),date_time(6),date_time(7) + 842 format (1x,'TIMING: after getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,39) + else + write (6,41) + endif + 39 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fmin npts minval maxval') + 41 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fhr npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo cps_grib1_lev_loop + + endif + + endif + + endif +c + deallocate (f) + deallocate (lb) +c + return + end +c +c------------------------------------------------------------------- +c +c------------------------------------------------------------------- + subroutine bitmapchk (n,ld,d,dmin,dmax) +c +c This subroutine checks the bitmap for non-existent data values. +c Since the data from the regional models have been interpolated +c from either a polar stereographic or lambert conformal grid +c onto a lat/lon grid, there will be some gridpoints around the +c edges of this lat/lon grid that have no data; these grid +c points have been bitmapped out by Mark Iredell's interpolater. +c To provide another means of checking for invalid data points +c later in the program, set these bitmapped data values to a +c value of -999.0. The min and max of this array are also +c returned if a user wants to check for reasonable values. +c + logical(1) ld + dimension ld(n),d(n) +c + dmin=1.E15 + dmax=-1.E15 +c + do i=1,n + if (ld(i)) then + dmin=min(dmin,d(i)) + dmax=max(dmax,d(i)) + else + d(i) = -999.0 + endif + enddo +c + return + end +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic (imax,jmax,lb1d,lb2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of logical data (lb1d) into a 2-dimensional output +c array (dimension imax,jmax) of logical data (lb2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NOGAPS grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c lb1d 1-d array containing logical bitmap values +c iscanflag This is kgds(11), an integer value in the GDS, +c which holds the scanning mode for the data values +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + logical(1) lb1d(imax*jmax),lb2d(imax,jmax) + logical(1) :: need_to_flip_lats +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + lb2d(ilon,ilatix) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + lb2d(ilon,ilat) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine conv1d2d_real (imax,jmax,dat1d,dat2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of real data (dat1d) into a 2-dimensional output +c array (dimension imax,jmax) of real data (dat2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NOGAPS grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d real array of data +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c dat2d 2-d real array of data +c + logical(1) :: need_to_flip_lats + real dat1d(imax*jmax),dat2d(imax,jmax) +c + if (need_to_flip_lats) then + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + dat2d(ilon,ilatix) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + dat2d(ilon,ilat) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_nlists (inp,trkrinfo) +c +c ABSTRACT: This subroutine simply reads in the namelists that are +c created in the shell script. Namelist datein contains the +c starting date information, plus the model identifier. Namelist +c stswitch contains the flags for processing for each storm. +c + USE inparms; USE set_max_parms; USE atcf; USE trkrparms; USE phase + USE structure; USE gfilename_info + USE verbose_output; USE waitfor_parms + + implicit none + + integer ifh + type (datecard) inp + type (trackstuff) trkrinfo +c + namelist/datein/inp + namelist/atcfinfo/atcfnum,atcfname,atcfymdh,atcffreq + namelist/trackerinfo/trkrinfo + namelist/phaseinfo/phaseflag,phasescheme,wcore_depth + namelist/structinfo/structflag,ikeflag + namelist/fnameinfo/gmodname,rundescr,atcfdescr + namelist/verbose/verb + namelist/waitinfo/use_waitfor,wait_min_age,wait_min_size + & ,wait_max_wait,wait_sleeptime + & ,use_per_fcst_command,per_fcst_command + +c Set namelist default values: + use_per_fcst_command='t' + per_fcst_command=' ' + atcffreq=600 + trkrinfo%want_oci=.false. + trkrinfo%gribver=1 ! Set to GRIB1 as default, can be set to + ! something else in the namelist input. + + read (5,NML=datein,END=801) + 801 continue + read (5,NML=atcfinfo,END=807) + 807 continue + print *,'just before trackerinfo read namelist' + read (5,NML=trackerinfo,END=809) + 809 continue + print *,'just after trackerinfo read namelist' + read (5,NML=phaseinfo,END=811) + 811 continue + read (5,NML=structinfo,END=815) + 815 continue + read (5,NML=fnameinfo,END=817) + 817 continue + read (5,NML=verbose,END=819,ERR=833) + 819 continue + read (5,NML=waitinfo,END=821) + 821 continue + goto 837 + 833 continue + verb = 1 + 837 continue + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After datein namelist in trak.f, namelist ' + & ,'parms follow:' + print *,'Forecast initial year = byy = ',inp%byy + print *,'Forecast initial month = bmm = ',inp%bmm + print *,'Forecast initial day = bdd = ',inp%bdd + print *,'Forecast initial hour = bhh = ',inp%bhh + print *,'Forecast model identifier = model= ',inp%model + print *,'Forecast model type = modtyp= ',inp%modtyp + print *,'Forecast model data lead time units= lt_units= ' + & ,inp%lt_units + print *,'Forecast model data sequencing setup= file_seq= ' + & ,inp%file_seq + print *,'Forecast model nest type = ',inp%nesttyp +c + print *,' ' + print *,'Values read in from atcfinfo namelist: ' + write (6,89) atcfnum,atcfname + write (6,90) atcfymdh + write (6,92) atcffreq + 89 format ('ATCF ID = ',i2,' ATCF Name = ',a4) + 90 format ('ATCF date (initial date on output atcf records) = ' + & ,i10) + 92 format ('ATCF output frequency (in hours*100) = atcffreq = ',i6) +c + print *,' ' + print *,'Values read in from trackerinfo namelist follow: ' + write (6,101) ' western boundary = westbd = ',trkrinfo%westbd + write (6,101) ' eastern boundary = eastbd = ',trkrinfo%eastbd + write (6,101) ' northern boundary = northbd = ',trkrinfo%northbd + write (6,101) ' southern boundary = southbd = ',trkrinfo%southbd + write (6,102) ' tracker type = ',trkrinfo%type + write (6,103) ' mslp threshold = mslpthresh = ' + & ,trkrinfo%mslpthresh + write (6,103) ' v850 threshold = v850thresh = ' + & ,trkrinfo%v850thresh + write (6,104) ' model grid type = ',trkrinfo%gridtype + write (6,101) ' Contour interval to be used = ',trkrinfo%contint + write (6,106) ' Flag for whether or not roci will be computed' + & ,' and written out for tracker-type case = ' + & ,trkrinfo%want_oci + write (6,105) ' Flag for whether or not vitals will be written ' + & ,'out = ',trkrinfo%out_vit + write (6,107) ' Flag for which GRIB version (1 or 2) the input' + & ,' data will be in = ',trkrinfo%gribver + write (6,108) ' Flag for input GRIB2 JPDTN (0 or 1) = ' + & ,trkrinfo%g2_jpdtn + 101 format (a31,f7.2) + 102 format (a16,a7) + 103 format (a31,f7.4) + 104 format (a19,a8) + 106 format (a46,a41,L1) + 105 format (a48,a6,a1) + 107 format (a47,a19,i1) + 108 format (a38,i1) + + print *,' ' + print *,'Values read in from phaseinfo namelist: ' + write (6,211) phaseflag,phasescheme + write (6,212) wcore_depth + 211 format ('Storm phase flag = ',a1,' Phase scheme = ',a4) + 212 format ('Storm phase, warm core depth (wcore_depth) = ',f7.2) + + print *,' ' + print *,'Values read in from structinfo namelist: ' + write (6,93) structflag + write (6,95) ikeflag + 93 format ('Structure flag = ',a1) + 95 format ('IKE flag = ',a1) + + print *,' ' + print *,'Values read in for grib file name from fnameinfo' + & ,' namelist: ' + write (6,131) gmodname + write (6,133) rundescr + write (6,135) atcfdescr + 131 format ('Model name description = gmodname = ',a4) + 133 format ('Forecast run description = rundescr = ',a40) + 135 format ('Optional ATCF / Storm name description = atcfdescr = ' + & ,a40) + + print *,' ' + print *,'Value read in from verbose namelist:' + write (6,141) verb + 141 format ('Value read in for verbose flag = verb = ',i2) + + print *,' ' + print *,'Values read in from waitinfo namelist:' + write (6,151) use_waitfor + write (6,152) wait_min_age + write (6,153) wait_min_size + write (6,154) wait_max_wait + write (6,155) wait_sleeptime + if(len_trim(per_fcst_command)>0) then + write (6,156) trim(per_fcst_command) + else +c No command specified, so disable the feature + use_per_fcst_command='n' + endif + 151 format ('Flag for input file waiting = use_waitfor = ',a1) + 152 format ('min age (time in seconds since last mod) = ' + & ,'wait_min_age = ',i8) + 153 format ('min file size in bytes = wait_min_size = ',i12) + 154 format ('max number of seconds to wait for each file = ' + & ,'wait_max_wait = ',i6) + 155 format ('number of seconds to sleep between checks = ' + & ,'wait_sleeptime = ',i6) + 156 format ('command to run after every forecast time = "',A,'"') +c + if (use_waitfor == 'y') then + if (inp%file_seq == 'multi') then + continue + else + print *,' ' + print *,'!!! ERROR: The use_waitfor flag is set to "y".' + print *,' This requires that the inp%file_seq flag be' + print *,' set to "multi", but you have specified ' + print *,' something else. ' + print *,' inp%file_seq = ',inp%file_seq + print *,' STOPPING....' + print *,' ' + STOP 95 + endif + endif +c + endif + return + end +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_fhours (ifhmax) +c +c ABSTRACT: This subroutine reads in a text file that contains the +c forecast times that will be read in. The format of the file is +c in "MMMMM", i.e., minutes, for example, for a forecast going out +c to 120h, the file would look like this: +c +c For reference, here +c are the times that +c match up with the +c minutes on the left: +c +c 1 0 0:00 +c 2 240 4:00 +c 3 270 4:30 +c 4 300 5:00 +c 5 330 5:30 +c 6 360 6:00 +c 7 600 10:00 +c 8 630 10:30 +c 9 660 11:00 +c 10 690 11:30 +c 11 720 12:00 +c 12 960 16:00 +c 13 990 16:30 +c . . . +c . . . +c . . . +c 87 7200 120:00 +c +c Note that we are now allowing for sub-hourly time intervals. +c + USE tracked_parms + USE verbose_output + + implicit none +c + integer, parameter :: iunit_fh=15 + integer itmphrs(750),itmpmins(750),input_mins(750),itmpltix(750) + integer ifhmax,inphr,inpmin,ict,i,ifa,ifma,icma,ira,inpltix,ila + real xminfract + + itmphrs = -99 + itmpmins = -99 + + if (allocated(ifhours)) deallocate (ifhours) + if (allocated(iftotalmins)) deallocate (iftotalmins) + if (allocated(ifclockmins)) deallocate (ifclockmins) + if (allocated(fhreal)) deallocate (fhreal) + if (allocated(ltix)) deallocate (ltix) + + ict = 0 + do while (.true.) + + if ( verb .ge. 3 ) then + print *,'Top of while loop in read_fhours' + endif + + read (iunit_fh,85,end=130) inpltix,inpmin + write (6,85) inpltix,inpmin + + if (inpmin >= 0 .and. inpmin < 150000) then + ict = ict + 1 + itmpltix(ict) = inpltix + itmphrs(ict) = inpmin / 60 + itmpmins(ict) = mod(inpmin,60) + input_mins(ict) = inpmin + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Input minutes not between 0 and 150000' + print *,'!!! inpmin= ',inpmin + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + if ( verb .ge. 3 ) then + print *,'readloop, ict= ',ict,' inpmin= ',inpmin + endif + + enddo + + 130 continue + + ifhmax = ict + + 85 format (i4,1x,i5) + + if ( verb .ge. 3 ) then + print *,' ' + endif + + allocate (ifhours(ifhmax),stat=ifa) + allocate (iftotalmins(ifhmax),stat=ifma) + allocate (ifclockmins(ifhmax),stat=icma) + allocate (fhreal(ifhmax),stat=ira) + allocate (ltix(ifhmax),stat=ila) + if (ifa /= 0 .or. ifma /= 0 .or. icma /= 0 .or. ira /= 0 .or. + & ila /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_fhours allocating either ifhours,' + print *,'!!! iftotalmins, ifclockmins or fhreal.' + print *,'!!! ifa = ',ifa,' ifma= ',ifma,' ira= ',ira + print *,'!!! icma= ',icma,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + do i = 1,ifhmax + + ltix(i) = itmpltix(i) + xminfract = float(itmpmins(i)) / 60. + fhreal(i) = float(itmphrs(i)) + xminfract + ifhours(i) = itmphrs(i) + ifclockmins(i) = itmpmins(i) + iftotalmins(i) = input_mins(i) + + if (i > 1) then + if (fhreal(i) > fhreal(i-1)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: In read_fhours, the time read in ' + print *,'!!! is not greater than the previous time.' + print *,'!!! i= ',i + print *,'!!! fhreal(i)= ',fhreal(i) + print *,'!!! fhreal(i-1)= ',fhreal(i-1) + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + endif + + if ( verb .ge. 3 ) then + write (6,87) i,ltix(i),iftotalmins(i),fhreal(i),ifhours(i) + & ,ifclockmins(i) + endif + + enddo + + 87 format (1x,'i= ',i3,' input lead time index= ',i4,' minutes= ' + & ,i5,' real_lead_time= ',f6.2,' clock_lead_time= ',i3,':' + & ,i2) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + ii=1 + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + maxstorm = numtcv + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that you have the Fortran' + print *,'!!! unit assigned right in your script.' + endif + + iret = 99 + return + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + ii = numtcv + 1 + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1,1x + & ,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo (imax,jmax,ifh,dx,dy,lugb,lugi,trkrinfo + & ,need_to_flip_lats,need_to_flip_lons,inp,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just read the +c grib file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE tracked_parms; USE inparms + USE verbose_output; USE params; USE grib_mod + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + + type(gribfield) :: gfld,prevfld,holdgfld + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1), allocatable :: lb(:) + logical :: unpack=.true. + logical :: open_grb=.false. + CHARACTER(len=8) :: pabbrev + integer,dimension(200) :: jids,jpdt,jgdt + integer, parameter :: jf=40000000 + integer :: listsec1(13) + integer pdt_4p0_vert_level,pdt_4p0_vtime + real xhold,xlondiff,xlatdiff,temp,firstval,lastval + real, allocatable :: f(:) + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer jpds(200),jgds(200),igetpds(200),igetgds(200) + integer, intent(in) :: ifh + integer, intent(out) :: imax,jmax + integer iia,ija,ila,midi,midj,i,j,iix,jix,ifa,iret + integer iscanflag,iggret,kf,k,lugb,lugi,jskp,jdisc + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 + + iggret = 0 + + allocate (lb(jf),stat=ila); allocate (f(jf),stat=ifa) + if (ila /= 0 .or. ifa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating either lb or f' + print *,'!!! ila = ',ila,' ifa= ',ifa + endif + iggret = 97 + return + endif + + if (trkrinfo%gribver == 2) then + + ! Search for a record from a GRIB2 file + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 ! meteorological products + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for Temperature or GP Height by production template.... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + ! Request a record on a lat/lon grid. + + jgdtn = 0 + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpdt(8) = 0 + jpdt(9) = iftotalmins(ifh) + else + jpdt(8) = 1 + jpdt(9) = ifhours(ifh) + endif + + print *,'before getgb2 call, lugb= ',lugb,' lugi= ',lugi + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + if ( iret.ne.0) then + print *,' ' + print *,' ERROR: getgb2 error in getgridinfo = ',iret + print *,' FATAL ERROR: cannot proceed without info ' + print *,' from getgridinfo. STOPPING....' + stop 95 + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if ( verb .ge. 3 ) then + print *,' ' + print *,' -- BEGIN getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb .ge. 3 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'PDT num= gfld%ipdtnum= ',gfld%ipdtnum + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + imax = gfld%igdtmpl(8) + jmax = gfld%igdtmpl(9) + dx = float(gfld%igdtmpl(17))/1.e6 + dy = float(gfld%igdtmpl(17))/1.e6 + kf = gfld%ngrdpts + + holdgfld = gfld + + if (verb .ge. 3) then + + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + + pdt_4p0_vtime = gfld%ipdtmpl(9) + +cJ.Peng------------------------------------------------ + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ndpts,firstval,lastval +c & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + + print *,' ' + print *,' -- END getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' ' + print *,' ' + + endif + + need_to_flip_lons = .false. + + iscanflag = gfld%igdtmpl(19) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(gfld%igdtmpl(12))/1.e6 + glatmax = float(gfld%igdtmpl(15))/1.e6 + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(gfld%igdtmpl(15))/1.e6 + glatmax = float(gfld%igdtmpl(12))/1.e6 + need_to_flip_lats = .false. + endif + + glonmin = float(gfld%igdtmpl(13))/1.e6 + glonmax = float(gfld%igdtmpl(16))/1.e6 + + print *,'TEST getgridinfo: glatmin= ',glatmin + print *,'TEST getgridinfo: glatmax= ',glatmax + print *,'TEST getgridinfo: glonmin= ',glonmin + print *,'TEST getgridinfo: glonmax= ',glonmax + + else + + ! Search for a record from a GRIB1 file + + jpds = -1 + jgds = -1 + + jgds(1) = 0 ! Request a record that's on a lat/lon grid + + if ( verb .ge. 3 ) then + print *,'before getgb in getgridinfo, ifh= ',ifh + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* Forecast hour: ',i4,':',i2.2) + print *,' ifhours(ifh)= ',ifhours(ifh) + print *,' iftotalmins(ifh)= ',iftotalmins(ifh) + endif + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + j=0 + + call getgb(lugb,lugi,jf,j,jpds,jgds, + & kf,k,igetpds,igetgds,lb,f,iret) + + if (iret.ne.0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo calling getgb' + print *,'!!! Return code from getgb = iret = ',iret + endif + + iggret = iret + else + iggret=0 + imax = igetgds(2) + jmax = igetgds(3) + dx = float(igetgds(9))/1000. + dy = float(igetgds(10))/1000. + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,' dx= ',dx,' dy= ',dy + endif + + +c ---------------------------------------------------------------- +c Get boundaries of the data grid. NOTE: gds(4) is referred to in +c GRIB documenatation as the "Latitude of origin", which might +c imply "minimum Latitude". However, for the grids that we'll be +c using in this program, the "Latitude of origin" will be listed +c under gds(4) as the northernmost point (eg., in MRF, +c gds(4) = 90), so for this program, use gds(4) as your max lat, +c and gds(7) as your min lat. However, in case NCEP, UKMET or +c ECMWF change their convention and begin flipping their grids, a +c check is made to make sure that the max lat is not less than the +c min lat. +c +c BUGFIX (August, 2001): It is possible to have an input grid +c which goes from south to north (such as NOGAPS). In this case, +c we flip the data in subroutine conv1d2d_real. However, the max +c and min latitudes listed in the GRIB GDS will be confused, so we +c need to check the value of the GRIB scanning mode flag here. + + need_to_flip_lons = .false. + + iscanflag = igetgds(11) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(igetgds(4))/1000. + glatmax = float(igetgds(7))/1000. + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(igetgds(7))/1000. + glatmax = float(igetgds(4))/1000. + need_to_flip_lats = .false. + endif + + glonmin = float(igetgds(5))/1000. + glonmax = float(igetgds(8))/1000. + + endif + +c After this point in this subroutine, nothing is GRIB1 / GRIB2 +c specific, so it does not need to be within the if/then +c statement above that differentiated between GRIB / GRIB2. + + if (glonmin < 0.0) glonmin = 360. - abs(glonmin) + if (glonmax < 0.0) glonmax = 360. - abs(glonmax) + + if (glonmin < 0.0) then + glonmin = 360. - abs(glonmin) + if (glonmax <= 0.0) then + glonmax = 360. - abs(glonmax) + else + glonmax = 360 + abs(glonmax) + endif + endif + + if (glatmax < glatmin) then + temp = glatmax + glatmax = glatmin + glatmin = temp + endif + + if (glonmin > 200.0 .and. glonmin <= 360.) then + if (glonmax < 50.) then + ! Likely GM-wrapping in current record + glonmax = glonmax + 360. + endif + endif +c + if ( verb .ge. 3 ) then + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + print *,' ' + print *,'NOTE: For regional grids, valid data points might' + print *,'NOT extend all the way to the gds-defined grid ' + print *,'boundary, due to the fact that data have been ' + print *,'interpolated from a NPS or Lamb-Conf grid onto a ' + print *,'lat/lon grid. This program checks the logical ' + print *,'bitmap for valid data points, but just keep this in' + print *,'mind if trying to debug errors that occur near the' + print *,'grid boundaries for regional models.' + endif + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glat)) deallocate(glat) + if (allocated(glon)) deallocate(glon) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + endif + + iggret = 96 + return + endif + + do j=1,jmax + glat(j) = glatmax - (j-1)*dy + enddo + do i=1,imax + glon(i) = glonmin + (i-1)*dx + enddo + + deallocate (lb); deallocate(f) + +c -------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have +c forgotten to change the input grid bounds from a global grid +c run). Modify the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_valid_point (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) +c +c ABSTRACT: This subroutine checks to see if the input lat/lon +c point is associated with four surrounding (i,j) locations that +c have valid data. The writing of this routine was prompted by the +c HFIP project in February, 2009. Some of their high resolution +c data for their inner nests contained grids that had been rotated +c from native map projections to regular lat/lon grids, but that +c rotation left "empty" spots on the lat/lon grid where there is +c no data. Then when searching in find_maxmin, we were running +c barnes iterations from these lat/lon locations where there was +c no data, which would give artificially low values at those +c lat/lon locations (because the barnes scheme would only include +c points that were relatively far away where there was valid data). +c So in this routine, we call subroutine fix_latlon_to_ij in order +c to get the nearest (i,j) coordinates, and then we check all of +c these points to make sure that valid data exist. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing in i-direction +c dy grid spacing in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c rlatt,rlont input lat/lon about which we will check the +c surrounding (i,j) locations for valid data. +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c icvpret return code from this routine. A value of 0 means that +c all is okay and the input point is surrounded by valid +c data. + + USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,ifix,jfix + integer ifilret,icvpret + character(*) cmaxmin + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real rlont,rlatt,xdum,gridpoint_maxmin + real dx,dy,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +c + call fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt + & ,xdum,ifix,jfix,gridpoint_maxmin,'checker' + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) + + if (ifilret /= 0) then + icvpret = 99 + return + endif + + if (valid_pt(ifix,jfix)) then + icvpret = 0 + else + icvpret = 99 + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.10) then + grfact = 4 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif + + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (1*grfact) + iend = ipfix + (2*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (2*grfact) + iend = ipfix + (1*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (1*grfact) + iend = ipfix + (1*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (2*grfact) + jend = jpfix + (1*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (1*grfact) + jend = jpfix + (2*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (1*grfact) + jend = jpfix + (1*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +c print *,' End of fix_latlon_to_ij, gridpoint_maxmin = ' +c & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine rvcal (imax,jmax,dlon,dlat,z,vp) +c +c ABSTRACT: This routine calculates the relative vorticity (zeta) +c from u,v on an evenly-spaced lat/lon grid. Centered finite +c differences are used on the interior points and one-sided +c differences are used on the boundaries. +c +c NOTE: There are 3 critical arrays in this subroutine, the first +c being zeta and the 2nd and 3rd being u and v. There is a +c critical difference in the array indexing for the levels. For +c zeta, the array is dimensioned with levels from 1 to 3, with +c 1 = 850, 2 = 700, 3 = sfc. However, there is an extra level +c for the winds, such that the level dimension goes 1 = 850, +c 2 = 700, 3 = 500, 4 = sfc. So we need to adjust for that in +c this routine. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE trig_vals; USE grid_bounds + USE verbose_output + + implicit none + + dimension cosfac(jmax),tanfac(jmax) + real tmpzeta(imax,jmax) + real xlondiff,xlatdiff,dlon,dlat,dfix + real dlat_edge,dlat_inter,dlon_edge,dlon_inter + real rlat(jmax),cosfac,tanfac + integer z,iscanflag,nlat,nlon,i,j,imax,jmax,w + integer ii,jj + logical(1) vp(imax,jmax) + +c -------------------------- + +c Figure out what level of data we have and what the array +c indices should be. + + if (z == 1) then + ! z = 1 for 850 mb zeta, w = 1 for 850 mb winds + w = 1 + else if (z == 2) then + ! z = 2 for 700 mb zeta, w = 2 for 700 mb winds + w = 2 + else if (z == 3) then + ! z = 3 for sfc zeta, w = 4 for sfc (10m) winds + w = 4 + endif + +c Calculate grid increments for interior and edge points. + +c IMPORTANT: If dtk is defined in module trig_vals in km, then +c we need to multiply by 1000 here to get meters. If it's defined +c as meters, just let it be. Since the wind values are given in +c meters, that's why we need the dlon values to be in meters. + + if (dtk < 750.) then ! chances are, dtk was defined as km + dfix = 1000.0 + else ! dtk was already defined as meters + dfix = 1.0 + endif + + dlon_edge = dtk * dfix * dlon ! Di dist over 1 grid pt + dlat_edge = dtk * dfix * dlat ! Dj dist over 1 grid pt + dlon_inter = dtk * dfix * 2.0 * dlon ! Di dist over 2 grid pts + dlat_inter = dtk * dfix * 2.0 * dlat ! Dj dist over 2 grid pts + + +c Calculate required trig functions. These are functions of +c latitude. Remember that the grid must go from north to south. +c This north-to-south requirement has +c already been checked in subroutine getgridinfo. If necessary, +c any flipping of the latitudes was done there, and flipping of +c the data, again if necessary, was done in subroutine getdata. + + do j=2,jmax-1 + rlat(j) = glatmax - ((j-1) * dlat) + cosfac(j) = cos(dtr*rlat(j)) + tanfac(j) = (tan(dtr*rlat(j)))/erad + enddo + +c Set trig factors at end points to closest interior point +c to avoid a singularity if the domain includes the poles, +c which it will for the global grids (MRF, GDAS, GFS, UKMET,NCE) + + cosfac(1) = cosfac(2) + tanfac(1) = tanfac(2) + cosfac(jmax) = cosfac(jmax-1) + tanfac(jmax) = tanfac(jmax-1) + +c NOTE: These next bits of vorticity calculation code assume that +c the input grid is oriented so that point (1,1) is the upper +c left-most (NW) and point (imax,jmax) is the lower right- +c most point. Any other grids will probably crash the +c program due to array out of bounds errors. +c NOTE: Before each calculation is done, the logical array is +c checked to make sure that all the data points in this +c calculation have valid data (ie., that the points are not +c outside a regional model's boundaries). +c +c !!! IMPORTANT NOTE: While testing this, I uncovered a bug, which was +c that I had the "j+1" and "j-1" reversed. Just from a physical +c understanding, the du/dy term at a point is calculated by taking +c the u value north of the point minus the u value south of the +c point. Intuitively, this is u(j+1) - u(j-1). However, we have +c designed this program to have the northernmost point as +c the beginning of the grid (i.e., for the global grids, j=1 at 90N, +c and j increases southward). Thus, if you would do u(j+1) - +c u(j-1), you would actually be taking the u value south of the +c point minus the u value north of the point, EXACTLY THE OPPOSITE +c OF WHAT YOU WANT. Therefore, the vorticity calculations have +c been changed so that we now have u(j-1) - u(j+1). +c +c UPDATE FEB 2009: With limited domain grids that have missing +c data on them (such as you would have for a grid that has been +c converted from a non-lat/lon grid to a lat/lon grid), we were +c running into problems below with the setting of zeta values to +c a missing value of -999. In place of this, the easiest thing to +c do is to simply assign a value of the background coriolis value +c to that point. No, this is not correct, but it is the easiest +c workaround for this right now. Setting it to zero would be too +c far off. Setting it to the coriolis component has a net effect +c of not having much impact on the barnes scheme result. +c +c --------------- +c Interior points +c --------------- + + if ( verb .ge. 3 ) then + print *,'Just before inter rvcalc, dlon_inter = ',dlon_inter + & ,' dlat_inter = ',dlat_inter + endif + + do j=2,jmax-1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1) .and. vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo + enddo +c +c ----------------------------- +c Bottom (Southernmost) points +c ----------------------------- +c + j=jmax + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------- +c Top (Northernmost) points +c -------------------------- +c + j=1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c ------------------------------- +c Left edge (Westernmost) points +c ------------------------------- +c + i=1 + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------------- +c Right edge (Easternmost) points +c -------------------------------- +c + i=imax + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c --------- +c SW corner +c --------- + i=1 + j=jmax + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i+1,j,w)-v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NW corner +c --------- + i=1 + j=1 + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NE corner +c --------- + i=imax + j=1 + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c SE corner +c --------- + i=imax + j=jmax + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i,j,w)-v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + + do ii=1,imax + do jj=1,jmax + tmpzeta(ii,jj) = zeta(ii,jj,z) * 1.e5 + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine first_ges_center (imax,jmax,dx,dy,cparm,fxy + & ,cmaxmin,trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) +c +c ABSTRACT: This subroutine scans an array and picks out areas of +c max or min, then loads those center positions into the first- +c guess lat & lon arrays to be used by subroutine tracker for +c locating the very specific low center positions. +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c dx Grid spacing in i-direction for the input grid +c dy Grid spacing in j-direction for the input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c finf Logical. Field of influence. Dimension same as fxy +c cmaxmin Char string to indicate if search is for a max or a min +c trkrinfo Derived type that holds/describes various tracker parms, +c including the contour interval to be used +c ifh Index for the forecast hour +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c contour_info Type cint_stuff from module contours. Contains +c contour information +c +c OUTPUT: +c maxmini Integer array containing i-indeces of max/min locations +c maxminj Integer array containing j-indeces of max/min locations +c ifgcret return code from this subroutine +c +c OTHER: +c storm Contains the tcvitals for the storms (module def_vitals) + + USE trkrparms; USE grid_bounds; USE set_max_parms; USE def_vitals + USE contours; USE tracked_parms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,n,isstart,ifamret,ibeg,jbeg,iend,jend + integer ifh,maxstorm,imax,jmax,itemp,ifgcret + integer stormct,oldstormct + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + character(*) cparm,cmaxmin + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real dmax,dmin,dx,dy,dbuffer,tmp + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of first_ges_center *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for new lows at hour ',i4,':',i2.2) + print *,'*-------------------------------------------------*' + endif + + +c First check the user-supplied grid boundaries to see if we will +c scan the entire array or just a portion of it. + + if (trkrinfo%northbd < -998.0 .or. trkrinfo%southbd < -998.0 .or. + & trkrinfo%westbd < -998.0 .or. trkrinfo%eastbd < -998.0) then + ! User did not specify a subgrid, so scan the whole domain + ibeg = 1 + iend = imax + jbeg = 1 + jend = jmax + else + +c if (trkrinfo%westbd > 360.0 .or. trkrinfo%eastbd < 0.0 .or. +c & trkrinfo%westbd < 0.0 .or. + + if (trkrinfo%westbd > 360.0 .or. + & trkrinfo%northbd > 90.0 .or. trkrinfo%northbd <-90.0 .or. + & trkrinfo%southbd > 90.0 .or. trkrinfo%southbd <-90.0 .or. + & trkrinfo%westbd >= trkrinfo%eastbd .or. + & trkrinfo%southbd >= trkrinfo%northbd) then + + if (trkrinfo%westbd > trkrinfo%eastbd) then + + if (trkrinfo%westbd < 360.0 .and. + & trkrinfo%eastbd >= 0.0)then + + ! In this special case, the user has specified that the + ! western boundary be to the west of the Greenwich + ! meridian and the eastern boundary be to the east of it. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE: The user supplied grid lon boundaries' + print *,'++ span across the Greenwich meridian.' + print *,'++ ' + print *,'++ Western boundary: ',trkrinfo%westbd + print *,'++ Eastern boundary: ',trkrinfo%eastbd + print *,'++ Northern boundary: ',trkrinfo%northbd + print *,'++ Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ! Calculate the beginning and ending i and j points for + ! this case of spanning the Greenwich meridian. The + ! beginning and ending j points are, obviously, the same + ! as for the regular case below in the else. The + ! i-beginning point will also be the same as for the + ! regular case. However, the i-ending point will be + ! modified for the meridian wrap; it will be > imax. + + jbeg = int(((glatmax + dy - trkrinfo%northbd) + & / dy) + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) + & / dy) + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) + & / dx) + 0.5) +c iend = int(((trkrinfo%eastbd - glonmin + dx) +c & / dx) + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) + & / dx) + 0.5) + imax + + goto 377 + + endif + endif + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. There is a' + print *,'!!! problem with the user-supplied grid ' + print *,'!!! boundaries. Please check them and ' + print *,'!!! resubmit the program.' + print *,'!!!' + print *,'!!! Western boundary: ',trkrinfo%westbd + print *,'!!! Eastern boundary: ',trkrinfo%eastbd + print *,'!!! Northern boundary: ',trkrinfo%northbd + print *,'!!! Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ifgcret = 91 + return + + 377 continue + + else + ! Calculate the beginning and ending i and j points.... + jbeg = int(((glatmax + dy - trkrinfo%northbd) / dy) + & + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) / dy) + & + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) / dx) + & + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) / dx) + & + 0.5) + endif + endif + +c Scan the requested portion of the grid and pick out the max and +c min data values, figure out what the max and min contour levels +c will be, and fill an array with the values of the various +c intermediate, incremental contour levels. + + if (trkrinfo%contint <= 0) then + + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. For a midlat' + print *,'!!! or tcgen run of the tracker, the contour' + print *,'!!! interval supplied by the user is not ' + print *,'!!! greater than 0.' + print *,'!!! ' + print *,'!!! User-supplied contint = ',trkrinfo%contint + print *,' ' + endif + + ifgcret = 91 + return + endif + + dmin = 9.99e20 + dmax = -9.99e20 + + do j = jbeg,jend + do i = ibeg,iend + if (i > imax) then + itemp = i - imax ! If wrapping past GM + else + itemp = i + endif + if (valid_pt(itemp,j)) then + if (fxy(itemp,j) < dmin) dmin = fxy(itemp,j) + if (fxy(itemp,j) > dmax) dmax = fxy(itemp,j) + endif + enddo + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*--------------------------------------------*' + print *,'In first_ges_center, dmin= ',dmin,' dmax= ',dmax + endif + + +c We want to allow for storms moving out of the sub-region, +c in which case we might hit slightly lower or higher +c contours than were found in the sub-region, so allow for +c an extra buffer and modify dmin and dmax.... + + dbuffer = (dmax - dmin) / 2.0 + dmax = dmax + dbuffer + dmin = dmin - dbuffer + + if ( verb .ge. 3 ) then + print *,'after adjustment, dmin= ',dmin,' dmax= ',dmax + endif + +c Next 2 lines changed for compiler compatibility on +c other platforms.... +c contour_info%xmaxcont = dmax - amod(dmax,trkrinfo%contint) +c contour_info%xmincont = dmin - amod(dmin,trkrinfo%contint) + + tmp = trkrinfo%contint + contour_info%xmaxcont = dmax - mod(dmax,tmp) + contour_info%xmincont = dmin - mod(dmin,tmp) + + if ( verb .ge. 3 ) then + print *,'A1 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A1 contour_info%xmincont= ',contour_info%xmincont + endif + + if (contour_info%xmincont > contour_info%xmaxcont) then + contour_info%xmincont = contour_info%xmaxcont + endif + +c if (dmin > contour_info%xmincont) then +c contour_info%xmincont=contour_info%xmincont + trkrinfo%contint +c endif +c if (dmax < contour_info%xmaxcont) then +c contour_info%xmaxcont=contour_info%xmaxcont - trkrinfo%contint +c endif + + if ( verb .ge. 3 ) then + print *,'A2 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A2 contour_info%xmincont= ',contour_info%xmincont + print *,'maxconts= ',maxconts + endif + +c NOTE: In the loop below, the contour_info%contvals array is now +c (5/2003) no longer used in subsequent subroutines. But we still +c need to figure out the value of the contvals as we iterate the +c loop so we can know when we've surpassed dmax and can stop +c incrementing contour_info%numcont, which we do need in subsequent +c subroutines. + + contour_info%numcont = 0 + do n = 1,maxconts + contour_info%numcont = contour_info%numcont + 1 + contour_info%contvals(n) = contour_info%xmincont + + & float(n-1)*trkrinfo%contint +c print *,'n= ',n,' contour_info%contvals(n)= ' +c & ,contour_info%contvals(n) + if (contour_info%contvals(n) >= dmax) exit + enddo + + oldstormct = stormct + call find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) + + if (stormct > 0) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' ' + print *,'!!! ************************************************' + print *,'!!! ' + print *,'!!! NOTE: In first_ges_center, the value of stormct' + print *,'!!! returned from find_all_maxmins is not greater' + print *,'!!! than 0. This means there are no new centers' + print *,'!!! to track, which is not likely. Perhaps you are' + print *,'!!! searching over too small of an area??' + print *,'!!! ' + print *,'!!! ************************************************' + print *,' ' + endif + + endif + + if (stormct > oldstormct .and. stormct > 0) then + isstart = oldstormct + 1 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,*) 'New search: ' + write (6,*) 'Possible new max/min locations at ifh= ',ifh + write (6,*) '--------------------------------------------' + endif + + do n = isstart,stormct + if (trkrinfo%type == 'midlat') then + storm(n)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(n)%tcv_center = 'TCG ' + endif + slonfg(n,ifh) = glonmin + (maxmini(n)-1)*dx + slatfg(n,ifh) = glatmax - (maxminj(n)-1)*dy + storm(n)%tcv_stspd = -99 + storm(n)%tcv_stdir = -99 + write (storm(n)%tcv_storm_id,'(i4.4)') n + write (storm(n)%tcv_storm_name,'(i4.4)') n + stormswitch(n) = 1 + if (cparm == 'mslp') then + + if ( verb .ge. 3 ) then + write (6,71) maxmini(n),maxminj(n),slonfg(n,ifh) + & ,360.-slonfg(n,ifh),slatfg(n,ifh) + & ,slp(maxmini(n),maxminj(n))/100.0 + endif + + endif + enddo + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' New search: ' + print *,'!!! NOTE: No new storms found in find_all_maxmins' + print *,'!!! at ifh = ',ifh,' stormct= ',stormct + print *,'!!! oldstormct= ',oldstormct + print *,' ' + endif + + endif + + 71 format (1x,'i= ',i3,' j= ',i3,' lon: ',f7.2,'E (',f6.2,'W)' + & ,2x,' lat: ',f6.2,' mslp: ',f6.1,' mb') +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) +c +c ABSTRACT: This subroutine will search an area delineated by +c input i and j indeces in order to find all local maxes or mins +c in that area. The (i,j) locations of the maxes/mins are returned +c in the maxmini and maxminj arrays. The input 3-character string +c cmaxmin will tell the subroutine to look for a "max" or a "min". +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c ibeg i-index for upper left location of grid to search +c iend i-index for lower right location of grid to search +c jbeg j-index for upper left location of grid to search +c jend j-index for lower right location of grid to search +c fxy Real array of data values +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c contour_info Type cint_stuff from module contours containing the +c the following 4 variables: +c 1. xmincont Real value for min contour level in the fxy data array +c 2. xmaxcont Real value for max contour level in the fxy data array +c 3. contvals Real array holding values of cont levels at this time +c 4. numcont Number of contour intervals found at this time +c trkrinfo derived type containing various user-input tracker parms +c cmaxmin String that declares if "min" or "max" is being searched +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c +c OUTPUT: +c maxmini integer array containing i-indeces of the max/min points +c maxminj integer array containing j-indeces of the max/min points +c ifamret return code from this subroutine + + USE trkrparms; USE set_max_parms; USE contours + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + integer stormct,i,j,ibeg,iend,jbeg,jend,ix,jx,ixp1,ixm1 + integer ip,jp,maxstorm,jxp1,jxm1,ifamret,isret,iaret + integer isoiret,icccret,igicwret,imax,jmax + character ccflag*1,get_last_isobar_flag*1 + character(*) cmaxmin + logical(1) still_finding_valid_maxmins,rough_gradient_check_okay + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real xavg,stdv,search_cutoff,dmin,dmax,sphere_cutoff + real plastbar,rlastbar +c----- + still_finding_valid_maxmins = .true. + + +c print *,'ctm beg of find_all_maxmins, maxstorm= ',maxstorm + + +c First, we want to get the mean and standard deviation of the input +c field to be searched. We can use the standard deviation info as +c part of our guideline for when to stop searching for maxes & mins. +c We will set the search cut-off threshold at 1/2 standard deviation +c above the mean for min searches. So, for the example of mslp, if +c the mean pressure over the whole domain is 1010 mb and the +c standard deviation is 12 mb, then when we are searching, if the +c lowest available (i.e., hasn't been found in a previous iteration +c of this loop) pressure is 1016, then it's time to stop searching. + + call avgcalc (fxy,imax*jmax,valid_pt,xavg,iaret) + call stdevcalc (fxy,imax*jmax,valid_pt,xavg,stdv,isret) + if (iaret /= 0 .or. isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_all_maxmins, the calls to avgcalc' + print *,'!!! and/or stdevcalc returned an error.' + print *,'!!! iaret= ',iaret,' isret= ',iaret + print *,' ' + endif + + ifamret = 98 + return + endif + + if (cmaxmin == 'min') then + search_cutoff = xavg + stdv*0.5 + else + search_cutoff = xavg - stdv*0.5 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In find_all_maxmins, search_cutoff= ',search_cutoff + print *,' ' + endif + +c Now begin to search the domain. We do a simple gridpoint scan, +c and once we find the max/min value, we pass the (i,j) coordinates +c at that point to a routine to check for a closed contour. Then +c we mask out those points in the contour (or, if there is not a +c closed contour, just the 8 points immediately surrounding the low +c center) and we do another iteration of search_loop to look for +c more lows. We mask out points we've found so that on subsequent +c iterations of search_loop, we don't find the same old center +c again and again and again..... + + search_loop: do while (still_finding_valid_maxmins) + + dmin = 9.99e20 + dmax = -9.99e20 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + ip = i + jp = j + + if (ip > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: In find_all_maxmins, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. The search' + print *,'!!! will not extend to the user-requested' + print *,'!!! grid boundary.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',ip + print *,' ' + endif + + exit iloop + + endif + endif + + if (valid_pt(ip,jp) .and..not. masked_out(ip,jp)) then + if (cmaxmin == 'min') then + if (fxy(ip,jp) < dmin) then + dmin = fxy(ip,jp) + ix = ip + jx = jp + endif + else + if (fxy(ip,jp) > dmax) then + dmax = fxy(ip,jp) + ix = ip + jx = jp + endif + endif + endif + + enddo iloop + enddo jloop + + if (cmaxmin == 'min') then + if (dmin < search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + else + if (dmax > search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + endif + +c As a rough first check, see if the neighboring points on all +c 4 sides have a gradient sloping down into the found min point, +c or at least that there is a flat field not having a gradient +c sloping away from the center point. + + call get_ijplus1_check_wrap (imax,jmax,ix,jx,ixp1,jxp1,ixm1 + & ,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In find_all_maxmins, the center we found' + print *,'!!! is too close to the grid boundary and will' + print *,'!!! NOT be checked for a closed contour.' + print *,'!!! ix= ',ix,' jx= ',jx,' fxy= ',fxy(ix,jx) + print *,'!!! ' + print *,' ' + endif + + masked_out(ix,jx) = .true. + cycle search_loop + endif + + if (cmaxmin == 'min') then + if (fxy(ix,jx) <= fxy(ixp1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxm1) .and. + & fxy(ix,jx) <= fxy(ixm1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + else + if (fxy(ix,jx) >= fxy(ixp1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxm1) .and. + & fxy(ix,jx) >= fxy(ixm1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + endif + + if (rough_gradient_check_okay) then + + if ( verb .ge. 3 ) then + print *,'Found a possible max/min at ix= ',ix,' jx= ',jx + endif + + +c From this rough check, we appear to have a gradient sloping +c in towards the center point. Now call the subroutine to +c check whether or not there is in fact a closed contour +c surrounding this local maximum or minimum. + + get_last_isobar_flag = 'n' + ccflag = 'n' + call check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,ccflag,cmaxmin,trkrinfo + & ,1,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if (ccflag == 'y') then + if (stormct < maxstorm) then + stormct = stormct + 1 + + if ( verb .ge. 3 ) then + print *,'AAA stormct= ',stormct,' ix= ',ix,' jx= ',jx + endif + + maxmini(stormct) = ix + maxminj(stormct) = jx + else + + if ( verb .ge. 3 ) then + print *,'---max stormct reached, stormct= ', stormct + endif + + endif + else + + if ( verb .ge. 3 ) then + print *,'!!! contour check negative, ccflag= ',ccflag + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*-----------------------------------------------*' + print *,' ' + endif + + endif + +c Regardless of whether or not the found point turns out to have +c a closed contour, we don't want to find this local minimum or +c its 8 surrounding points again in a search on a subsequent +c iteration of this loop. + + masked_out(ix,jx) = .true. + masked_out(ix,jxp1) = .true. + masked_out(ixp1,jxp1) = .true. + masked_out(ixp1,jx) = .true. + masked_out(ixp1,jxm1) = .true. + masked_out(ix,jxm1) = .true. + masked_out(ixm1,jxm1) = .true. + masked_out(ixm1,jx) = .true. + masked_out(ixm1,jxp1) = .true. + + enddo search_loop + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,closed_contour,cmaxmin,trkrinfo + & ,num_requested_contours,contour_info + & ,get_last_isobar_flag,plastbar,rlastbar,icccret) +c +c ABSTRACT: This subroutine checks a field of data around an input +c (ix,jx) data point to see if a closed contour exists around +c that data point. It can check for a closed contour on a max or a +c min field, depending on the value of the input variable 'cmaxmin'. +c The algorithm works by examining rings of the 8 data points +c surrounding a data point that is in the contour interval. For +c example, in the diagram below, the X represents the location of +c the local minimum value which was passed into this routine with +c the coordinates (ix,jx), let's say it's 985 mb. And let's assume +c that the data values at points A-I are all in the 4 mb contour +c interval of 985-989 mb, and that all the surrounding points have +c data values >= 989. To test for a closed contour, we first check +c the ring of 8 points immediately around point X to see what their +c data values are. If a data value is found that is below the +c lower limit of this contour interval (985 mb) or lower than the +c local minimum value at the X point that we initially targeted +c (985 mb), then we do NOT have a closed contour, and we exit this +c subroutine. But in our example, that's not the case, and we have +c 5 points (B,D,E,F,G) that are in the interval. So in our next +c iteration of the loop, we set up 5 rings, each one set up around +c the points found in the first iteration (B,D,E,F,G), and we check +c the 8 points around each of those points. A logical array is +c used so that as soon as a point is found, it is flagged as being +c found. In this way, when we look at the ring around point D, for +c example, we won't pick point X again and set up another ring +c around it in the next ring iteration and end up in an infinite +c loop, going back and forth between point X and point D. While +c checking the 8 points in a ring, if a found data value is above +c our contour interval (i.e., >= 989 mb), we just ignore the +c point; we only mark points that are in our contour interval, +c and again, if we find a point below our contour interval, we +c exit the subroutine with a flag indicating a closed contour was +c NOT found. So in this method, we keep spreading out from the +c initial local minimum and creating and checking new rings until +c we either: (a) Hit the edge of the regional grid, in which case +c we consider a closed contour NOT found, (b) Run into a data +c point that has been marked as being under the influence of +c another nearby low, in which case we consider a closed contour +c NOT found, (c) Run into a point which is below (above) our +c contour interval for a min (max) check, in which case we +c consider a closed contour NOT found, or (d) we run out of +c points to keep searching, we have no rings left to create and +c check because all of the surrounding points are above (below) +c our contour interval for a min (max) check, and by default we +c consider this a closed contour and return to the calling +c subroutine a flag indicating such. +c +c + + + + + + + + + + +c + + + + + + + + + + +c + + A B + + + + + + +c + + C D X E + + + + +c + + + + F G + + + + +c + + + + + H I + + + +c + + + + + + + + + + +c + + + + + + + + + + +c +c UPDATE: This subroutine was updated to keep searching for +c multiple closed contours until it can't find anymore. The +c input parameter num_requested_contours dictates how many +c contours to search for. In the case of just trying to roughly +c locate new centers and establish that there is a closed +c circulation, num_requested_contours will = 1, and we will exit +c after finding that 1 contour. But for a check after making a +c full center fix, we set num_requested_contours = 999 so that +c we can keep searching for all closed contours around the low. +c In this 999 case, you will eventually get to a point where +c there is no closed contour. In that case, in the standard +c output you will see a message telling you that you hit a point +c that is not in the contour and that there is no closed contour, +c but you will also notice that the ccflag = y, meaning there is +c a closed contour (because you have found at least 1 closed +c contour along the way). The reason to keep searching for more +c closed contours is that we can then return the value of the +c outermost closed isobar. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c cmaxmin character string ('max' or 'min') that tells this +c routine what we're looking for. +c trkrinfo derived type that holds/describes various tracker parms +c contour_info Type cint_stuff from module contours. Contains +c contour information +c num_requested_contours For the simple first_ges_center check, +c this will be 1 (we just want to know if there's at +c least 1 closed contour). For the verifying check after +c we've found a center, this will be 9999 (i.e., just keep +c searching for more contours) +c get_last_isobar_flag character ('y' or 'n') to indicate whether +c or not to report on the value of the last closed isobar +c and the radius of the last closed isobar. +c +c OUTPUT: +c closed_contour character; A returned value of 'y' indicates that +c this routine was able to find a closed contour. +c plastbar Contains the value of the last closed isobar (unrounded) +c rlastbar Contains the mean radius of the last closed isobar +c +c LOCAL: +c num_pts_in_all_contours Counter for the number of pts inside of +c the contour we're looking at +c next_ring_ct Counter for the number of points that have been +c tagged to be used as center points for the next +c iteration of multiple_ring_loop. +c next_contour_ct Counter for the number of points that have been +c tagged to be used as center points in the first iteration +c through single_contour_scan_loop as we begin to scan +c points in the *next* contour interval. This counter gets +c incremented when, for example, we are searching points +c around a current center point and we find one that is not +c in our current interval, but rather is in the next +c interval. We want to remember this point and store the +c location, so we increment this counter and store the +c location in next_contour_i and next_contour_j arrays. +c beyond_contour_ct Counter for the number of points that have been +c tagged to be used as center points for some subsequent +c iteration of successive_contours_loop. This is +c different from next_contour_ct, which is used to hold +c the locations of points that are definitely in the +c *next* contour interval. Here, we have points that we +c just store in a pool of potential points to be searched +c in future iterations. These points can come about in +c cases where there is a very intense, very compact low +c with a tight pressure gradient, such that multiple +c contour intervals could be spanned in between 2 adjacent +c gridpoints (this is especially the case if the contour +c interval you have chosen is small). You need to be +c careful with how you handle this array. Once you find +c that you have searchable points in next_contour_i or +c next_contour_j, do not just simply empty out this +c beyond_contour count and its i and j arrays. The +c reason being that some of these "beyond" points may end +c up being used and searched in subsequent iterations, but +c not if we just delete them now. + + + USE set_max_parms; USE trkrparms; USE contours; USE grid_bounds + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,ir,iria,irja,irx,jrx,ix,jx,imax,jmax + integer nb,ibx,jby,nct,iflip + integer mr,ringct,ixp1,ixm1,jxp1,jxm1,nring,iter + integer icenx,jcenx,icccret,next_ring_ct,igicwret + integer num_pts_in_all_contours,next_contour_ct + integer beyond_contour_ct + integer num_pts_in_one_contour + integer num_requested_contours,num_found_contours + integer nm,im,jm,inall,insingle,isc_count,rlast_distct + character found_a_point_in_our_contour*1,closed_contour*1 + character found_a_point_below_contour*1 + character found_a_point_above_contour*1,get_last_isobar_flag*1 + character(*) cmaxmin + logical(1) still_scanning + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + logical(1) point_is_already_in_our_contour(imax,jmax) + logical(1) point_is_already_in_next_contour(imax,jmax) + logical(1) point_is_already_in_beyond_pool(imax,jmax) + integer isni,isnj,inci,incj,ibci,ibcj,ihmi,ihmj,itmi,itmj + integer, allocatable :: search_next_i(:) + integer, allocatable :: search_next_j(:) + integer, allocatable :: next_contour_i(:) + integer, allocatable :: next_contour_j(:) + integer, allocatable :: beyond_contour_i(:) + integer, allocatable :: beyond_contour_j(:) + integer, allocatable :: hold_mask_i_loc(:) + integer, allocatable :: hold_mask_j_loc(:) + integer, allocatable :: temp_mask_i_loc(:) + integer, allocatable :: temp_mask_j_loc(:) + integer, allocatable :: ringposi(:),ringposj(:) + real,allocatable :: ringpos(:,:) + real fxy(imax,jmax),contvals(maxconts) + real contlo,conthi,xcentval,contlo_next,conthi_next + real dist,degrees,rlast_distsum,plastbar,rlastbar +c + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + allocate (search_next_i(imax*jmax),stat=isni) + allocate (search_next_j(imax*jmax),stat=isnj) + allocate (next_contour_i(imax*jmax),stat=inci) + allocate (next_contour_j(imax*jmax),stat=incj) + allocate (beyond_contour_i((imax*jmax)/2),stat=ibci) + allocate (beyond_contour_j((imax*jmax)/2),stat=ibcj) + allocate (hold_mask_i_loc(imax*jmax),stat=ihmi) + allocate (hold_mask_j_loc(imax*jmax),stat=ihmj) + allocate (temp_mask_i_loc(imax*jmax),stat=itmi) + allocate (temp_mask_j_loc(imax*jmax),stat=itmj) + if (isni /= 0 .or. isnj /= 0 .or. inci /= 0 .or. incj /= 0 .or. + & ibci /= 0 .or. ibcj /= 0 .or. ihmi /= 0 .or. ihmj /= 0 .or. + & itmi /= 0 .or. itmj /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various search, hold and temp arrays.' + print *,'!!! isni = ',isni,' isnj= ',isnj + print *,'!!! inci = ',inci,' incj= ',incj + print *,'!!! ibci = ',ibci,' ibcj= ',ibcj + print *,'!!! ihmi = ',ihmi,' ihmj= ',ihmj + print *,'!!! itmi = ',itmi,' itmj= ',itmj + print *,' ' + endif + + STOP 98 + endif + + closed_contour = 'n' + xcentval = fxy(ix,jx) + num_found_contours = 0 + next_contour_ct = 0 + beyond_contour_ct = 0 + num_pts_in_all_contours = 0 + hold_mask_i_loc = 0 + hold_mask_j_loc = 0 + beyond_contour_i = 0 + beyond_contour_j = 0 + point_is_already_in_our_contour = .false. + point_is_already_in_beyond_pool = .false. + icccret = 0 + isc_count = 0 + plastbar = -999.0 + rlastbar = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* Top of check_closed_contour, ix= ',ix,' jx= ',jx + print *,'*-----------------------------------------------*' + print *,' ' + print *,'fxy(ix,jx)= ',fxy(ix,jx),' xcentval= ',xcentval + endif + +c First, set up the contour intervals that will be used. In +c the original version of this code, we used preset +c standard intervals (984,988,992,996,1000,1004....). But upon +c further review, it was decided that this was too arbitrary. +c So instead, we consider the found min (max) value to be the +c bottom (top) of the list of contour intervals. In this way, +c we can clearly specify and screen storms based on the "depth" +c of the pressure field as compared to the surroundings. + + i = 1 + do while (i <= maxconts) + if (cmaxmin == 'min') then + contvals(i) = xcentval + float(i-1)*trkrinfo%contint + i = i + 1 + else + iflip = maxconts - i + 1 + contvals(iflip) = xcentval - float(i-1)*trkrinfo%contint + i = i + 1 + endif + enddo + +c This successive_contours loop is the master loop.... + + successive_contours_loop: do while (num_found_contours < + & num_requested_contours) + +c Find the contour interval in which the center value resides. +c Note that the lower bound is included for a min check, while +c the upper bound is included for a max check. Note also that +c this subroutine can be used to find the last closed contour, +c and part of that functionality shows up in the next while +c statement where we reference "num_found_contours" in the +c array indeces for the contour values. Basically, the way we +c do this is, for example, if our central value is 990.4 mb and +c our contour interval is 4 mb, then in the first run through +c successive_contours_loop we see if we have a closed contour in +c the interval 990.4-994.4. If yes, then the next time through +c this loop, we see if we have a closed contour in the interval +c 994.4-998.4. If yes, then the next loop check is for 998.4- +c 1002.4, and so on.... We stop searching if we find a value +c that is either below the xcentval input into this subroutine +c or below the lower value of the current contour interval (this +c would mean a change in the gradient and would indicate that, +c in the case of mslp, we are heading down towards another, +c different low). + + isc_count = isc_count + 1 + + point_is_already_in_next_contour = .false. + + i = 1 + do while (i < maxconts) + if (cmaxmin == 'min') then + if (contvals(i) <= xcentval .and. xcentval < contvals(i+1)) + & then + + if ( verb .ge. 3 ) then + print *,'At A, num_found_contours= ',num_found_contours + endif + + contlo = contvals(i+num_found_contours) + conthi = contvals(i+1+num_found_contours) + + if ( verb .ge. 3 ) then + print *,'At A, contlo= ',contlo,' conthi= ',conthi + endif + exit + + endif + else + if (contvals(i) < xcentval .and. xcentval <= contvals(i+1)) + & then + contlo = contvals(i-num_found_contours) + conthi = contvals(i-num_found_contours+1) + exit + endif + endif + i = i + 1 + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'num_found_contours= ',num_found_contours + print *,'contlo= ',contlo,' conthi= ',conthi + print *,'xcentval= ',xcentval + endif + + +c This single_contour_scan_loop is the main loop for searching +c for one individual contour. If it is determined that a contour +c exists, control is returned to the successive_contours_loop, +c and if more contours were requested to be found, then the +c search continues onward & outward.... + + temp_mask_i_loc = 0 + temp_mask_j_loc = 0 + + iter = 1 + num_pts_in_one_contour = 0 + still_scanning = .true. + + rlast_distsum = 0.0 + rlast_distct = 0 + + single_contour_scan_loop: do while (still_scanning) + +c print *,' ' +c print *,' top of single contour scan loop' +c print *,'+++ iter= ',iter +c print *,' N1: next_contour_ct= ',next_contour_ct + + if (iter == 1 .and. num_found_contours == 0) then + ! For the first iteration, we have only the first ring, + ! which is centered on the input minimum/maximum point. + ringct = 1 + search_next_i(1) = ix + search_next_j(1) = jx + +c point_is_already_in_our_contour(ix,jx) = .true. +c num_pts_in_one_contour = num_pts_in_one_contour + 1 +c temp_mask_i_loc(num_pts_in_one_contour) = ix +c temp_mask_j_loc(num_pts_in_one_contour) = jx + + else if (iter == 1 .and. num_found_contours > 0) then + ! This is the first iteration in a *new* contour. + ! That is, we have already found 1 or more previous + ! contours while in previous iterations of + ! successive_contours_loop and we are now beginning + ! to look for the next contour. + +c print *,' N2: next_contour_ct= ',next_contour_ct + + if (next_contour_ct == 0) then + ! This would be for the special case in which, for + ! example, you've got a very intense, compact storm + ! that "skips" a contour. That is, suppose the + ! min pressure of a storm is 982 mb, and we are + ! utilizing a 4-mb contour interval, but all + ! surrounding data points are, say, 987 mb or + ! higher. Then, next_contour_ct would be 0 since no + ! data points were found in the next contour interval + ! of 982-986 mb, but we can continue searching since the + ! gradient is still sloping the correct way. The code in + ! this if statement handles this special case. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ALERT: next_contour_ct = 0 ' + endif + + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + +c print *,'b4 ZZ, ringct= ',ringct +c print *,'at ZZ, bcc= ',beyond_contour_ct +c & ,'contlo_next= ',contlo_next +c & ,'conthi_next= ',conthi_next + + bey_con_min_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_min_loop + endif + +c print *,'-- ZZ, ibx= ',ibx,' jby= ',jby +c & ,' fxy(ibx,jby)= ',fxy(ibx,jby) + + if (fxy(ibx,jby) >= contlo_next .and. + & fxy(ibx,jby) < conthi_next) then + +c print *,'>> ZZ HIT!!, ibx= ',ibx,' jby= ',jby +c +c print *,' +++ BEYOND in NEXT: i= ',ibx,' j= ',jby +c & ,' fxy= ',fxy(ibx,jby) + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + +c print *,'.. ZZ, next_contour_ct= ',next_contour_ct + + enddo bey_con_min_loop + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + +c print *,'At A, beyond_contour_ct= ',beyond_contour_ct +c print *,' contlo_next = ',contlo_next +c print *,' conthi_next = ',conthi_next + + bey_con_max_loop: do nb = 1,beyond_contour_ct + +c print *,'in bey_con_max_loop, nb= ',nb + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_max_loop + endif + +c print *,'ibx= ',ibx,' jby= ',jby,' data= ' +c & ,fxy(ibx,jby) + + if (fxy(ibx,jby) > contlo_next .and. + & fxy(ibx,jby) <= conthi_next) then + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + +c print *,' ++ HIT! ibx= ',ibx,' jby= ',jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + enddo bey_con_max_loop + endif + + if (next_contour_ct > 0) then + ringct = next_contour_ct + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! XXX next_contour_ct not > 0 !!!' + print *,'next_contour_ct= ',next_contour_ct + print *,'beyond_contour_ct= ',beyond_contour_ct + print *,'ringct= ',ringct + print *,'next_ring_ct= ',next_ring_ct + print *,'cycling to top of successive_contours_loop..' + print *,' ' + endif + + ! The number of rings that we have available to search + ! in the next contour interval is 0, so cycle all the + ! way back to the top of the outer loop, which is + ! successive_contours_loop, so that we can increase the + ! contour bounds and search inside those new bounds. + ! Again, this is for the case in which we have an + ! intense, compact storm and we are using a small + ! contour interval, such that we are essentially + ! "skipping" over one of these intervals in one of the + ! loop iterations. We need to bump up the + ! num_found_contours by one in order to increase the + ! array index in the contvals array at the top of the + ! successive_contours_loop. It is kosher to do this + ! since the reason we are cycling back to the top of + ! that loop is that we are skipping over a contour + ! interval. + + num_found_contours = num_found_contours + 1 + cycle successive_contours_loop + + endif + + else + + ringct = next_contour_ct + + endif + + do nring = 1,ringct + search_next_i(nring) = next_contour_i(nring) + search_next_j(nring) = next_contour_j(nring) +c print *,'at A, nring= ',nring,' next_contour_i(nring)= ' +c & ,next_contour_i(nring),' next_contour_j(nring)= ' +c & ,next_contour_j(nring) + enddo + + next_contour_ct = 0 + + else + ringct = next_ring_ct + endif + + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + allocate (ringposi(ringct),stat=iria) + allocate (ringposj(ringct),stat=irja) + if (iria /= 0 .or. irja /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various ring arrays. iria = ',iria + print *,'!!! irja = ',irja + print *,' ' + endif + + STOP 98 + endif + +ctm +c print *,' ' +c print *,'ringct= ',ringct + + do nring = 1,ringct + ringposi(nring) = search_next_i(nring) + ringposj(nring) = search_next_j(nring) +ctm +c print *,'nring= ',nring,' ringposi= ',ringposi(nring) +c & ,' ringposj= ',ringposj(nring) + enddo + + next_ring_ct = 0 + + ! This next loop reviews the points that have been + ! labelled for the "beyond_contour" pool. As we get further + ! into successive iterations of successive_contours_loop, + ! some of these previously "beyond" points are now within + ! the contour interval range that we are checking, so we + ! need to go through the list of "beyond" points and remove + ! any that are no longer in that "beyond" category.... + + check_beyond_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! This point may have been removed already in a + ! previous iteration of successive_contours_loop. + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle check_beyond_loop + endif + + ! Check to see if any of the points being searched in the + ! upcoming multiple_ring_loop are points that had previously + ! been saved as "beyond_contour" points. If so, remove + ! their status as "beyond_contour" points by setting the + ! logical flag to false. + + do nring = 1,ringct + + if (ibx == ringposi(nring) .and. jby == ringposj(nring)) + & then +c print *,' ' +c print *,'!!! beyond remove: ibx= ',ibx,' jby= ',jby + point_is_already_in_beyond_pool(ibx,jby) = .false. + endif + + enddo + + enddo check_beyond_loop + + +c In each iteration of single_contour_scan_loop, we can have a +c different number of rings to analyze. In the first +c iteration, we only have 1 ring, the initial ring around the +c local max/min that was input to this subroutine. Subsequent +c iterations will have a variable number of rings, depending on +c how many new data points within our contour interval were +c found in the previous iteration. + + multiple_ring_loop: do mr = 1,ringct + + icenx = ringposi(mr) + jcenx = ringposj(mr) + +ctm +c print *,' --- iter= ',iter,' mr= ',mr,' icenx= ',icenx +c & ,' jcenx= ',jcenx + + call get_ijplus1_check_wrap (imax,jmax,icenx,jcenx,ixp1,jxp1 + & ,ixm1,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NO CLOSED CONTOUR: The call to ' + print *,'!!! get_ijplus1_check_wrap indicates the' + print *,'!!! max/min contour extends past the edge of' + print *,'!!! our regional grid. ' + print *,' ' + print *,' ' + endif + + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c For each individual ring, we check all 8 points surrounding +c the center point. The points are numbered for each ring as +c shown in the diagram to the right of the "select case" +c statement just below. REMEMBER: The j in our grids +c increases from north to south, so that for a global grid, +c j=1 is at 90N and j=jmax is at 90S. + + individual_ring_loop: do ir = 1,9 + + select case (ir) + case (1); irx=ixm1; jrx=jcenx;! 2 3 4 + case (2); irx=ixm1; jrx=jxm1; ! + case (3); irx=icenx;jrx=jxm1; ! + case (4); irx=ixp1; jrx=jxm1; ! 1 (icenx,jcenx) 5 + case (5); irx=ixp1; jrx=jcenx;! + case (6); irx=ixp1; jrx=jxp1; ! + case (7); irx=icenx;jrx=jxp1; ! 8 7 6 + case (8); irx=ixm1; jrx=jxp1; ! + case (9); irx=icenx; jrx=jcenx; ! = center pt of ring + end select + +c Make sure the point we are looking at has valid data. +c This is an issue only on regional grids, where we have a +c buffer of bitmapped (null) data points surrounding the +c real grid. + +c print *,'ind ring loop: ir= ',ir,' irx= ',irx,' jrx= ',jrx + + if (.not. valid_pt(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a non-' + print *,'!!! valid point, meaning we are near the' + print *,'!!! bounds of the grid, or at least the ' + print *,'!!! bounds of the valid data for this ' + print *,'!!! grid. We will skip the' + print *,'!!! search for this center.' + print *,'!!! ' + print *,'!!! (i,j) of non-valid pt = (' + & ,irx,',',jrx,')' + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c Check to make sure that the point we are looking at is +c not considered under the influence of another nearby low. + + if (masked_out(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a point' + print *,'!!! that has been masked out, meaning it' + print *,'!!! belongs under the influence of ' + print *,'!!! another nearby low, so we will skip' + print *,'!!! the search for this center....' + print *,'!!! ' + print *,'!!! Min central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Masked-out value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of masked value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we have already hit this point on a previous ring +c check, then just ignore this point and cycle past it. + + if (point_is_already_in_our_contour(irx,jrx)) then +ctm +c print *,' ' +c print *,'Pt. AAA, already-in-contour.....' +c print *,'irx= ',irx,' jrx= ',jrx + cycle individual_ring_loop + endif + +c For a MIN check, check to see if the data point is below +c the contour interval or is below the local minimum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c For a MAX check, check to see if the data point is above +c the contour interval or is above the local maximum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c +c For example, for mslp, this would be as we're moving +c outward away from lower pressures to higher pressures, +c and then all of a sudden we come upon a lower pressure. +c This probably means we're heading toward another low +c pressure area, so mark the point and return to the +c calling routine. + + found_a_point_below_contour = 'n' + found_a_point_above_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) < xcentval .or. fxy(irx,jrx) < contlo) + & then + found_a_point_below_contour = 'y' + endif + else + if (fxy(irx,jrx) > xcentval .or. fxy(irx,jrx) > conthi) + & then + found_a_point_above_contour = 'y' + endif + endif + + if (found_a_point_below_contour == 'y' .or. + & found_a_point_above_contour == 'y') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a data' + print *,'!!! value that is less (greater) than the' + print *,'!!! current contour interval bound for a' + print *,'!!! min (max) and/or is less (greater) ' + print *,'!!! than the minimum (maximum) central ' + print *,'!!! value that we are centering the ' + print *,'!!! search on.' + print *,'!!! ' + print *,'!!! Central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Flagged value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of flagged value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we've made it this far, then we at least know that the +c gradient is still heading in the right direction. Do the +c check now to see if the value at this point is within our +c specific contour interval (there is the possibility that +c the value is beyond our interval, which will be checked +c for just below, and if that's the case, then that point +c will be processed in a subsequent iteration of this loop +c that encompasses that correct contour interval). + + found_a_point_in_our_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) >= contlo .and. fxy(irx,jrx) < conthi) + & then + found_a_point_in_our_contour = 'y' + endif + else + if (fxy(irx,jrx) > contlo .and. fxy(irx,jrx) <= conthi) + & then + found_a_point_in_our_contour = 'y' + endif + endif + + if (found_a_point_in_our_contour == 'y') then + ! We've found a data point in our interval, something + ! that is inside the closed contour, and it hasn't been + ! marked as being found in a previous iteration of this + ! loop, so mark it now and store the (i,j) location so + ! that we can scan a ring around this point in a + ! successive iteration of this loop for more potential + ! points within this interval... + + point_is_already_in_our_contour(irx,jrx) = .true. + + next_ring_ct = next_ring_ct + 1 + search_next_i(next_ring_ct) = irx + search_next_j(next_ring_ct) = jrx + +c print *,'at B, next_ring_ct= ',next_ring_ct +c & ,' search_next_i()= ',search_next_i(next_ring_ct) +c & ,' search_next_j()= ',search_next_j(next_ring_ct) + + num_pts_in_one_contour = num_pts_in_one_contour + 1 + temp_mask_i_loc(num_pts_in_one_contour) = irx + temp_mask_j_loc(num_pts_in_one_contour) = jrx + + if (get_last_isobar_flag == 'y') then + call calcdist (glon(ix),glat(jx) + & ,glon(irx),glat(jrx),dist,degrees) + rlast_distsum = rlast_distsum + dist + rlast_distct = rlast_distct + 1 + endif + +ctm +c print *,' ' +c print *,' PT IN! irx= ',irx,' jrx= ',jrx,' xval= ' +c & ,fxy(irx,jrx) +c print *,'next_ring_ct= ',next_ring_ct +c print *,'num_pts_in_one_contour= ' +c & ,num_pts_in_one_contour + endif + +c If we've made it this far AND the +c found_a_point_in_our_contour flag indicates that this +c point is not in our contour interval, then by default that +c means that this point is for a contour interval beyond +c what we're currently looking at. E.g., if we're looking +c at the contours around a 972 mb low and we're moving +c outward and currently checking the 984-988 mb contour +c interval, it means that we found, say, a gridpoint with +c 991 mb. So we want to mark that point for a future +c iteration of this loop that would be checking the +c 988-992 mb contour interval. + + if (found_a_point_in_our_contour /= 'y' .and. + & .not. point_is_already_in_next_contour(irx,jrx)) then + ! We've found a data point that is beyond our interval, + ! so this is not a concern for finding the bounds of + ! our current contour interval, but we want to mark + ! these points and remember them for the next iteration + ! of successive_scan_loop. (For example, suppose we + ! are currently searching for points in the 984-988 mb + ! range, and we find a point that is 990 -- mark it + ! here to be remembered when we scan for 988-992 mb). + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + if (fxy(irx,jrx) >= contlo_next .and. + & fxy(irx,jrx) < conthi_next) then + ! "NEXT_CONTOUR" Comment: + ! We've found a point that is in the very next + ! contour interval.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. + else if (fxy(irx,jrx) >= conthi_next) then + ! "BEYOND_CONTOUR" Comment: + ! This point is at least 1 contour interval beyond + ! the next contour interval. Dump the info into + ! these i and j arrays. This info will be used if + ! in the next iteration of single_contour_scan_loop, + ! next_contour_ct = 0. That would mean that we + ! have, e.g., an intensely deep low with a sharp + ! mslp gradient that essentially "skips" over a + ! contour interval. E.g., if using a 4 mb interval, + ! we go from 947 to 953 AND there are NO + ! intervening gridpoints in the 948-952 interval. + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) + endif + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + if (fxy(irx,jrx) > contlo_next .and. + & fxy(irx,jrx) <= conthi_next) then + ! See "NEXT_CONTOUR" comment just above.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. +c print *,'NEXT ncc= ',next_contour_ct +c & ,'next_contour_i()= ' +c & ,next_contour_i(next_contour_ct) +c & ,'next_contour_j()= ' +c & ,next_contour_j(next_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + else if (fxy(irx,jrx) <= contlo_next) then + ! See "BEYOND_CONTOUR" comment just above.... + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'BEYOND bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + endif + endif + endif + + enddo individual_ring_loop + + enddo multiple_ring_loop + + if (next_ring_ct > 0) then + iter = iter + 1 + else + icccret = 0 + still_scanning = .false. + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + num_found_contours = num_found_contours + 1 + closed_contour = 'y' + if (num_found_contours == 1) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Closed contour found ' + endif + + endif + endif + + enddo single_contour_scan_loop + + do insingle = 1,num_pts_in_one_contour + num_pts_in_all_contours = num_pts_in_all_contours + 1 + inall = num_pts_in_all_contours + hold_mask_i_loc(inall) = temp_mask_i_loc(insingle) + hold_mask_j_loc(inall) = temp_mask_j_loc(insingle) + enddo + + if (get_last_isobar_flag == 'y') then + if (cmaxmin == 'min') then + plastbar = conthi + else + plastbar = contlo + endif + if (rlast_distct > 0) then + rlastbar = rlast_distsum / float(rlast_distct) + rlastbar = rlastbar * 0.539638 ! convert km to nm + else + rlastbar = -999.0 + endif + endif + + enddo successive_contours_loop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'END SUM: num of iterations = ',isc_count + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine get_ijplus1_check_wrap (imax,jmax,i,j,iplus1,jplus1 + & ,iminus1,jminus1,trkrinfo,igicwret) +c +c ABSTRACT: This subroutine takes an (i,j) position input and +c returns the four neighboring (i,j) points to the east, south, +c west and north. The routine checks for wrap around the GM, so +c that if, for example, you are on a global 360x181 grid and you +c are at point i=360, then i+1 = 361, so you need something to +c adjust that back to i = 1. Likewise, if you are at i=1 and +c looking for point i-1, it will adjust it to be point 360 +c instead of the meaningless point 0 (i=0). + + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer i,j,imax,jmax,iplus1,jplus1,iminus1,jminus1,igicwret + + igicwret = 0 + + jplus1 = j + 1 + jminus1 = j - 1 + iplus1 = i + 1 + iminus1 = i - 1 + + if (iplus1 > imax) then + if (trkrinfo%gridtype == 'global') then + iplus1 = iplus1 - imax ! If wrapping east of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is too close to the eastern bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested eastern ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',iplus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (iminus1 < 1) then + if (trkrinfo%gridtype == 'global') then + iminus1 = imax + iminus1 ! If wrapping west of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested western search boundary' + print *,'!!! is too close to the western bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested western ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested western i = ',iminus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (jplus1 > jmax .or. jminus1 < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The ' + print *,'!!! user-requested northern or southern search' + print *,'!!! boundary is too close to the bounds of the' + print *,'!!! grid. Cut back your requested northern or' + print *,'!!! southern boundary by a degree or 2 in the' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested northern j = ',jminus1 + print *,'!!! User-requested southern j = ',jplus1 + print *,'!!! jmax of grid = ',jmax + print *,' ' + endif + + igicwret = 91 + return + endif + + return + end + +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + SUBROUTINE qsort(x,ind,n) +c +c Code converted using TO_F90 by Alan Miller +c Date: 2002-12-18 Time: 11:55:47 + + IMPLICIT NONE + INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(12, 60) + + REAL (dp), INTENT(IN) :: x(n) + INTEGER, INTENT(OUT) :: ind(n) + INTEGER, INTENT(IN) :: n + +c *************************************************************************** +c +c ROBERT RENKA +c OAK RIDGE NATL. LAB. +c +c THIS SUBROUTINE USES AN ORDER N*LOG(N) QUICK SORT TO SORT A REAL (dp) +c ARRAY X INTO INCREASING ORDER. THE ALGORITHM IS AS FOLLOWS. IND IS +c INITIALIZED TO THE ORDERED SEQUENCE OF INDICES 1,...,N, AND ALL INTERCHANGES +c ARE APPLIED TO IND. X IS DIVIDED INTO TWO PORTIONS BY PICKING A CENTRAL +c ELEMENT T. THE FIRST AND LAST ELEMENTS ARE COMPARED WITH T, AND +c INTERCHANGES ARE APPLIED AS NECESSARY SO THAT THE THREE VALUES ARE IN +c ASCENDING ORDER. INTERCHANGES ARE THEN APPLIED SO THAT ALL ELEMENTS +c GREATER THAN T ARE IN THE UPPER PORTION OF THE ARRAY AND ALL ELEMENTS +c LESS THAN T ARE IN THE LOWER PORTION. THE UPPER AND LOWER INDICES OF ONE +c OF THE PORTIONS ARE SAVED IN LOCAL ARRAYS, AND THE PROCESS IS REPEATED +c ITERATIVELY ON THE OTHER PORTION. WHEN A PORTION IS COMPLETELY SORTED, +c THE PROCESS BEGINS AGAIN BY RETRIEVING THE INDICES BOUNDING ANOTHER +c UNSORTED PORTION. +c +c INPUT PARAMETERS - N - LENGTH OF THE ARRAY X. +c +c X - VECTOR OF LENGTH N TO BE SORTED. +c +c IND - VECTOR OF LENGTH >= N. +c +c N AND X ARE NOT ALTERED BY THIS ROUTINE. +c +c OUTPUT PARAMETER - IND - SEQUENCE OF INDICES 1,...,N PERMUTED IN THE SAME +c FASHION AS X WOULD BE. THUS, THE ORDERING ON +c X IS DEFINED BY Y(I) = X(IND(I)). +c +c ********************************************************************* + + ! NOTE -- IU AND IL MUST BE DIMENSIONED >= LOG(N) WHERE LOG HAS BASE 2. + + !********************************************************************* + + INTEGER :: iu(21), il(21) + INTEGER :: m, i, j, k, l, ij, it, itt, indx + REAL :: r + REAL (dp) :: t + + ! LOCAL PARAMETERS - + + ! IU,IL = TEMPORARY STORAGE FOR THE UPPER AND LOWER + ! INDICES OF PORTIONS OF THE ARRAY X + ! M = INDEX FOR IU AND IL + ! I,J = LOWER AND UPPER INDICES OF A PORTION OF X + ! K,L = INDICES IN THE RANGE I,...,J + ! IJ = RANDOMLY CHOSEN INDEX BETWEEN I AND J + ! IT,ITT = TEMPORARY STORAGE FOR INTERCHANGES IN IND + ! INDX = TEMPORARY INDEX FOR X + ! R = PSEUDO RANDOM NUMBER FOR GENERATING IJ + ! T = CENTRAL ELEMENT OF X + + IF (n <= 0) RETURN + + ! INITIALIZE IND, M, I, J, AND R + + DO i = 1, n + ind(i) = i + END DO + m = 1 + i = 1 + j = n + r = .375 + + ! TOP OF LOOP + + 20 IF (i >= j) GO TO 70 + IF (r <= .5898437) THEN + r = r + .0390625 + ELSE + r = r - .21875 + END IF + + ! INITIALIZE K + + 30 k = i + + ! SELECT A CENTRAL ELEMENT OF X AND SAVE IT IN T + + ij = i + r*(j-i) + it = ind(ij) + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) > t) THEN + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + END IF + + ! INITIALIZE L + + l = j + + ! IF THE LAST ELEMENT OF THE ARRAY IS LESS THAN T, + ! INTERCHANGE IT WITH T + indx = ind(j) + IF (x(indx) >= t) GO TO 50 + ind(ij) = indx + ind(j) = it + it = indx + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) <= t) GO TO 50 + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + GO TO 50 + + ! INTERCHANGE ELEMENTS K AND L + + 40 itt = ind(l) + ind(l) = ind(k) + ind(k) = itt + + ! FIND AN ELEMENT IN THE UPPER PART OF THE ARRAY WHICH IS + ! NOT LARGER THAN T + + 50 l = l - 1 + indx = ind(l) + IF (x(indx) > t) GO TO 50 + + ! FIND AN ELEMENT IN THE LOWER PART OF THE ARRAY WHCIH IS NOT SMALLER THAN T + + 60 k = k + 1 + indx = ind(k) + IF (x(indx) < t) GO TO 60 + + ! IF K <= L, INTERCHANGE ELEMENTS K AND L + + IF (k <= l) GO TO 40 + + ! SAVE THE UPPER AND LOWER SUBSCRIPTS OF THE PORTION OF THE + ! ARRAY YET TO BE SORTED + + IF (l-i > j-k) THEN + il(m) = i + iu(m) = l + i = k + m = m + 1 + GO TO 80 + END IF + + il(m) = k + iu(m) = j + j = l + m = m + 1 + GO TO 80 + + + ! BEGIN AGAIN ON ANOTHER UNSORTED PORTION OF THE ARRAY + + 70 m = m - 1 + IF (m == 0) RETURN + i = il(m) + j = iu(m) + + 80 IF (j-i >= 11) GO TO 30 + IF (i == 1) GO TO 20 + i = i - 1 + + ! SORT ELEMENTS I+1,...,J. NOTE THAT 1 <= I < J AND J-I < 11. + + 90 i = i + 1 + IF (i == j) GO TO 70 + indx = ind(i+1) + t = x(indx) + it = indx + indx = ind(i) + IF (x(indx) <= t) GO TO 90 + k = i + + 100 ind(k+1) = ind(k) + k = k - 1 + indx = ind(k) + IF (t < x(indx)) GO TO 100 + + ind(k+1) = it + GO TO 90 + END SUBROUTINE qsort + +cJ.Peng----2014-10-01-------------------------------------------------- +c----------------------------------------------------------------------- + subroutine get_ushe_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanushe,igridushe,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,iushet) +c +c ABSTRACT: This subroutine finds the minimum and mean U-shear values +c between 200 & 850 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cJ.Peng----2014-10-01--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cJ.Peng----2014-10-01------------------------------ + logical(1) readflag(16),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanushe,dx,dy,re,ri,parmlon,parmlat + real igridushe,imeanushe + integer n,ix1,ix2,npts,imax,jmax,iushet,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ushe_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + iushet = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_ushe_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + iushet = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max ushe values at 200 and 850 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_ushe_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_ushe_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeanushe = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (ushear(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid ushe data for this level, so + ! we first call barnes now to get the mean ushe + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then +c cvort_maxmin = 'max' +c else + cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'ushe' + & ,ushear(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanushe + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cJ.Peng--------------------------- +c imeanushe = int (xmeanushe + 0.5) + imeanushe = xmeanushe + else + imeanushe = -999. + igridushe = -999. + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_ushe_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for ushe values will not be done.') + exit report_ushe_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanushe = -999. + igridushe = -999. + exit report_ushe_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeanushe,imeanushe + 621 format (1x,' xmeanushe= ',f9.3,' imeanushe= ',f9.3) + write (6,*) ' --- mean ushe raw = ',xmeanushe + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! ushe data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,ushear(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanushe,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cJ.Peng--------------------------- +c igridushe = int (gridpoint_maxmin + 0.5) + igridushe = gridpoint_maxmin + else + igridushe = -999. + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridushe,ifilret + 623 format (1x, + & ' grid ushe= ',f9.3,' igrid ushe= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid ushe raw= ',gridpoint_maxmin + endif + + enddo report_ushe_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get U-shear for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- +cJ.Peng----2014-10-01-------------------------------------------------- +c----------------------------------------------------------------------- + subroutine get_rhum_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanrhum,igridrhum,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,irhumt) +c +c ABSTRACT: This subroutine finds the minimum and mean RH values +c at 500 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cJ.Peng----2014-10-01--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cJ.Peng----2014-10-01------------------------------ + logical(1) readflag(16),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanrhum,dx,dy,re,ri,parmlon,parmlat + real igridrhum,imeanrhum + integer n,ix1,ix2,npts,imax,jmax,irhumt,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_rhum_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + irhumt = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_rhum_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + irhumt = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max rhum values at 500 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_rhum_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_rhum_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeanrhum = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (rhumid(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid rhum data for this level, so + ! we first call barnes now to get the mean rhum + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then +c cvort_maxmin = 'max' +c else + cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'rhum' + & ,rhumid(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanrhum + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cJ.Peng------------------------------------------------- +c imeanrhum = int (xmeanrhum + 0.5) + imeanrhum = xmeanrhum + else + imeanrhum = -99.0 + igridrhum = -99.0 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_rhum_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for rhum values will not be done.') + exit report_rhum_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanrhum = -99.0 + igridrhum = -99.0 + exit report_rhum_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeanrhum,imeanrhum + 621 format (1x,' xmeanrhum= ',f9.3,' imeanrhum= ',f9.3) + write (6,*) ' --- mean rhum raw = ',xmeanrhum + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! rhum data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,rhumid(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanrhum,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cJ.Peng------------------------------------------------- +c igridrhum = int (gridpoint_maxmin + 0.5) + igridrhum = gridpoint_maxmin + else + igridrhum = -99.0 + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridrhum,ifilret + 623 format (1x, + & ' grid rhum= ',f9.3,' igrid rhum= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid rhum raw= ',gridpoint_maxmin + endif + + enddo report_rhum_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get RH for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- +cJ.Peng----2014-10-01------------------------------------------- +c This is for area mean caculations +c--------------------------------------------------------------------- + subroutine fix_latlon_mean_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +cJ.Peng----2014-10-01--------------------------------------- + real dsum, dnum + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.10) then + grfact = 4 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif +cJ.Peng----2012--04--18---10X10 average ------------------------- + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (4*grfact) + iend = ipfix + (5*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (5*grfact) + iend = ipfix + (4*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (4*grfact) + iend = ipfix + (4*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (5*grfact) + jend = jpfix + (4*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (4*grfact) + jend = jpfix + (5*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (4*grfact) + jend = jpfix + (4*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix +cJ.Peng----2014-10-01--------------------------------------- + dsum=0.0 + dnum=0.0 + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif +cJ.Peng----2014-10-01--------------------------------------- + dsum=dsum+fxy(i,j) + dnum=dnum+1 + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +cJ.Peng----2014-10-01--------------------------------------- + if (dnum.gt.0.0) then + gridpoint_maxmin=dsum/dnum + else + gridpoint_maxmin=-9999.0 + endif + + print *,'istart=',istart,'iend=',iend + print *,'jstart=',jstart,'jend=',jend + print *,'End of fix_latlon_mean_ij, gridpoint_maxmin = ' + & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +cJ.Peng----2014-10-01------10X10 area mean temperature----------------- +c----------------------------------------------------------------------- + subroutine get_temp_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeantemp,igridtemp,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,itempt) +c +c ABSTRACT: This subroutine finds the maximum and mean temperature +c between 300 & 500 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cJ.Peng----2014-10-01--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cJ.Peng----2014-10-01------------------------------ + logical(1) readflag(16),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeantemp,dx,dy,re,ri,parmlon,parmlat + real igridtemp,imeantemp + integer n,ix1,ix2,npts,imax,jmax,itempt,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_temp_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + itempt = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_temp_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + itempt = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max temp values at 300 and 500 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_temp_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_temp_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeantemp = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (tmean(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid temp data for this level, so + ! we first call barnes now to get the mean temp + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' +c else +c cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'temp' + & ,tmean(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeantemp + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cJ.Peng------------------- +c imeantemp = int (xmeantemp + 0.5) + imeantemp = xmeantemp + else + imeantemp = -99.0 + igridtemp = -99.0 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_temp_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for temp values will not be done.') + exit report_temp_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeantemp = -99.0 + igridtemp = -99.0 + exit report_temp_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeantemp,imeantemp + 621 format (1x,' xmeantemp= ',f9.3,' imeantemp= ',f9.3) + write (6,*) ' --- mean temp raw = ',xmeantemp + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! temp data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,tmean(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeantemp,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cJ.Peng------------------- +c igridtemp = int (gridpoint_maxmin + 0.5) + igridtemp = gridpoint_maxmin + else + igridtemp = -99.0 + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridtemp,ifilret + 623 format (1x, + & ' grid temp= ',f9.3,' igrid temp= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid temp raw= ',gridpoint_maxmin + endif + + enddo report_temp_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get T-mean for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/gettrk_gen_modules.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/gettrk_gen_modules.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/gettrk_gen_modules.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/gettrk_gen_modules.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile index e6c3f0e4a0..22e8210d1d 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc +FCOMP= mpiifort -nofree +CCOMP= mpiicc FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) CFLAGS= -O3 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_hera similarity index 86% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_hera index db4ca661ed..122e0d90a8 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_hera @@ -1,10 +1,10 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc -FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 -CFLAGS= -O2 -axSSE4.2,AVX,CORE-AVX2 +FCOMP= mpiifort -nofree +CCOMP= mpiicc +FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) +CFLAGS= -O2 gettrk_gen_g2: gettrk_gen_main_g2.f gettrk_gen_modules.o module_waitfor.o cwaitfor.o @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_jet new file mode 100644 index 0000000000..5a07f7d805 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_jet @@ -0,0 +1,37 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +FCOMP= mpiifort -nofree +CCOMP= mpiicc +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +CFLAGS= -O3 -axSSE4.2,AVX,CORE-AVX2 + +gettrk_gen_g2: gettrk_gen_main_g2.f gettrk_gen_modules.o module_waitfor.o cwaitfor.o + @echo " " + @echo " Compiling the main tracking program and subroutines....." + $(FCOMP) $(FFLAGS) gettrk_gen_modules.o module_waitfor.o cwaitfor.o gettrk_gen_main_g2.f $(LIBS) -o gettrk_gen_g2 + @echo " " + +cwaitfor.o: cwaitfor.c + @echo " " + @echo " Compiling the waitfor C routine...." + $(CCOMP) $(CFLAGS) -c cwaitfor.c -o cwaitfor.o + +module_waitfor.o: module_waitfor.f + @echo " " + @echo " Compiling the waitfor fortran module...." + $(FCOMP) $(FFLAGS) -c module_waitfor.f -o module_waitfor.o + +gettrk_gen_modules.o: gettrk_gen_modules.f + @echo " " + @echo " Compiling the regular tracker fortran modules....." + $(FCOMP) $(FFLAGS) -c gettrk_gen_modules.f -o gettrk_gen_modules.o + @echo " " + +CMD = gettrk_gen_g2 + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_orion index 0556e91a0c..073501037c 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_orion @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc +FCOMP= mpiifort -nofree +CCOMP= mpiicc #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) CFLAGS= -O3 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/module_waitfor.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/module_waitfor.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_g2.fd/module_waitfor.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_g2.fd/module_waitfor.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/cwaitfor.c b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/cwaitfor.c similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/cwaitfor.c rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/cwaitfor.c diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_g2.f_07292019 b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_g2.f_07292019 new file mode 100644 index 0000000000..68e5654d23 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_g2.f_07292019 @@ -0,0 +1,27263 @@ + program trakmain +c +c$$$ MAIN PROGRAM DOCUMENTATION BLOCK +c +c Main Program: GETTRK Track model vortices +C PRGMMR: MARCHOK ORG: NP22 DATE: 2002-05-20 +c +c ABSTRACT: This program tracks the average of the max or min +c of several parameters in the vicinity of an input +c first guess (lat,lon) position of a vortex in order to give +c forecast position estimates for that vortex for a given numerical +c model. For the levels 700 & 850 mb, the tracked parameters are: +c Relative vorticity (max), wind magnitude (min), and geopotential +c height (min). Also tracked is the min in the MSLP. So many +c parameters are tracked in order to provide more accurate position +c estimates for weaker storms, which often have poorly defined +c structures/centers. Currently, the system is set up to be able +c to process GRIB input data files from the GFS, MRF, UKMET, GDAS, +c ECMWF, NGM, NAM and FNMOC/NAVGEM models. Two 1-line files +c are output from this program, both containing the forecast fix +c positions that the tracker has obtained. One of these output +c files contains the positions at every 12 hours from forecast +c hour 0 to the end of the forecast. The other file is in ATCF +c format, which is the particular format needed by the Tropical +c Prediction Center, and provides the positions at forecast hours +c 12, 24, 36, 48 and 72, plus the maximum wind near the storm center +c at each of those forecast hours. +c +c Program history log: +c 98-03-16 Marchok - Original operational version. +c 98-07-15 Marchok - Added code to calculate radii of gale-, storm-, +c and hurricane-force winds in each quadrant. +c 99-04-01 Marchok - Added code to be able to read in 4-digit years +c off of the TC Vitals records. +c Added code, including subroutine is_it_a_storm, +c to make a better determination of whether or +c not the center that was found at each time is +c the center of a storm, and not just a passing +c vort max, etc. +c 99-06-15 Marchok - Fixed a bug in calcdist that was triggered by a +c rounding error sending a number just above 1 +c into ACOS to get the distance between 2 +c identical points (which, obviously, is 0). +c 00-06-20 Marchok - Added GDAS option for vortex relocation work. +c Changed nhalf from 3 to 5. Relaxed the +c requirements for pthresh and vthresh. +c 00-11-30 Marchok - Added ability to handle GFDL and NCEP Ensemble +c model data. Extended time range to be able to +c handle 5-day capability. Forecast hours are +c now input via a namelist (easiest way to account +c for NAM, GFS and GFDL having different forecast +c lengths at 00/12z and 06/18z). Model ID's are +c now input via a namelist (makes it easier, for +c example, to run for many different ensemble +c members). Added new output, the atcfunix +c format, needed for 5-day forecasts. +c 01-08-24 Marchok Fixed a bug in rvcal and getgridinfo. When a +c grid that was south-->north is flipped in +c conv1d2d_real to be north-->south, the scanning +c mode flag remains 64 and what we would consider +c the max and min latitudes are reversed, so I +c added code to correct this in both routines. +c 02-05-20 Marchok Weakened the mslp gradient threshold and v850 +c threshold in is_it_a_storm to cut down on the +c number of dropped storms. +c 03-03-18 Marchok Fixed a bug in get_ij_bounds that was allowing +c a cos(90) and cos(-90), which then led to a +c divide by zero. +c 05-08-01 Marchok Updated to allow tracking of ECMWF hi-res, ECMWF +c ensemble, CMC hi-res, CMC ensemble, NCEP +c ensemble. +c 06-11-07 Marchok Updated to locate, and report to the atcfunix +c file, the value of the gridpoint minimum value +c of mslp. Previously, the barnes-averaged +c value had been used. +c 08-01-10 Marchok Changed the storm ID for genesis tracking so +c that the ID includes info +c on storm detection location & time. Added +c algorithms for Hart's cyclone phase space. +c Added new output fields to the atcfunix +c records, actually creating a modified atcfunix +c record, to include things such as the mean & +c max values of zeta850 & zeta700 centered on +c the storm, the speed & direction of storm +c translation, and the Hart CPS parameters. +c 10-01-07 Marchok - input grib lead time can be hrs or minutes +c - added code for warm core check +c - added code to detect genesis +c - added code to report on sfc wind structure +c - added buffer ("grid_buffer") to avoid fixing +c center to boundaries on regional grids +c - modified rvcal to report missing zeta values +c as background coriolis instead of -999, since +c the -999 was messing up center-fixing +c - added 10-m wind and sfc zeta as center-fixing +c parms. +c +c 10-05-25 Slocum Add verbose feature to code +c 0 = Not terminal output, 1 = error messages only +c 2 = all output +c +c 10-05-26 Marchok - added flags and code to check the temporal +c consistency of the mslp closed contour and +c Vt850 checks for tcgen and midlat cases. +c +c 13-04-01 Marchok Added code to upgrade the wind radii diagnosid. +c Hurricane Sandy exposed an issue with the +c tracker for large storms. The code was modified +c to use an iterative technique that can +c diagnose radii for large storms but still +c accurately diagnost radii for small storms. See +c subroutine getradii for more details. +c +c 15-11-01 Marchok Replaced the routine which tracks the wind +c minimum at the center of a storm, as that +c routine proved troublesome with very hi-res +c grids (0.02-deg) from HWRF for very small +c storms. This has been replaced with a routine +c that looks for "wind circulation difference", +c whereby the center for this parm is located at +c the spot where the tangential wind circulation +c minus the wind magnitude at the candidate +c center position is maximized. ALSO: Added in +c tracking of thickness as an additional +c tracked parm. ALSO: Added a separate verbose +c flag for only the GRIB2 read diagnostics, which +c can be voluminous. +c +c 16-09-01 Marchok Added in the ability to read in NetCDF files. +c As with GRIB data, the NetCDF data must be on +c a lat/lon grid. +c +c 17-08-31 Marchok Added a logical bitmap capability for NetCDF +c files to prevent the accessing of missing data. +c Also modified the code to permit more accurate +c reporting of the grid point value of the +c minimum SLP for reporting to the atcfunix file. +c Finally, fixed a bug (reported by JTWC) whereby +c radii were being reported for thresholds that +c were in exceedance of the tracker-diagnosed +c Vmax (e.g., 34-kt radii for a storm with +c Vmax = 25 kts). +c +c Input files: +c unit 11 Unblocked GRIB1 file containing model data +c unit 12 Text file containing TC Vitals card for current time +c unit 31 Unblocked GRIB index file +c +c Output files: +c unit 61 Output file with forecast positions every 12h from +c vt=00h to the end of the forecast +c unit 62 Output file in ATCF format, with forecast positions +c at vt = 12, 24, 36, 48 and 72h, plus wind speeds. +c unit 63 Output file with forecast wind radii for 34, 50 and +c 64 knot thresholds in each quadrant of each storm. +c +c Subprograms called: +c read_nlists Read input namelists for input date & storm number info +c read_tcv_card Read TC vitals file to get initial storm position +c getgridinfo Read GRIB file to get basic grid information +c tracker Begin main part of tracking algorithm +c +c Attributes: +c Language: Standard Fortran_90 +c +c$$$ +c +c------- +c +c LOCAL: +c +c ifhours: Integer array holding numerical forecast times for +c the input model (99 = no more times available). +c These values are read in via a namelist. +c Model numbers used: (1) GFS, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) NAM, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble (13) SREF +c Ensemble, (14) NCEP Ensemble (from ensstat mean +c fields), (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) Ensemble RELOCATION (21) UKMET hi-res (NHC) +c (23) FNMOC Ensemble +c stormswitch: This switch tells how to handle each storm in +c the TCV file: +c 1 = process this storm for this forecast hour. +c 2 = Storm was requested to be tracked, but either +c the storm went off the grid (regional models), +c the storm dissipated, or the program was +c unable to track it. +c 3 = Storm was NOT requested to be tracked at all. +c storm: An array of type tcvcard. Each member of storm +c contains a separate TC Vitals card. +c maxstorm: Maximum number of storms the system is set up to +c handle at any 1 time. +c slonfg,slatfg: Holds first guess positions for storms. The +c very first, first guess position is read from the +c TC vitals card. (maxstorm,maxtime) +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms) +c + USE def_vitals; USE inparms; USE set_max_parms; USE level_parms + USE trig_vals; USE atcf; USE trkrparms; USE verbose_output + USE netcdf_parms +c + implicit none +c + logical(1) file_open + integer date_time(8) + character (len=10) big_ben(3) + character :: ncfile*180,ncfile_has_hour0*1 + integer itret,iggret,iicret,igcret,iret,ifhmax,maxstorm,numtcv + integer iocret,enable_timing,ncfile_id,ncfile_tmax,irnhret + integer, parameter :: lugb=11,lugi=31,lucard=12,lgvcard=14,lout=51 +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + +c -------------------------------------------------------- + + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: beginning ... ',i2.2,':',i2.2,':',i2.2) + + call w3tagb('GETTRK ',1999,0104,0058,'NP22 ') + + pi = 4. * atan(1.) ! Both pi and dtr were declared in module + dtr = pi/180.0 ! trig_vals, but were not yet defined. + ncfile_has_hour0 = 'n' ! Default value; set in read_netcdf_hours +c + call read_nlists (inp,trkrinfo,netcdfinfo) + enable_timing=trkrinfo%enable_timing + + call read_fhours (ifhmax) + + call read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_tcv_card, num vitals = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_tcv_card, rc= ',iret + endif + goto 890 + endif + + call read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_gen_vitals, total number of vitals (both' + & ,' TC and non-TC) now = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_gen_vitals, rc= ' + & ,iret + endif + goto 890 + endif + + if (inp%file_seq == 'onebig') then + if (trkrinfo%inp_data_type == 'netcdf') then + ncfile = netcdfinfo%netcdf_filename + print *,' ' + print *,'before open_ncfile call, ncfile= ',ncfile + call open_ncfile (ncfile,ncfile_id) + print *,'after open_ncfile call, ncfile_id= ',ncfile_id + call read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) + if (irnhret /= 0) then + print *,'(/,a32,a5,i4,/)','!!! ERROR: in read_netcdf_hours,' + & ,' rc= ',irnhret + goto 890 + endif + else + call open_grib_files (inp,lugb,lugi,'dummy','dummy',lout,iret) + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: in open_grib_files, rc= ' + & ,iret + goto 890 + endif + endif + endif + + call tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +890 continue + + igcret=0 + iicret=0 + iocret=0 + + inquire (unit=lugb, opened=file_open) + if (file_open) call baclose(lugb,igcret) + inquire (unit=lugi, opened=file_open) + if (file_open) call baclose(lugi,iicret) + inquire (unit=lout, opened=file_open) + if (file_open) call baclose(lout,iocret) + if ( verb .ge. 3 ) then + print *,'baclose: igcret= ',igcret,' iicret= ',iicret + print *,'baclose: iocret= ',iocret + endif + call w3tage('GETTRK ') +c + stop + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +c ABSTRACT: This subroutine is the core of the program. It contains +c the main loop for looping through all the forecast hours and all +c the storms. Basically, the way it works is that it has an outer +c loop that loops on the forecast hour. At the beginning of this +c loop, the data are read in for all parameters and levels needed +c for tracking. The full regional or global grid is read in. +c If vorticity was not read in (some of the centers do not send us +c vorticity), then vorticity calculations are done on the whole +c grid at both 850 and 700 mb. Then the program goes into the inner +c loop, which loops on storm number (program originally set up to +c handle a max of 15 storms). For each storm, subroutine +c find_maxmin is called for the following parameters: Rel Vort and +c geopotential hgt at 700 & 850 mb, and MSLP. Within find_maxmin, +c a barnes analysis is performed over the guess position of the +c storm to find the max or min value, and then iteratively, the +c grid size is cut in half several times and the barnes analysis +c rerun to refine the positioning of the max or min location. After +c the center positions for these parameters have been obtained, +c subroutine get_uv_center is called to get a center fix for the +c minimum in the wind field, specifically, a minimum in the +c magnitude of the wind speed (vmag). The calculation of the vmag +c minimum is done differently than the calculation for the other +c parameters; for vmag, the grid near the storm center guess +c position is interpolated down to a very fine grid, and then +c find_maxmin is called and a barnes analysis is done on that +c smaller grid. For vmag, there are no further calls made to barnes +c with a smaller grid, since the grid has already been interpolated +c down to a smaller grid. Once all of the parameter center fixes +c have been made, subroutine fixcenter is called to average these +c positions together to get a best guess fix position. Then a check +c is done with a call to subroutine is_it_a_storm to make sure that +c the center that we have found does indeed resemble a tropical +c cyclone. Finally, subroutine get_next_ges is called to make a +c guess position for the next forecast time for this storm. +c +c INPUT: +c inp contains input date and model number information +c maxstorm maximum # of storms to be handled +c numtcv number of storms read off of the tcvitals file +c ifhmax max number of analysis & forecast times to be handled +c trkrinfo derived type that holds/describes various tracker parms +c ncfile if the input data type is netcdf, then this ncfile +c variable contains the name of the netcdf file +c ncfile_id if the input data type is netcdf, then this ncfile_id +c variable contains an integer id assigned to the netcdf +c file from the open_ncfile subroutine +c ncfile_has_hour0 character flag (y|n) that, if the tracker is +c running on NetCDF data, tells if the NetCDF file +c actually contains hour0 data or not (some, like the +c 2016 version of FV3, do not). +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file itself in +c subroutine read_netcdf_fhours. +c +c OUTPUT: +c itret return code from this subroutine +c +c LOCAL PARAMETERS: +c storm contains the tcvitals for the storms +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c maxtime Max number of forecast times program can track +c maxtp Max number of tracked parameters program will track. +c Currently (7/2015), this maxtp is 11, and these 11 are +c listed just a few lines below. +c readflag L Indicates status of read for each of 16 parms: +c 1: 850 mb absolute vorticity +c 2: 700 mb absolute vorticity +c 3: 850 mb u-comp +c 4: 850 mb v-comp +c 5: 700 mb u-comp +c 6: 700 mb v-comp +c 7: 850 mb gp hgt +c 8: 700 mb gp hgt +c 9: MSLP +c 10: near-surface u-comp +c 11: near-surface v-comp +c 12: 500 mb u-comp +c 13: 500 mb v-comp +c 14: Mean temperature, centered at 400 mb +c 15: 500 mb gp hgt +c 16: 200 mb gp hgt +c 17: Land-Sea Mask (for use in tcgen applications, and +c even there, it's optional) +c +c calcparm L indicates which parms to track and which not to. +c Array positions are defined exactly as for clon +c and clat, listed next, except that, in general, when +c flag 3 is set to a value, flag 4 is set to the same +c value as 3, and when flag 5 is set to a value, flag +c 6 is set to the same value as 5. This is because +c 3 & 4 are for the 850 mb winds, and if either u or +c v is missing, we obviously can't calculate the +c magnitude of the wind. The same applies for 5 & 6, +c which are for the 700 mb winds. And also for reference, +c here is a list of all the variables & levels for the +c tracked parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms). +c For the third position (max_#_parms), here they are: +c 1: Relative vorticity at 850 mb +c 2: Relative vorticity at 700 mb +c 3: Wind circulation difference at 850 mb +c 4: NOT CURRENTLY USED +c 5: Wind circulation difference at 700 mb +c 6: NOT CURRENTLY USED +c 7: Geopotential height at 850 mb +c 8: Geopotential height at 700 mb +c 9: Mean Sea Level Pressure +c 10: Wind circulation difference at 10 m +c 11: Relative vorticity at 10 m +c 12: Lower-level thickness (500-850) +c 13: Upper-level thickness (200-500) +c 14: Deep-Layer thickness (200-850) +c +c xmaxwind Contains maximum near-surface wind near the storm +c center for each storm at each forecast hour. +c stderr Standard deviation of the position "errors" of the +c different parameters for each storm at each time. +c fixlat,fixlon: Contain the final coordinates for each storm at +c each forecast hour. These coordinates are a +c weighted average of all the individual parameter +c positions (hgt, zeta, mslp, vmag). +c cvort_maxmin: Contains the characters 'max' or 'min', and is +c used when calling the find_maxmin routine for the +c relative vorticity (Look for max in NH, min in SH). +c vradius Contains the distance from the storm fix position to +c each of the various near-surface wind threshhold +c distances in each quadrant. +c (3,4) ==> (# of threshholds, # of quadrants) +c See subroutine getradii for further details. +c wfract_cov Fractional coverage (areal coverage) of winds +c exceeding a certain threshold (34, 50, 64 kts) in +c each quadrant. +c (5,5,3) ==> (# of quadrants + 1, # of distance bins, +c # of thresholds). +c The "extra" array size for quadrants (5, instead of 4) +c is there to hold the total (i.e., "whole disc") +c statistics. +c See subroutine get_fract_wind_cov for further details +c +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c isastorm Character array used in the call to is_it_a_storm, +c tells whether the minimum requirement for an MSLP +c gradient was met (isastorm(1)), whether for the midlat +c and tcgen cases if a closed mslp contour was found +c (isastorm(2)), and if a circulation exists at 850 mb +c (isastorm(3)). Can have a value of 'Y' (requirement +c met), 'N' (requirement not met) or 'U' (requirement +c undetermined, due to the fact that no center location +c was found for this parameter). +c maxmini These 2 arrays contain the i and j indeces for the +c maxminj max/min centers that are found using the rough check +c in first_ges_ctr and subsequent routines. Only needed +c for a midlatitude or a genesis run, NOT needed for a +c TC tracker run. +c stormct Integer: keeps and increments a running tab of the +c number of storms that have been tracked at any time +c across all forecast hours. Used only for midlat or +c tcgen runs. +c gridprs This contains the actual value of the minimum pressure +c at a gridpoint. The barnes analysis will return an +c area-averaged value of pressure; this variable will +c contain the actual minimum value at a gridpoint near +c the lat/lon found by the barnes analysis. +c closed_mslp_ctr_flag This flag keeps track of the value of the +c closed contour flag returned from subroutine +c check_closed_contour. +c vt850_flag This flag keeps track of the value of the flag for +c the 850 mb Vt check. +c----- +c + USE def_vitals; USE inparms; USE tracked_parms; USE error_parms + USE set_max_parms; USE level_parms; USE grid_bounds; USE trkrparms + USE contours; USE atcf; USE radii; USE trig_vals; USE phase + USE gen_vitals; USE structure; USE verbose_output + USE waitfor_parms; USE module_waitfor; USE netcdf_parms + USE tracking_parm_prefs +c + implicit none +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (cint_stuff) contour_info +c + character, allocatable :: closed_mslp_ctr_flag(:,:)*1 + character, allocatable :: vt850_flag(:,:)*1 + character :: r34_check_okay*1,had_to_try_backup_850_vt_check*1 + character :: need_to_expand_r34(4)*1,ncfile_has_hour0*1 + character*(*), intent(in) :: ncfile + integer :: ncfile_id +c integer, parameter :: nreadparms=17 +cPENG----2018-06-07 ------------------------ + integer, parameter :: nreadparms=19 + + real, allocatable :: prstemp(:),iwork(:) + integer, parameter :: numdist=14,numquad=4,lout=51 + integer, allocatable :: prsindex(:) + integer imax,jmax,ifh,ist,irf,jj,istmp,ifhtemp,itret,ivpa + integer isiret1,isiret2,isiret3,idum,m,iix,jjx,imode,numtcv + integer iha,isa,iua,iva,iza,maxstorm,ivort,ifix,jfix,issret + integer imoa,imoca,iksa,isda,ileadtime,leadtime_check + integer ioaret,ioaxret,ifgcret,ifmret,igugret,isoiret,icccret + integer igrret,igmwret,iorret,ignret,iovret,icbret,igucret,ita +cPENG----2018-06-07 ------------------------ + integer ish, irh + + integer ifilret,ifret,iaret,isret,iotmret,iwa,iisa,sl_counter + integer iicret,igcret,pfcret,igwcret,imbowret,iatret + logical(1), allocatable :: valid_pt(:,:) + logical(1), allocatable :: masked_outc(:,:),masked_out(:,:) + logical(1) readflag(nreadparms),calcparm(maxtp,maxstorm) + logical(1) tracking_previously_known_storms + logical(1) need_to_flip_lats,need_to_flip_lons + logical(1) file_open,first_time_thru_getradii + character cvort_maxmin*3,isastorm(3)*1,ccflag*1,gotten_avg_value*1 + character cmaxmin*3,get_last_isobar_flag*1,wcore_flag*1 + character gfilename*120,ifilename*120,gridmove_status*7 + integer vradius(3,4),igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) +cPENG----2018-06-07 ------------------------ + real igridushe,imeanushe + real igridrhum,imeanrhum + real igridtemp,imeantemp + + integer maxmini(maxstorm),maxminj(maxstorm),pdf_ct_bin(16) + integer ifcsthour,stormct,prevstormct,kf,istmspd,istmdir,iggret + integer igiret,iuret,jdum,icount,ilonfix,jlatfix,igpret,ifhmax + integer ibeg,jbeg,iend,jend,ix1,ix2,n,ilev,npts,icpsa,igzvret +cPENG----2018-06-07 ------------------------ + integer iushet, irhumt, itempt + real wcore_mean_val,wcore_point_max + + integer igfwret,ioiret,igisret,iofwret,iowsret,igwsret,igscret + integer pdf_ct_tot,lugb,lugi,iret,icmcf,iccfh,ivt8f + integer waitfor_gfile_status,waitfor_ifile_status,ncfile_tmax + integer wait_max_ifile_wait,ivr,r34_good_ct,itha,ilma,inctcv + integer date_time(8) + character (len=10) big_ben(3) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridprs(maxstorm,maxtime) + real wfract_cov(5,5,3) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real ike(max_ike_cats) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmaxwind(maxstorm,maxtime),xmeanzeta + real stderr(maxstorm,maxtime),xval(maxtp),cps_vals(3) + real gridpoint_maxmin,dist,distnm,xknots,xmaxspeed + real uvgeslon,uvgeslat,xavg,stdv,search_cutoff,re,ri,dx,dy + real xinp_fixlat,xinp_fixlon,degrees,plastbar,rlastbar + real xinterval_fhr,cc_time_sum_tot,cc_time_sum_yes + real rmax,sdp,wdp,paramb,vtl_slope,vtu_slope + real xsfclon,xsfclat,cc_time_pct,radmax,r34_dist_thresh + real prev_latmax,prev_latmin,prev_lonmax,prev_lonmin + real vradius_km,hold_old_contint,tcv_max_wind_ms + real tcv_mslp_pa,r34_from_tcv,roci_from_tcv + real proci_from_tcv,prs_contint_thresh + integer enable_timing,igrct + character(pfc_cmd_len) :: pfc_final +c + prev_latmax = -999.0 + prev_latmin = -999.0 + prev_lonmax = -999.0 + prev_lonmin = -999.0 + enable_timing=trkrinfo%enable_timing + icmcf = 0 + ivt8f = 0 + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + allocate (closed_mslp_ctr_flag(maxstorm,ifhmax),stat=icmcf) + allocate (vt850_flag(maxstorm,ifhmax),stat=ivt8f) + ! Initialize flags to 'u', not 'n'. That way, + ! when we are evaluating its value back over recent past hours, + ! we can distinguish a "no" value from an initialized value of + ! 'u' for which a storm hadn't yet been detected. + closed_mslp_ctr_flag = 'u' + vt850_flag = 'u' + endif + + allocate (prsindex(maxstorm),stat=iisa) + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iisa /= 0 .or. iva /= 0 .or. iwa /= 0 .or. icmcf /= 0 .or. + & ivt8f /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating prsindex,' + print *,'!!! prstemp or iwork array for storms: iisa = ',iisa + print *,'!!! iva= ',iva,' iwa= ',iwa,' icmcf= ',icmcf + print *,'!!! ivt8f= ',ivt8f + endif + itret = 94 + return + endif + + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + clon = 0.0 + clat = 0.0 + stderr = stermn ! initialize stderr to 0.1 (error_parms) + itret = 0 + xmaxwind = 0.0 + stormct = 0 + + ! It is critical to initialize the gridprs array to something + ! greater than normal atmospheric pressures (I've chosen 9999.99 + ! mb). This is so that in the sort on pressure before stormloop, + ! the top of the sorting index array will be filled with pressure + ! values from active storms, while those inactive 9999 storms + ! will fill the bottom of the sorting index array (prsindex). + + gridprs = 999999.0 + fixlon = -999.0 + fixlat = -999.0 + + if (inp%file_seq == 'multi') then + ! Each tau will have a separate file, starting with unit + ! number 200 (GRIB data) and 5200 (GRIB index file) and + ! incrementing upwards from there for each tau. + if (trkrinfo%gribver == 1) then + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + else + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + endif + else + ! All lead times are included in one big file. These values + ! for lugb and lugi will remain static for all taus. + lugb = 11 + lugi = 31 + endif + + ifh = 1 + + if ( verb .ge. 3 ) then + print *,'top of tracker, ifh= ',ifh,' ifhmax= ',ifhmax + endif + + ifhloop: do while (ifh <= ifhmax) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------*' + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* New forecast hour: ',i4,':',i2.2) + print *,'*-------------------------------------------*' + endif + + if (inp%file_seq == 'multi') then + + lugb = lugb + 1 + lugi = lugi + 1 + + call get_grib_file_name (ifh,gfilename,ifilename) + + if (use_waitfor == 'y') then + + ! First check for existence of grib file.... + + call waitfor(trim(gfilename),waitfor_gfile_status + & ,wait_min_age,wait_min_size,wait_max_wait + & ,wait_sleeptime) + if (waitfor_gfile_status /= 0) then + print *,' ' + write(6,405) + write(6,406) wait_max_wait,trim(gfilename) + 405 format('ERROR: TIMEOUT from waitfor for GRIB file.') + 406 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + ! Now check for existence of index file. Use a separate + ! max_wait time -- a much shorter one -- since once the + ! grib file is there, the index file should appear within + ! a matter of seconds. Also, the index file is much + ! smaller, so set the wait_min_size accordingly. + + wait_max_ifile_wait = 180 + wait_min_size = 500 + call waitfor(trim(ifilename),waitfor_ifile_status + & ,wait_min_age,wait_min_size,wait_max_ifile_wait + & ,wait_sleeptime) + if (waitfor_ifile_status /= 0) then + print *,' ' + write(6,415) + write(6,416) wait_max_ifile_wait,trim(ifilename) + 415 format('ERROR: TIMEOUT from waitfor for INDEX file.') + 416 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + endif + + call open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: from open_grib_files, rc= ' + & ,iret + print *,'!!! Files after hour0 are missing, ' + & ,'exiting normally' + stop 0 + endif + endif + + if (trkrinfo%inp_data_type == 'grib') then + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is CLOSED' + endif + endif + + !-------------------------------------------------------------- + ! Within this next IF statement, we deal with writing out atcf + ! records for storms for the case in which we have netcdf data, + ! but that netcdf data does not have hour0 data (as of Nov 2016, + ! this is the case for FV3 data). In this case, we write out + ! missing values for the hour0 time, and then we update the + ! guess for next lead time by extrapolating data from TC Vitals. + ! Note in the IF statement itself, "iftotalmins" is the array + ! of *user-requested* lead times, meaning that the user has + ! requested to look at hour0, but the ncfile_has_hour0 flag + ! indicates the hour0 time is not in the NetCDF data. + !-------------------------------------------------------------- + + if (ifh == 1 .and. iftotalmins(ifh) == 0 .and. + & trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') then + + null_netcdf_hour0_storm_loop: do inctcv = 1,numtcv + + call output_atcfunix (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,inctcv +c & ,0,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,inctcv + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',inctcv + write (6,431) storm(inctcv)%tcv_storm_id + & ,storm(inctcv)%tcv_storm_name + 431 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + + call advect_tcvitals_from_hour0 (slonfg,slatfg,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) + + if (iatret /= 0) then + fixlon (inctcv,ifh) = -999.0 + fixlat (inctcv,ifh) = -999.0 + stormswitch(inctcv) = 2 + cycle null_netcdf_hour0_storm_loop + endif + + stormswitch(inctcv) = 1 + + enddo null_netcdf_hour0_storm_loop + + ifh = ifh + 1 + cycle ifhloop + + endif + + !-------------------------------------------------------------- + ! Make call to getgridinfo in order to get info on the imax, + ! jmax, as well as the x- and y-increments, and also to see if + ! the grid is correctly oriented for the tracker so that the + ! data go north to south and west to east or if we need to flip + ! either the lats or the lons. + !-------------------------------------------------------------- + + if (trkrinfo%inp_data_type == 'grib') then + call getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) + else + print *,' ' + print *,'!!! ERROR: trkrinfo%inp_data_type NOT VALID ' + print *,'!!! trkrinfo%inp_data_type= ',trkrinfo%inp_data_type + print *,'!!! Should have value of grib or netcdf.' + print *,'!!! EXITING....' + print *,' ' + stop 93 + endif + + if (iggret == 0) then + if ( verb .ge. 1 ) then + print *,'TEST after getgridinfo in sub tracker, ' + & ,'iggret= ',iggret + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in getgridinfo, rc= ' + & ,iggret + endif + stop 95 + endif + + if (inp%modtyp == 'regional' .and. inp%nesttyp == 'moveable') + & then + if (glatmax == prev_latmax .and. glatmin == prev_latmin .and. + & glonmax == prev_lonmax .and. glonmin == prev_lonmin) then + ! The moveable, nested regional grid has not moved since + ! the last lead time. This could be an indication that the + ! model lost the storm and so the grid has not moved to + ! stay with the cyclone center. Set a flag to indicate this. + gridmove_status = 'stopped' + else + gridmove_status = 'moving' + endif + else + gridmove_status = 'notappl' + endif + + prev_latmax = glatmax + prev_latmin = glatmin + prev_lonmax = glonmax + prev_lonmin = glonmin + + gotten_avg_value = 'n' + +c First, allocate the working data arrays.... + + if (allocated(valid_pt)) deallocate (valid_pt) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) +cPENG----2018-06-07------------------------------- + if (allocated(ushear)) deallocate (ushear) + if (allocated(rhumid)) deallocate (rhumid) + + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + + ! Allocate all of the allocatable arrays.... + + allocate (valid_pt(imax,jmax),stat=ivpa) + allocate (zeta(imax,jmax,nlevzeta),stat=iza) + allocate (u(imax,jmax,nlevs),stat=iua) + allocate (v(imax,jmax,nlevs),stat=iva) + allocate (hgt(imax,jmax,nlevhgt),stat=iha) + allocate (slp(imax,jmax),stat=isa) + allocate (tmean(imax,jmax),stat=ita) +cPENG----2018-06-07------------------------------- + allocate (ushear(imax,jmax),stat=ish) + allocate (rhumid(imax,jmax),stat=irh) + + allocate (thick(imax,jmax,nlevthick),stat=itha) + allocate (lsmask(imax,jmax),stat=ilma) + allocate (masked_out(imax,jmax),stat=imoa) + allocate (masked_outc(imax,jmax),stat=imoca) + + ita=0 + icpsa=0 + if (phaseflag == 'y') then + if (phasescheme == 'cps' .or. phasescheme == 'both') then + if (allocated(cpshgt)) deallocate (cpshgt) + allocate (cpshgt(imax,jmax,nlevs_cps),stat=icpsa) + endif + endif + +c if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. +c & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. +c & itha /= 0 .or. imoa /= 0 .or. imoca /= 0 .or. ilma /= 0) +c & then +cPENG----2018-06-07 ------------------------ + if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. + & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. + & itha /= 0 .or. imoa /= 0 .or. imoca /= 0 .or. ilma /= 0 + & .or. ish /= 0 .or. irh /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating arrays.' + print *,'!!! iza = ',iza,' iua= ',iua,' iha= ',iha + print *,'!!! iva = ',iva,' isa= ',isa,' icpsa= ',icpsa + print *,'!!! iksa = ',iksa,' isda= ',isda,' ivpa= ',ivpa + print *,'!!! ita = ',ita,' imoa= ',imoa,' imoca= ',imoca + print *,'!!! itha = ',itha,' ilma= ',ilma + endif + itret = 94 + return + endif + + masked_out = .false. ! Initialize all pts to false at each hr + masked_outc = .false. ! Initialize all pts to false at each hr + + if ( verb .ge. 3 ) then + print *,'in beginning of tracker, imax= ',imax,' jmax= ',jmax + endif + +c Initialize all readflags to NOT FOUND for this forecast time, +c then call subroutine to read data for this forecast time. + + zeta = -9999.0 + u = -9999.0 + hgt = -9999.0 + v = -9999.0 + slp = -9999.0 + tmean = -9999.0 +cPENG----2018-06-07 ------------------------ + ushear = -9999.0 + rhumid = -9999.0 + + readflag = .FALSE. + + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: b4 getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + + if (trkrinfo%inp_data_type == 'grib') then + call getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,32) date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: after getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + +c Count how many parms were successfully read for this fcst time. +c Also, for right now, put the value of readflag into all of the +c calcparms for parameters 3 through 9. Note that in getdata we +c read in 17 parms, but in this next loop we only check the +c readflags up to maxtp (= 14 as of 7/2015). That's because +c parms 12 & 13 are for 500 mb u & v, which are not used for +c tracking, only for calculating the deep layer mean wind for +c the next guess, and parm 14 is the 300-500 mb mean temperature, +c which is used for determining storm phase. Parms 10 & 11 are +c for the near-surface winds, which are used in estimating surface +c winds near the storm, and will now also be used as a +c parameter for position estimates. Finally, parm 17 is the +c land-sea mask, which is not used as a tracking parm. + + idum = 0 + do irf = 1,nreadparms + if (readflag(irf)) idum = idum + 1 + if (irf > 2 .and. irf < 10) then + ! calcparm for parms > 9 is done further below. + do jj=1,maxstorm + calcparm(irf,jj) = readflag(irf) + enddo + endif + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Of ',nreadparms,' readable parms, you read in ',idum + print *,'parms for this fcst hour from the input grib file.' + endif + +c If not enough tracked parms were read in, exit the program.... + + if (idum == 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in subroutine tracker' + print *,'!!! Not enough tracked parms read in from getdata.' + print *,'!!! Check for a problem with the input GRIB file.' + print *,'!!! Model identifier = ',inp%model + print *,'!!! STOPPING EXECUTION FOR THIS MODEL' + endif + itret = 99 + ifhtemp = ifh + do while (ifhtemp <= ifhmax) + do istmp=1,maxstorm + fixlon (istmp,ifhtemp) = -999.0 + fixlat (istmp,ifhtemp) = -999.0 + enddo + ifhtemp = ifhtemp + 1 + enddo +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) + if (ifh == 1) then + ! Per Jim Gross (1/01), if the tracker ran but was unable + ! to get an initial fix (or, in this case, unable to get + ! the data needed to run), write out zeroes for the 00h + ! fixes to indicate that the tracker ran unsuccessfully, + ! but don't write out any subsequent forecast times + ! with zeroes.... + vradius = 0 + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initial value of 'undetermined' + do istmp = 1,maxstorm + if (stormswitch(istmp) /= 3) then + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0,-999.0,inp,istmp + & ,ifcsthour,0.0,0.0,vradius,maxstorm + & ,trkrinfo,-99.0,-99.0,-99.0,cps_vals + & ,wcore_flag,ioaxret) + call output_hfip (-999.0,-999.0,inp,istmp + & ,ifh,0.0,0.0,vradius,-99.0,ioaxret) + endif + enddo + endif + return + endif + +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for z850, z700 and mslp.... + + if (user_wants_to_track_gph850 == 'n' .or. + & user_wants_to_track_gph850 == 'N') then + do jj=1,maxstorm + calcparm(7,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_gph700 == 'n' .or. + & user_wants_to_track_gph700 == 'N') then + do jj=1,maxstorm + calcparm(8,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_mslp == 'n' .or. + & user_wants_to_track_mslp == 'N') then + do jj=1,maxstorm + calcparm(9,jj) = .FALSE. + enddo + endif + + +c Parameters 1 & 2 are abs vorticity at 850 & 700. If the data +c files had this parm at 850 & 700 (ECMWF & UKMET do NOT), then +c we don't need to re-calculate relative vorticity, we just need +c to subtract out the Coriolis component. If the files did not +c have vorticity, then we need to calculate relative vorticity. +c If we're able to read vorticity or calculate it, then set the +c vorticity calcparms to TRUE for all storms for now. + + vortloop: do ivort=1,2 + + if (ivort == 1) then + if (user_wants_to_track_zeta850 == 'n' .or. + & user_wants_to_track_zeta850 == 'N') then + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (ivort == 2) then + if (user_wants_to_track_zeta700 == 'n' .or. + & user_wants_to_track_zeta700 == 'N') then + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (readflag(ivort)) then + + call subtract_cor (imax,jmax,dy,ivort) + + do jj=1,maxstorm + calcparm(ivort,jj) = .TRUE. + enddo + else + if (ivort == 1) then + if (readflag(3) .and. readflag(4)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(1,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + endif + else + if (readflag(5) .and. readflag(6)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(2,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + endif + endif + endif + + enddo vortloop + + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for user preferences for the wind circulation +c difference at 850 & 700... + + if (readflag(3) .and. readflag(4)) then + if (user_wants_to_track_wcirc850 == 'n' .or. + & user_wants_to_track_wcirc850 == 'N') then + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(3,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + endif + + if (readflag(5) .and. readflag(6)) then + if (user_wants_to_track_wcirc700 == 'n' .or. + & user_wants_to_track_wcirc700 == 'N') then + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(5,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + endif + + +c Compute the sfc vorticity if sfc_u and sfc_v have been read in. + + if (readflag(10) .and. readflag(11)) then + + if (user_wants_to_track_wcircsfc == 'n' .or. + & user_wants_to_track_wcircsfc == 'N') then + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(10,jj) = .TRUE. + enddo + endif + + if (user_wants_to_track_zetasfc == 'n' .or. + & user_wants_to_track_zetasfc == 'N') then + do jj=1,maxstorm + calcparm(11,jj) = .FALSE. + enddo + else + ! The 3 in the next call to rvcal is to indicate the 3rd + ! level for the zeta array, which is for the surface (or + ! 10m) data. + call rvcal (imax,jmax,dx,dy,3,valid_pt) + do jj=1,maxstorm + calcparm(11,jj) = .TRUE. + enddo + endif + + else + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + calcparm(11,jj) = .FALSE. + enddo + endif + + +c Compute the thicknesses for 200-850, 200-500 and 500-850 mb +c if the gp hgt fields have been read in for 200, 500 and 850. + + if (readflag(7) .and. readflag(15) .and. readflag(16)) then + + call thickness_calc (imax,jmax,valid_pt) + + do jj=1,maxstorm + + if (user_wants_to_track_thick500850 == 'n' .or. + & user_wants_to_track_thick500850 == 'N') then + calcparm(12,jj) = .FALSE. + else + calcparm(12,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200500 == 'n' .or. + & user_wants_to_track_thick200500 == 'N') then + calcparm(13,jj) = .FALSE. + else + calcparm(13,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200850 == 'n' .or. + & user_wants_to_track_thick200850 == 'N') then + calcparm(14,jj) = .FALSE. + else + calcparm(14,jj) = .TRUE. + endif + + enddo + else + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Thickness will not be tracked since at least' + print *,'one of the gp height fields was not read in.' + print *,' readflag(7) -- 850 mb ---> ',readflag(7) + print *,' readflag(15) -- 500 mb ---> ',readflag(15) + print *,' readflag(16) -- 200 mb ---> ',readflag(16) + print *,' ' + endif + do jj=1,maxstorm + calcparm(12,jj) = .FALSE. + calcparm(13,jj) = .FALSE. + calcparm(14,jj) = .FALSE. + enddo + endif + +c --------------------------------------------------------------- +c Now call find_maxmin for the variables zeta, hgt and slp. Only +c process those storms for which stormswitch is set to 1. If a +c storm is selected to be processed, we still have to check the +c calcparm for each parameter, to make sure that the particular +c parm exists at that level and is able to be processed. +c +c The following commented-out data statements are just included +c as a reference so you can see the array positioning of the +c different parameters and levels that are read in: +c +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,sfc,sfc +c ,100,100,100,100,100/ +c data iglev /850,700,850,850,700,700,850,700,0,sfc,sfc +c ,500,500,400,500,200/ +c +c And also for reference, here are the variables / levels for +c the *tracked* parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c NOTE: For mid-latitude cases, we will track ONLY mslp, which +c is why we set all the other calcparms to 'false' just below. + + if (trkrinfo%type == 'midlat') then + do m = 1,maxstorm + calcparm(1,m) = .false. + calcparm(2,m) = .false. + calcparm(3,m) = .false. + calcparm(4,m) = .false. + calcparm(5,m) = .false. + calcparm(6,m) = .false. + calcparm(7,m) = .false. + calcparm(8,m) = .false. + calcparm(10,m) = .false. + calcparm(11,m) = .false. + calcparm(12,m) = .false. + calcparm(13,m) = .false. + calcparm(14,m) = .false. + enddo + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + call sort_storms_by_pressure (gridprs,ifh,maxstorm,prsindex + & ,issret) + if ( (ifh == 1) .or. + & (ifh == 2 .and. trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') ) then + stormct = numtcv + endif + endif + + prevstormct = stormct + tracking_previously_known_storms = .true. + + stormloop: do sl_counter = 1,maxstorm + + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initialized value of 'undetermined' + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + ist = prsindex(sl_counter) + else + ist = sl_counter + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + + if (ist == (prevstormct + 1)) then + + ! For the mid-latitude and tropical cyclogenesis cases, we + ! need to scan the mslp field to find new storms. If we + ! are at this point inside the if statement in stormloop, + ! then that means we have looped through and attempted to + ! track all storms that have already been found up to this + ! point in the forecast, and we need to scan the field for + ! any new storms at this forecast hour. If this is for + ! forecast hour = 0, then right off the bat we may be + ! scanning the field (if there were no tcvitals records + ! read in for this forecast), since ist = 1 and + ! (prevstormct + 1) = 0 + 1 = 1. All that the call just + ! below to first_ges_center does is return a rough idea + ! of the location of new lows; more specific locations are + ! obtained through the barnes analysis tracking algorithm + ! further below. + + if (readflag(9)) then + if (ifh > 1) then + ! We need the use of 2 different masks. One + ! (masked_out) is to be used when looking for new lows, + ! so that after we find a new low, we mask out the + ! surrounding area so we don't find it on a subsequent + ! search for this forecast hour. The other + ! (masked_outc) is used in the routine to check for a + ! closed contour. If checking for a closed contour + ! at, say 70W/25N, this and surrounding points may have + ! already been masked out in first_ges_center, so "N" + ! would misleadingly/incorrectly be returned from + ! check_closed_contour, so that is why we need 2 masks. + ! But now after the first forecast hour (t=0), the way + ! we have this set up is that we track previously known + ! storms first, and once we're done with them, we + ! search for new storms at that same forecast hour. + ! But when looking for new storms, we need to know the + ! positions of the previously tracked storms at this + ! current forecast hour, so we copy the masked_outc + ! array to masked_out in this case.... + + masked_out = masked_outc + + endif + call first_ges_center (imax,jmax,dx,dy,'mslp',slp + & ,'min',trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) + tracking_previously_known_storms = .false. + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In subroutine tracker, readflag' + print *,'!!! for mslp indicates that the mslp data' + print *,'!!! is not available for this forecast ' + print *,'!!! hour, and it is needed for a "midlat"' + print *,'!!! or "tcgen" run of the tracker. ' + print *,'!!! We will exit....' + print *,'!!! readflag(9) = ',readflag(9) + print *,'!!! ifh= ',ifh + print *,' ' + endif + itret = 98 + return + endif + endif + endif + + xval = 0.0 ! initialize entire xval array to 0 + isastorm = 'U' ! re-initialize flag for each time, each storm + + select case (stormswitch(ist)) + + case (1) + + vradius = 0 + + if ( verb .ge. 2 ) then + print *,' ---------------------------------------------' + print *,' | *** TOP OF STORM LOOP *** ' + print *,' | Beginning of storm loop in tracker for' + print *,' | Storm number ',ist + write (6,418) ifhours(ifh),ifclockmins(ifh) + 418 format (1x,' | Forecast hour: ',i4,':',i2.2) + print *,' | Storm name = ',storm(ist)%tcv_storm_name + print *,' | Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,' ---------------------------------------------' + print *,' ' + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3 + & ,'_',i3.3,a1,'_',i4.4,a1,'_',a3) + + endif +c First, make sure storm is within the grid boundaries... + + call check_bounds (slonfg(ist,ifh),slatfg(ist,ifh),ist,ifh + & ,trkrinfo,icbret) + if (icbret == 95) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + + if (slatfg(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + if (calcparm(1,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,1),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(1,ist),clon(ist,ifh,1),clat(ist,ifh,1) + & ,xval(1),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(2,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,2),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(2,ist),clon(ist,ifh,2),clat(ist,ifh,2) + & ,xval(2),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(7,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,1),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(7,ist) + & ,clon(ist,ifh,7),clat(ist,ifh,7),xval(7) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(8,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,2),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(8,ist) + & ,clon(ist,ifh,8),clat(ist,ifh,8),xval(8) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(9,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for mslp' + endif + + call find_maxmin (imax,jmax,dx,dy,'slp' + & ,slp,'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(9,ist) + & ,clon(ist,ifh,9),clat(ist,ifh,9),xval(9) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(11,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for sfc zeta' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,3),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(11,ist),clon(ist,ifh,11),clat(ist,ifh,11) + & ,xval(11),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 + + if (calcparm(12,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 500-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,1),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(12,ist) + & ,clon(ist,ifh,12),clat(ist,ifh,12),xval(12) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(13,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-500 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,2),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(13,ist) + & ,clon(ist,ifh,13),clat(ist,ifh,13),xval(13) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(14,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,3),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(14,ist) + & ,clon(ist,ifh,14),clat(ist,ifh,14),xval(14) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c Now get centers for wind circulation at 700 & 850 mb and +c at 10m. First, get a modified guess lat/lon position for +c wind circulation. Do this because we will be searching +c for this wind circulation center over a smaller area and +c so it's more crucial to have a better first guess position. +c This modified guess position will be an average of the first +c guess position for this time and the fix positions for this +c time from some of the other parameters. + + if (slatfg(ist,ifh) >= 0.0) then + cmaxmin = 'max' + else + cmaxmin = 'min' + endif + + if (calcparm(3,ist) .and. calcparm(4,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 850 mb ' + endif + + print *,' ' + print *,'Before first call to get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' inp%modtyp= ',inp%modtyp + print *,' cmaxmin= ',cmaxmin + print *,' nlev850= ',nlev850 + print *,' u(1,1,nlev850)= ',u(1,1,nlev850) + print *,' u(imax,jmax,nlev850)= ',u(imax,jmax,nlev850) + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' calcparm(3,ist)= ',calcparm(3,ist) + print *,' clon(ist,ifh,3)= ',clon(ist,ifh,3) + print *,' clat(ist,ifh,3)= ',clat(ist,ifh,3) + print *,' xval(3)= ',xval(3) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,141) date_time(5),date_time(6),date_time(7) + 141 format (1x,'TIMING: Before GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,850,valid_pt,calcparm(3,ist) + & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,142) date_time(5),date_time(6),date_time(7) + 142 format (1x,'TIMING: After GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,850,valid_pt,calcparm(3,ist) +c & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + endif + else + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + clon(ist,ifh,3) = 0.0 + clat(ist,ifh,3) = 0.0 + endif + endif + + if (calcparm(5,ist).and. calcparm(6,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 700 mb ' + endif + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,143) date_time(5),date_time(6),date_time(7) + 143 format (1x,'TIMING: Before GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,700,valid_pt,calcparm(5,ist) + & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,144) date_time(5),date_time(6),date_time(7) + 144 format (1x,'TIMING: After GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,700,valid_pt,calcparm(5,ist) +c & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + endif + else + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + clon(ist,ifh,5) = 0.0 + clat(ist,ifh,5) = 0.0 + endif + endif + + if (calcparm(10,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for the' + print *,'surface (10m) level' + endif + + ! NOTE: The 1020 in the call here is just a number/code + ! to indicate to the subroutine to process sfc winds. + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,145) date_time(5),date_time(6),date_time(7) + 145 format (1x,'TIMING: Before GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,1020,valid_pt,calcparm(10,ist) + & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) + & ,trkrinfo,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,146) date_time(5),date_time(6),date_time(7) + 146 format (1x,'TIMING: After GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,1020,valid_pt,calcparm(10,ist) +c & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) +c & ,trkrinfo,igucret) + + if (igwcret /= 0) then + calcparm(10,ist) = .FALSE. + endif + else + calcparm(10,ist) = .FALSE. + clon(ist,ifh,10) = 0.0 + clat(ist,ifh,10) = 0.0 + endif + endif + +c ------------------------------------------------------ +c All of the parameter center fixes have been done. Now +c average those positions together to get the best guess +c fix position. If a center fix is able to be made, then +c call subroutine get_max_wind to get the maximum near- +c surface wind near the center, and then call get_next_ges +c to get a guess position for the next forecast hour. + + if (stormswitch(ist) == 1) then + + call fixcenter (clon,clat,ist,ifh,calcparm + & ,slonfg(ist,ifh),slatfg(ist,ifh),inp + & ,stderr,fixlon,fixlat,xval,maxstorm,ifret) + + if (ifret == 0) then + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'regional')then + if (fixlon(ist,ifh) > (trkrinfo%eastbd + 7.0) .or. + & fixlon(ist,ifh) < (trkrinfo%westbd - 7.0) .or. + & fixlat(ist,ifh) > (trkrinfo%northbd + 7.0) .or. + & fixlat(ist,ifh) < (trkrinfo%southbd - 7.0)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! will NOT be made for this time due' + print *,'!!! the storm being more than 7 degrees' + print *,'!!! outside the user-specified lat/lon' + print *,'!!! bounds for this run. We will stop' + print *,'!!! tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + 432 format (1x,'!!! Fcst hr = ',i4,':',i2.2) + print *,'!!! fixlat= ',fixlat(ist,ifh) + print *,'!!! fixlon= ',fixlon(ist,ifh) + print *,'!!! User East Bound = ',trkrinfo%eastbd + print *,'!!! User West Bound = ',trkrinfo%westbd + print *,'!!! User North Bound = ',trkrinfo%northbd + print *,'!!! User South Bound = ',trkrinfo%southbd + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + endif + cycle stormloop + endif + endif + else + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + +c Just because we've found a center doesn't mean there is +c actually a storm there. I noticed in the first year that +c for some decaying or just weak storms, the tracker would +c identify a center to follow, but it may have only been +c a weak trough passing by, or something else that's not +c our storm. This next subroutine checks to see that the +c surface pressure gradient and/or tangential winds at +c 850 mb resemble a storm. It is called twice; the first +c time for MSLP, the 2nd time for 850 mb winds. We will +c apply these storm-checking criteria if either the mslp +c or v850 check come back negative. Remember, there +c is the possibility that centers could not be found for +c 1 or both of these parameters, in which case the isastorm +c flag will have a value of 'U', for "undetermined". + + isiret1 = 0; isiret2 = 0; isiret3 = 0 + + print *,' ttest, ifret= ',ifret + + if (ifret == 0) then + + print *,' ttest, calcparm(9,ist)= ',calcparm(9,ist) + + if (calcparm(9,ist)) then + + ! Do a check of the mslp gradient.... + + print *,' ttest, in IF part: ' + print *,' clon(ist,ifh,9)= ',clon(ist,ifh,9) + print *,' clat(ist,ifh,9)= ',clat(ist,ifh,9) + print *,' xval(9)= ',xval(9) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,clon(ist,ifh,9),clat(ist,ifh,9) + & ,xval(9),trkrinfo,isastorm(1),isiret1) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for mslp (e.g., + ! maybe the mslp fix was too far away from the + ! guess?), then this check isn't performed. We are + ! changing this so that the mslp gradient check will + ! still be performed, but using the mean fixlat and + ! fixlon positions as the center. Still, we first + ! need to check to see if mslp was even read in. If + ! it wasn't, then we are just out of luck. + + print *,' ttest, in ELSE part: ' + + if (trkrinfo%use_backup_mslp_grad_check == 'y' .or. + & trkrinfo%use_backup_mslp_grad_check == 'Y') then + + print *,' ttest ELSE, readflag(9)= ',readflag(9) + + if (readflag(9)) then + + print *,'ttest ELSE A, ist= ',ist,' ifh= ',ifh + print *,'ttest ELSE A, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE A, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,9999.0,ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + + print *,'ttest ELSE B, ifilret= ',ifilret + + if (ifilret == 0) then + + print *,'ttest ELSE B, ifilret= ',ifilret + print *,'ttest ELSE B, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE B, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,gridpoint_maxmin,trkrinfo,isastorm(1) + & ,isiret1) + + if (isiret1 == 0) then + ! Even though calcparm(9) is FALSE and mslp + ! will not be used for center-fixing + ! purposes, we need to fill the clat and clon + ! arrays just a few lines below so that + ! calls to fix_latlon_to_ij below do not + ! get screwed up. So, into the clat and clon + ! arrays we put the mean fixlat and fixlon + ! positions for this lead time. + clat(ist,ifh,9) = fixlat(ist,ifh) + clon(ist,ifh,9) = fixlon(ist,ifh) + xval(9) = gridpoint_maxmin + endif + + endif + + endif + + endif + + endif + + ! If we have found a valid mslp gradient, then make + ! a call to fix_latlon_to_ij to (1) get the actual + ! gridpoint value of the mslp (the value previously + ! stored in xval(9) is an area-averaged value coming + ! from the barnes analysis), and (2) to get the + ! (i,j) indices for this gridpoint to be used in the + ! call to check_closed_contour below. + ! + ! NOTE: If a mslp fix was not made, or if the mslp + ! "isastorm" flag comes back as no, we make the same + ! call to fix_latlon_to_ij, but we use the mean fix + ! position as our input to search around, and then + ! basically we just find the lowest mslp near that + ! mean fix position. There is a check on the value + ! of xinp_fixlat and xinp_fixlon to make sure that + ! they contain valid values and not just the + ! initialized -999 values. + + if (isiret1 == 0 .and. isastorm(1) == 'Y') then + xinp_fixlat = clat(ist,ifh,9) + xinp_fixlon = clon(ist,ifh,9) + if (verb >= 3) then + print *,' ttest at location C IF....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + else + xinp_fixlat = fixlat(ist,ifh) + xinp_fixlon = fixlon(ist,ifh) + if (verb >= 3) then + print *,' ttest at location C ELSE....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + endif + + if (xinp_fixlat > -99.0 .and. xinp_fixlon > -990.0) + & then + if (verb >= 3) then + print *,' ttest at location D' + endif + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,xinp_fixlon,xinp_fixlat + & ,xval(9),ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (verb >= 3) then + print *,' ttest at location E, ifilret= ',ifilret + endif + if (ifilret == 0) then + gridprs(ist,ifh) = gridpoint_maxmin + else + ! Search went out of regional grid bounds.... + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + print *,' ttest at location F' + + ! For a "tracker" case, check to see if the user has + ! requested to compute and write out the ROCI. If + ! so, then we make a call to check_closed_contour, + ! being sure to specify 999 as the number of levels + ! to check.... + + if (isiret1 == 0 .and. isastorm(1) == 'Y' .and. + & trkrinfo%type == 'tracker') then + + if (trkrinfo%want_oci) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + if (xval(9) < 1100.0) then + ! Pressure units are in mb... + prs_contint_thresh = 4.0 + elseif (xval(9) >80000.0) then + ! Pressure units are in Pa... + prs_contint_thresh = 400.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' tracker. The mslp value' + print *,' (xval(9)) is not in range.' + print *,' before call to' + print *,' check_closed_contour.' + print *,' xval(9) = ',xval(9) + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + if (trkrinfo%contint < prs_contint_thresh) then + hold_old_contint = trkrinfo%contint + trkrinfo%contint = prs_contint_thresh + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before going into routine to diagnose' + print *,'the ROCI for a tracker run, the ' + print *,'requested contour interval is being ' + print *,'adjusted up (coarser) to avoid having' + print *,'the contour check routine break and ' + print *,'return an invalid value.' + print *,'User-requested contint value (Pa) = ' + & ,hold_old_contint + print *,'Modified contint value (Pa) = ' + & ,trkrinfo%contint + endif + endif + + masked_outc = .false. + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ' + & ,rlastbar,' nm' + print *,' ' + endif + + endif + + endif + + ! For the midlat & tcgen cases, do a check to see if + ! there is a closed mslp contour. The ifix and jfix + ! values passed into check_closed_contour are the + ! values for the (i,j) at the gridpoint minimum, + ! which was obtained just above from the call to + ! fix_latlon_to_ij. + ! UPDATE 7/12/2016 tpm: A change was made to fix a + ! hole in the logic. Previously, for a genesis run + ! (type = midlat or tcgen), if a fix was not made + ! for mslp, then the isastorm(1) flag would not be + ! 'Y', and so the call to check_closed_contour in + ! the following IF statement would not be made, and + ! that would prevent the mask from getting updated + ! for this particular storm, allowing the same storm + ! to be detected when the scan for new storms takes + ! place at this lead time (i.e., after all previously- + ! known storms from the last lead time have been + ! tracked). As a fix, if that isastorm(1) flag is not + ! 'Y', then we call a new subroutine which updates the + ! mask based on the circulation at 850 mb. + + if (isastorm(1) == 'Y' .and. isiret1 == 0 .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ',rlastbar + & ,' nm' + print *,' ' + endif + + ! This next bit of code adds a second layer of closed + ! contour checking. This is to decrease the + ! occurrence of interrupted midlat and tcgen tracks, + ! which usually happens when the closed contour + ! criterion is not met for one time period. So in + ! this next code, we check to see if the ccflag was + ! 'y' for at least half the time over the last 24h. + ! For time periods shorter than 24h (e.g., the storm + ! was just detected at 144h and we are now at 156h), + ! the threshold is still that for at least half of + ! the time the system has been detected as a storm, + ! it must have a ccflag value of 'y'. + + if (ccflag == 'y') then + closed_mslp_ctr_flag(ist,ifh) = 'y' + else + closed_mslp_ctr_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & closed_mslp_ctr_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (closed_mslp_ctr_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.50) then + ccflag = 'y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE ON CLOSED CONTOUR CHECK: The' + print *,' ccflag returned for this hour was' + print *,' NO, but a check of recent ccflags' + print *,' indicates that more than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + ccflag = 'n' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!! NOTE ON CLOSED CONTOUR CHECK: The' + print *,'!! ccflag returned for this hour was' + print *,' NO, and a check of recent ccflags' + print *,' indicates that less than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + if (ccflag == 'y') then + isastorm(2) = 'Y' + else if (ccflag == 'n') then + isastorm(2) = 'N' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*---------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*---------------------------------------*' + print *,' ' + endif + + + else if (isastorm(1) /= 'Y' .and. + & calcparm(3,ist) .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + ! The isastorm(1) flag indicates that a mslp gradient + ! could not be found at this lead time, so the mask + ! cannot be updated using mslp. Instead, + ! do a check of the 850 mb wind circulation + ! surrounding the 850 wind circulation fix, and then + ! set the mask to be TRUE for all points within the + ! area where mean cyclonic Vt exceed +1 m/s.... + +c call check_closed_contour (imax,jmax,ifix,jfix,slp +c & ,valid_pt,masked_outc,ccflag,'min',trkrinfo +c & ,999,contour_info,get_last_isobar_flag,plastbar +c & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Calling mask_based_on_wind_circ at ' + & ,ifcsthour + endif + + call mask_based_on_wind_circ (imax,jmax,dx,dy,850 + & ,valid_pt,masked_outc,trkrinfo + & ,clon(ist,ifh,3),clat(ist,ifh,3),inp%modtyp + & ,imbowret) + + endif + + ! For tropical cyclones, check the avg 850 mb tangential + ! windspeed close to the storm center.... + + if (trkrinfo%type == 'tcgen' .or. + & trkrinfo%type == 'tracker') then + + had_to_try_backup_850_vt_check = 'n' + + if (calcparm(3,ist)) then + + if (verb .ge. 3) then + print *,' ' + print *,'Checking 850 mb Vt speed using 850 mb ' + print *,'wind circulation fix: ' + print *,' 850 mb wcirc fix lon= ',clon(ist,ifh,3) + print *,' 850 mb wcirc fix lat= ',clat(ist,ifh,3) + print *,' Multi-parm fix lon= ',fixlon(ist,ifh) + print *,' Multi-parm fix lat= ',fixlat(ist,ifh) + print *,' ' + endif + + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,clon(ist,ifh,3),clat(ist,ifh,3) + & ,xval(3),trkrinfo,isastorm(3),isiret3) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for 850 mb wind + ! circulation (maybe the 850 mb wind circulation fix + ! was too far away from the guess?), then this check + ! isn't performed. We are changing this so that the + ! 850 mb Vt wind speed check will still be + ! performed, but using the mean fixlat and fixlon + ! positions as the center. Still, we first need to + ! check to see if 850 mb u-comp and v-comp were even + ! read in. If they weren't, then we are just out + ! of luck. + + had_to_try_backup_850_vt_check = 'y' + isiret3 = -99 + + if (trkrinfo%use_backup_850_vt_check == 'y' .or. + & trkrinfo%use_backup_850_vt_check == 'Y') then + + if (readflag(3) .and. readflag(4)) then + + if (verb .ge. 3) then + print *,' ' + print *,'!!! NOTE: 850 mb wcirc fix not ' + print *,'available. We are instead ' + print *,'checking 850 mb Vt speed using ' + print *,'multi-parm fix position: ' + print *,' Multi-parm fix lon= ' + & ,fixlon(ist,ifh) + print *,' Multi-parm fix lat= ' + & ,fixlat(ist,ifh) + print *,' ' + endif + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,0.00,trkrinfo,isastorm(3),isiret3) + + endif + + endif + + endif + + if (calcparm(3,ist) .or. + & (had_to_try_backup_850_vt_check == 'y' .and. + & isiret3 == 0) ) then + + if (trkrinfo%type == 'tcgen') then + ! This next bit of code adds a second layer of 850 + ! mb Vt magnitude checking. This is to decrease + ! the occurrence of interrupted tcgen tracks, + ! which occasionally happens for weak storms when + ! this criterion is not met for one time period. + ! So in this next code, we check to see if the + ! vt850_flag was 'y' for at least 75% of the time + ! over the last 24h. For time periods shorter + ! than 24h (e.g., the storm was just detected at + ! 144h and we are now at 156h), the threshold is + ! still that for at least 75% of the time the + ! system has been detected as a storm, it must + ! have a vt850_flag value of 'y'. + + if (isastorm(3) == 'Y') then + vt850_flag(ist,ifh) = 'y' + else + vt850_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & vt850_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - + & fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (vt850_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / + & cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.75) then + isastorm(3) = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ NOTE ON Vt_850 CHECK: The ' + print *,' isastorm flag returned for ' + print *,' this hour was NO, but a' + print *,' check of recent vt850_flags' + print *,' indicates that more than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE ON Vt_850 CHECK: The ' + print *,'!!! isastorm flag returned for ' + print *,' this hour was NO, and a' + print *,' check of recent vt850_flags ' + print *,' indicates that less than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + endif + + endif + endif + + else + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + isastorm(1) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! could not be made for mslp, ' + print *,'!!! therefore we will stop tracking ' + print *,'!!! for this storm.' + endif + + else + isastorm(1) = 'N' + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a TC tracker case, a fix could' + print *,'!!! not be made using any tracked parms,' + print *,'!!! therefore we will stop tracking for' + print *,'!!! this storm.' + endif + + endif + + if ( verb .ge. 3 ) then + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + + endif + + if (isiret1 /= 0 .or. isiret2 /= 0 .or. isiret3 /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: One of the calls to ' + print *,'!!! is_it_a_storm produced an error.' + print *,'!!! Chances are this is from a call to ' + print *,'!!! get_ij_bounds, meaning we are too close' + print *,'!!! to a regional grid boundary to do this ' + print *,'!!! analysis. Processing will continue....' + print *,'!!! isiret1= ',isiret1,' isiret2= ',isiret2 + print *,'!!! isiret3= ',isiret3 + endif + + endif + + if (isastorm(1) == 'N' .or. isastorm(2) == 'N' .or. + & isastorm(3) == 'N') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! At least one of the isastorm flags from' + print *,'!!! subroutine is_it_a_storm is "N", so ' + print *,'!!! either we were unable to find a good ' + print *,'!!! mslp gradient and/or a valid 850 mb ' + print *,'!!! circulation for the storm at this time,' + print *,'!!! or, for the cases of midlat or tcgen ' + print *,'!!! tracking, a closed mslp contour could ' + print *,'!!! not be found, thus we will stop tracking' + print *,'!!! this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! mslp gradient flag = ',isastorm(1) + print *,'!!! closed contour flag = ',isastorm(2) + print *,'!!! 850 mb winds flag = ',isastorm(3) + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + + ! Now do another check for the tracker and tcgen cases. + ! If the isastorm flags for mslp gradient and v850 BOTH + ! came back positive AND you have been able to locate an + ! 850 mb vort center, just do a check to make sure that + ! the distance between the 850 vort center and the mslp + ! center is not too great. + + if (trkrinfo%type == 'tracker' .or. + & trkrinfo%type == 'tcgen') then + if (isastorm(1) == 'Y' .and. isastorm(3) == 'Y' .and. + & calcparm(1,ist) .and. stormswitch(ist) == 1) then + +c if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) >= 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) < 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else +c trkrinfo%max_mslp_850 = 323.0 +c endif + + call calcdist (clon(ist,ifh,9),clat(ist,ifh,9) + & ,clon(ist,ifh,1),clat(ist,ifh,1),dist + & ,degrees) + + if (dist > trkrinfo%max_mslp_850) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, the dist betw' + print *,'!!! the mslp center & the 850 zeta ' + print *,'!!! center is too great, thus we will' + print *,'!!! stop tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + print *,'!!! Actual distance (km) = ',dist + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Actual distance between the parm centers' + print *,'for 850 zeta and mslp is ',dist,' (km)' + print *,'Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + endif + + endif + endif + endif + + ! Do one final check. Check the new fix position and + ! the old fix position and calculate the speed that the + ! storm would have had to travel to get to this point. + ! If that speed exceeds a certain threshold (~60 kt), + ! assume you're tracking the wrong thing and quit. + ! Obviously, only do this for times > 00h. The check + ! in the if statement to see if the previous hour's + ! lats and lons were > -999 is for the midlat and + ! tcgen cases -- remember, they can have genesis at + ! any hour of the forecast, in which case the previous + ! forecast hour's lat & lon would be -999. + + if (ifh > 1 .and. stormswitch(ist) == 1) then + if (fixlon(ist,ifh-1) > -999.0 .and. + & fixlat(ist,ifh-1) > -999.0 ) then + + if (trkrinfo%type == 'midlat') then + xmaxspeed = maxspeed_ml + else + xmaxspeed = maxspeed_tc + endif + + call calcdist (fixlon(ist,ifh-1),fixlat(ist,ifh-1) + & ,fixlon(ist,ifh),fixlat(ist,ifh),dist + & ,degrees) + + ! convert distance from km to nm and get speed. + + distnm = dist * 0.539638 + xinterval_fhr = fhreal(ifh) - fhreal(ifh-1) + xknots = distnm / xinterval_fhr + + if (xknots > xmaxspeed) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, calculated spd' + print *,'!!! of the storm from the last position' + print *,'!!! to the current position is too high,' + print *,'!!! so we will stop tracking this storm' + print *,'!!! (For fear that we are not actually ' + print *,'!!! tracking our storm, but have instead' + print *,'!!! locked onto some other feature....)' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max speed allowed (kt) = ',xmaxspeed + print *,'!!! Actual speed (kt) = ',xknots + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'The average speed that the storm moved' + print *,'at since the previous forecast time is' + & ,xknots,' knots.' + endif + + endif + + endif + + endif + + endif + +c Now get the maximum near-surface wind speed near the storm +c center (get_max_wind). Also, call getradii to get the +c radii in each storm quadrant of gale-force, storm-force +c and hurricane force winds. + + if (readflag(10) .and. readflag(11) .and. ifret == 0 + & .and. stormswitch(ist) == 1) then + call get_max_wind (fixlon(ist,ifh),fixlat(ist,ifh) + & ,imax,jmax,dx,dy,valid_pt,levsfc + & ,xmaxwind(ist,ifh),trkrinfo,rmax,igmwret) +c if (igmwret /= 0 .and. gridmove_status == 'stopped') then + if (igmwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Return code from get_max_wind is /= 0. ' + print *,'!!! rcc= igmwret= ',igmwret + print *,'!!! Also, this is a moveable, regional grid' + print *,'!!! and the grid did not change from last' + print *,'!!! lead time to current one, so what has' + print *,'!!! likely happened is that the storm has ' + print *,'!!! moved close to the edge of the nested ' + print *,'!!! grid domain, but the nested grid itself' + print *,'!!! had stopped moving, probably because it' + print *,'!!! dropped or lost the storm.' + print *,'!!! ' + print *,'!!! TRACKING WILL STOP FOR THIS STORM' + print *,'!!! ' + endif + + stormswitch(ist) = 2 + cycle stormloop + endif + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + + ! For the radii, we encountered a problem with radmax + ! being too small. It was set at 650 km. Hurricane + ! Sandy exceeded this in the models, so the values + ! returned from getradii were close to the default + ! radmax value of 650 km (350 nm), instead of higher. + ! To fix it, we now use an iterative technique, where + ! we start with radmax as a small value (500 km). If + ! getradii returns a value for R34 in a quadrant that + ! does not exceed 0.97*radmax, then that value is ok. + ! If it does exceed 0.97*radmax, then we bump up radmax + ! by 50 km and call getradii again, looking to diagnose + ! radii only in those quadrants where the + ! need_to_expand_r34 flag = 'n'. BTW... note the + ! initial IF statement... we will only go into this + ! routine if the max wind just diagnosed for this lead + ! time is at least 34 kts (17.5 m/s). + + if (xmaxwind(ist,ifh) >= 17.5) then + + vradius = 0 + first_time_thru_getradii = .true. + r34_check_okay = 'n' + do ivr = 1,4 + need_to_expand_r34(ivr) = 'y' + enddo + radmax = 500.0 ! Initial radmax, in km + + igrct = 1 + + if ( verb .ge. 3 ) then + write (6,242) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 242 format (1x,'TIMING: b4 getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + getrad_iter_loop: do while + & (r34_check_okay == 'n' .and. radmax <= 1050.) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,244) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 244 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + call getradii (fixlon(ist,ifh),fixlat(ist,ifh),imax + & ,jmax,dx,dy,valid_pt,storm(ist)%tcv_storm_id + & ,ifcsthour,xmaxwind(ist,ifh),vradius + & ,trkrinfo,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) + + if (igrret /= 0) then + if (verb >= 3) then + print *,' ' + print *,'!!! ERROR: Return code from getradii = ' + & ,igrret + print *,'!!! Searching for radii will not be ' + print *,'!!! completed for this lead time and' + print *,'!!! all radii values will be set to ' + print *,'!!! missing.' + print *,' ' + exit getrad_iter_loop + endif + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,245) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 245 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + first_time_thru_getradii = .false. + igrct = igrct + 1 + r34_dist_thresh = 0.97 * radmax + r34_good_ct = 0 + do ivr = 1,4 + vradius_km = float(vradius(1,ivr)) / 0.5396 + if (vradius_km < r34_dist_thresh) then + r34_good_ct = r34_good_ct + 1 + need_to_expand_r34(ivr) = 'n' + endif + enddo + if (r34_good_ct == 4) then + r34_check_okay = 'y' + endif + radmax = radmax + 50.0 + enddo getrad_iter_loop + + if ( verb .ge. 3 ) then + write (6,246) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 246 format (1x,'TIMING: after getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + endif + + endif + +c If the user has requested so, then call a routine to +c determine the type of cyclone, using Bob Hart's +c cyclone phase space (CPS) algorithms. It is only used +c for times after t=0, since for the first check (of the +c "parameter B" thickness asymmetry), we need to know +c in which direction the storm is moving. Pulling that +c storm movement data off of the tcvitals is not reliable +c since the model storm may not be moving in the same +c direction as the observed storm. However, we could do +c an upgrade later where this storm movement data is +c pulled from the "genesis vitals", which are derived +c from the model forecast data itself, not the obs. + + if (phaseflag == 'y' .and. stormswitch(ist) == 1) then + wcore_flag = 'u' ! 'u' = undetermined +c call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm +c & ,cps_vals,wcore_flag,igpret) +cPENG----2018-06-07 ------------------------ + call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag + & ,wcore_mean_val,wcore_point_max,igpret) + + endif + + if (structflag == 'y' .or. ikeflag == 'y') then + call get_sfc_center (fixlon(ist,ifh),fixlat(ist,ifh) + & ,clon,clat,ist,ifh,calcparm,xsfclon + & ,xsfclat,maxstorm,igscret) + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,er_wind,sr_wind,er_vr,sr_vr + & ,er_vt,sr_vt,maxstorm,trkrinfo,igwsret) + if (igwsret == 0) then + call output_wind_structure (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),er_wind,sr_wind + & ,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iowsret) + endif + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,wfract_cov,pdf_ct_bin + & ,pdf_ct_tot,maxstorm,trkrinfo,igfwret) + if (igfwret == 0) then + call output_fract_wind (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),wfract_cov,'earth' + & ,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) + endif + endif + + if (ikeflag == 'y' .and. stormswitch(ist) == 1) then + call get_ike_stats (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,ike,sdp,wdp,maxstorm + & ,trkrinfo,igisret) + if (igisret == 0) then + call output_ike (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),ike,sdp,wdp,maxstorm + & ,ioiret) + endif + endif + +c Now print out the current fix position and intensity +c (in knots) to standard output. Conversion for m/s to +c knots (1.9427) is explained in output_atcf. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to fixcenter, fix positions at ' + write (6,442) ifhours(ifh),ifclockmins(ifh) + 442 format (1x,'forecast hour= ',i4,':',i2.2,' follow:') + print *,' ' + endif + + if (ifret == 0 .and. stormswitch(ist) == 1) then + + if ( verb .ge. 3 ) then + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + & ,int((xmaxwind(ist,ifh)*1.9427) + 0.5) + print *,' ' + endif + + ! Only call output routines every atcffreq/100 hours.... + + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + + if (leadtime_check == 0) then + + ifcsthour = ileadtime / 100 + + call output_atcfunix (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + + ! Get the storm motion vector and the speed of + ! motion so that we can output this in the + ! "atcf_sink" forecast text file. + + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'vitals',trkrinfo + & ,ignret) + else + istmdir = -999 + istmspd = -999 + ignret = 0 + endif + + if ( verb .ge. 3 ) then + write (6,617) istmspd,istmdir,ignret + 617 format (1x,'+++ RPT_STORM_MOTION: istmspd= ',i5 + & ,' istmdir= ',i5,' rcc= ',i3) + endif + + ! Call a routine to find the mean & max relative + ! vorticity near the storm at 850 & 700. These will + ! be written out to the "atcf_sink" fcst text file. + + imeanzeta = -99 + igridzeta = -99 + call get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) +cPENG----2018-06-07 ------------------------ + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + + call get_temp_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeantemp,igridtemp,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,itempt) + + call get_ushe_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanushe,igridushe,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,iushet) + + call get_rhum_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanrhum,igridrhum,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,irhumt) +cPENG----2018-06-07 ------------------------ + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (fixlon(ist,ifh) +c & ,fixlat(ist,ifh),inp,ist +c & ,ifcsthour,xmaxwind(ist,ifh) +c & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo +c & ,istmspd,istmdir,plastbar,rlastbar,rmax +c & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo + & ,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + + call output_atcf_sink (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta + & ,igridzeta,cps_vals,plastbar,rlastbar + & ,ioaxret) + + if (inp%model == 12 .and. ifcsthour == 0) then + ! Write vitals for GFS ens control analysis + call output_tcvitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,iovret) + + endif + endif + + ! The exception here is for the call to the output_hfip + ! routine, which will be called for every lead time + ! that is processed.... + + call output_hfip (fixlon(ist,ifh),fixlat(ist,ifh),inp,ist + & ,ifh,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,rmax,ioaxret) + else + + if ( verb .ge. 3 ) then + write (6,452) 'fixpos ',storm(ist)%tcv_storm_id + & ,' fhr= ',ifhours(ifh),ifclockmins(ifh) + & ,' Fix not made for this forecast hour' + 452 format (1x,a7,1x,a4,a6,i4,':',i2.2,a36) + + print *,' ' + print *,'!!! RETURN CODE from fixcenter not equal to 0,' + print *,'!!! or output from is_it_a_storm indicated the' + print *,'!!! system found was not our storm, or the ' + print *,'!!! speed calculated indicated we may have ' + print *,'!!! locked onto a different center, thus a fix' + print *,'!!! was not made for this storm at this ' + print *,'!!! forecast hour.' + print *,'!!! mslp gradient check = ',isastorm(1) + print *,'!!! mslp closed contour check = ',isastorm(2) + print *,'!!! 850 mb winds check = ',isastorm(3) + print *,'!!! fixcenter return code = ifret = ',ifret + print *,' ' + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + +c if (inp%model == 1 .or. inp%model == 8 .or. +c & inp%model == 22) then +cPENG + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + + ! For the vt=00h lead time, if the tracker failed to + ! locate a position, we are going to write out an + ! atcfunix record that contains the position, + ! intensity, mslp and 34-kt wind radii from TC Vitals + ! for this storm and initial time. Only do this for + ! the GFS or GDAS runs of the tracker. + + tcv_max_wind_ms = float(storm(ist)%tcv_vmax) + tcv_mslp_pa = float(storm(ist)%tcv_pcen) * 100.0 + + ! Convert tcvitals NE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15ne) + if (r34_from_tcv > 0.0) then + vradius(1,1) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,1) = 0 + endif + + ! Convert tcvitals SE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15se) + if (r34_from_tcv > 0.0) then + vradius(1,2) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,2) = 0 + endif + + ! Convert tcvitals SW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15sw) + if (r34_from_tcv > 0.0) then + vradius(1,3) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,3) = 0 + endif + + ! Convert tcvitals NW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15nw) + if (r34_from_tcv > 0.0) then + vradius(1,4) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,4) = 0 + endif + + ! Convert tcvitals roci from km to nm + + if (storm(ist)%tcv_penvrad > 0) then + roci_from_tcv = float(storm(ist)%tcv_penvrad) + rlastbar = roci_from_tcv * 0.5396 + else + rlastbar = -99.0 + endif + + ! Convert tcvitals pressure at roci from km to nm + + if (storm(ist)%tcv_penv > 0) then + proci_from_tcv = float(storm(ist)%tcv_penv) + plastbar = proci_from_tcv * 100.0 + else + plastbar = -99.0 + endif + + write (6,291) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + & ,atcfymdh + 291 format (1x,'NOTE: TCVITALS_USED_FOR_ATCF_F00 ' + & ,' Storm ID: ',a4,' Storm name: ',a9 + & ,' YMDH: ',i10) + + call output_atcfunix (slonfg(ist,ifh) + & ,slatfg(ist,ifh),inp,ist + & ,ifcsthour,tcv_max_wind_ms + & ,tcv_mslp_pa,vradius,maxstorm,trkrinfo + & ,plastbar,rlastbar,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + else + + ! For all other models, we print out missing + ! data values at tau=00h if the tracker was + ! unable to find the storm.... + + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + + endif + + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (trkrinfo%type == 'tracker') then + ! Update 11/11: For a 'tracker' run, i.e., one in + ! which we know that there is an observed storm in + ! the area, we will assume that there was some type + ! of problem in the initialization that prevented + ! the storm from being found. In this case, even + ! though we have written out zeroes for the 00h + ! time, we want to at least try tracking again at + ! the next lead time. Requested by HWRF folks.... + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',ist + write (6,301) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + 301 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + call get_next_ges (slonfg,slatfg,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + stormswitch(ist) = 1 + endif + + endif + cycle stormloop + endif + + +c Now get first guess for next forecast time's position. +c But first, if this is the first time level (ifh=1) and +c the user has requested that storm vitals be output (this +c is usually only done for model analyses in order to get +c an analysis position from one time to the next), we will +c write out a storm vitals record for this time level. +c Note that we have already gotten the next guess position +c info just above for the case of the repeated analysis +c data, so we'll just output the genesis vitals record. + + if (ifh <= ifhmax) then + if (ifh == 1 .and. trkrinfo%out_vit == 'y') then + call output_gen_vitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,istmspd,istmdir,iovret) + endif + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: Problem getting first guess ' + print *,'!!! position for next lead time. Return' + print *,'!!! code from call to get_next_ges = ' + print *,'!!! ignret = ',ignret + print *,'!!! Storm name = ' + & ,storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! TRACKING WILL STOP FOR THIS STORM.' + endif + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + else + istmdir = -999 + istmspd = -999 + endif + endif + + case (2) + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Case 2 in tracker for stormswitch' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + endif + + case (3) + continue + +c print *,' ' +c print *,'!!! Case 3 in tracker for stormswitch' +c print *,'!!! Storm name = ',storm(ist)%tcv_storm_name +c print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + if (leadtime_check == 0) then + ifcsthour = ileadtime / 100 + endif + if (trkrinfo%inp_data_type == 'grib') then + call output_tracker_mask (masked_outc,valid_pt,ifh + & ,ifcsthour,imax,jmax,iotmret) + endif + endif + + if(use_per_fcst_command=='y') then +c User wants us to run a command per forecast time + +! Replace %[FHOUR] with forecast hour, %[FMIN] with forecast minute. + +! The %[] format is chosen to avoid shell syntax errors if someone +! includes unknown %[] constructs. A stray , for example, +! would generate syntax errors or unexpected results in some +! shells. + +! If an unrecognized %[xxx] sequence is used, it will be retained in +! the final command. This allows the underlying command to detect +! the unreplaced %[] and use suitable default values or abort, as +! appropriate. + + pfc_final=per_fcst_command + call argreplace(pfc_final,pfc_cmd_len,'%[FHOUR]', & + & ifhours(ifh)) + call argreplace(pfc_final,pfc_cmd_len,'%[FMIN]', & + & iftotalmins(ifh)) + + if(verb.ge.2) then + print *,' ' + print *,'!!! Running per-fcst command' + print *,'!!! Unparsed = ',trim(per_fcst_command) + print *,'!!! Parsed = ',trim(pfc_final) + endif + call run_command(trim(pfc_final),pfcret) + if(pfcret/=0 .and. verb.ge.1) then + print *,' ' + print *,'!!! Non-zero exit status from per-fcst command' + print *,'!!! Command = ',trim(pfc_final) + print *,'!!! Exit status = ',pfcret + print *,'!!! Continuing anyway...' + elseif(pfcret==0 .and. verb.ge.2) then + print *,' ' + print *,'!!! Per-fcst command returned success status (0)' + endif + endif + + ifh = ifh + 1 + if (ifh > ifhmax) exit ifhloop + + if (inp%file_seq == 'multi') then + call baclose(lugb,igcret) + call baclose(lugi,iicret) + if ( verb .ge. 3 ) then + print *,'baclose return code for unit ',lugb,' = igcret = ' + & ,igcret + print *,'baclose return code for unit ',lugi,' = iicret = ' + & ,iicret + endif + endif + + enddo ifhloop +c +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) +c + 73 format ('fixpos ',a4,' fhr= ',i4,':',i2.2,' Fix position= ' + & ,f7.2,'E (',f6.2,'W)',2x,f7.2,' Max Wind= ',i3,' kts') + + if (allocated(prstemp)) deallocate (prstemp) + if (allocated(prsindex)) deallocate (prsindex) + if (allocated(iwork)) deallocate(iwork) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) +cPENG----2018-06-07 ------------------------ + if (allocated(ushear)) deallocate (ushear) + if (allocated(rhumid)) deallocate (rhumid) + + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(vt850_flag)) deallocate (vt850_flag) + if (allocated(closed_mslp_ctr_flag)) + & deallocate (closed_mslp_ctr_flag) + if (allocated(netcdf_file_time_values)) + & deallocate (netcdf_file_time_values) + if (allocated(nctotalmins)) + & deallocate (nctotalmins) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine argreplace(arg,n,name,val) + ! This subroutine is used to generate the pre-forecast-command + ! It will edit the command (argument "arg") and replace string + ! name with value val. That is how the per-forecast-command + ! has these modifications: + + ! %[FHOUR] -> replace with -> last forecast hour + ! %[FMIN] -> replace with -> last forecast minute + + implicit none + + integer, intent(in) :: n + character(n), intent(inout) :: arg + character(*), intent(in) :: name + integer, intent(in) :: val + + integer found,namelen,i1,i2 + character(n) :: out + + found=index(arg,name) + namelen=len(name) + i1=found-1 ! last char that is before name + i2=found+namelen ! index of last char in name + + if(found==0) return + + out=' ' + + if(found>1 .and. i21) then +! special case: name is at end of string +! hope the value fits... + write(out,'(A,I0)') arg(1:i1),val + elseif(i2 + & ,'... gopen_i_file= ...',a,'...') + + print *,'gopen_g_file= ',gopen_g_file,'....' + print *,'gopen_i_file= ',gopen_i_file,'....' + + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + inquire (unit=lout, opened=output_file_open) + if (output_file_open) then + iooret = 0 + else + fnameo(1:5) = "fort." + write(fnameo(6:7),'(I2)') lout + call baopenw (lout,fnameo,iooret) + endif + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + inquire (file=gopen_g_file, opened=file_open4) + if (file_open4) then + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is OPEN' + else + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is CLOSED' + endif + + inquire (file=gopen_i_file, opened=file_open5) + if (file_open5) then + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is OPEN' + else + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'gettrk baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine open_ncfile (filename,ncid) + +c ABSTRACT: This subroutine opens a netcdf file specified by the +c input file "ncfile" and returns the netcdf file id that will be +c associated with that file. +c +c INPUT: +c ncfile character full-path file netcdf name +c +c OUTPUT: +c ncfile_id integer, netcdf id assigned to the netcdf file + + implicit none + + include "netcdf.inc" + + character*(*), intent(in) :: filename + integer, intent(out) :: ncid + integer :: status + + status = nf_open (filename, NF_NOWRITE, ncid) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine open_ncfile +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine is_it_a_storm (imax,jmax,dx,dy,cparm,ist + & ,defined_pt,parmlon,parmlat + & ,parmval,trkrinfo,stormcheck,isiret) + +c ABSTRACT: This subroutine is called after the center of the storm +c has been fixed. Its purpose is to determine whether or not +c the center that was found is actually a storm, and not just some +c passing trough (this has happened in the case of decaying or weak +c storms). It's called twice -- once to check for a minimum MSLP +c gradient, and once to check for a circulation at 850 mb. The +c subroutine input parameter "cparm" determines which parameter to +c check for. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is to be checked: +c slp = mslp, for a check of mslp gradient +c v850 = tangential winds at 850 mb +c ist integer storm number (internal to the tracker) +c defined_pt Logical; bitmap indicating if valid data at that pt. +c parmlon Longitude of the max/min value for the input parameter +c parmlat Latitude of the max/min value for the input parameter +c parmval Data value at parm's max/min point (used for mslp call) +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c stormcheck Character; set to 'Y' if mslp gradient or 850 mb +c tangential winds check okay. +c isiret Return code for this subroutine. +c + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE tracked_parms; USE atcf; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + real vt,vtavg,vr,parmlat,parmlon,parmval,dist + real pthresh,vthresh,degrees,dx,dy,dell,ri,radinf + real pgradient,xmaxpgrad + character(*) cparm + logical(1) defined_pt(imax,jmax) + character*1 stormcheck + integer isiret,imax,jmax,ist,npts,ilonfix,jlatfix,igvtret + integer ibeg,iend,jbeg,jend,ivt,i,j,iix,jix,bskip,igiret + + isiret = 0 + stormcheck = 'N' + + dell = (dx+dy)/2. + +c First define the radius of influence, which depends on the +c grid spacing of the model data being used. The ceiling statement +c for npts in the first if statement is needed in case the +c resolution of the grib files eventually goes very low, down to +c say a half degree or less, in order to cover enough points in +c the search. + + if (dell < 1.24) then ! GFS, MRF, NAM, NGM, NAVGEM, GDAS, + ! GFDL, NCEP Ensemble & Ensemble + ! Relocation, SREF Ensemble + ri = ritrk_most + if (cparm == 'slp') then + radinf = 300.0 + else + radinf = 225.0 + endif + npts = ceiling(radinf/(dtk*(dx+dy)/2.)) + else if (dell >= 1.24 .and. dell < 2.49) then ! UKMET + ri = ritrk_most + radinf = 275.0 + npts = 2 + else ! ECMWF + ri = ritrk_coarse + radinf = 350.0 + npts = 1 + endif + + pthresh = trkrinfo%mslpthresh ! These are read in in + vthresh = trkrinfo%v850thresh ! subroutine read_nlists.... + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,parmlon,parmlat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij B, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij B, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print*,' ' + print*,'!!! ERROR in is_it_a_storm from call to' + print*,'!!! get_ij_bounds, stopping processing for ' + print*,'!!! storm number ',ist + endif + + isiret = 92 + return + endif + +c If the input cparm is slp, then check to see that the MSLP +c gradient in any direction from the MSLP center is at least +c 1mb / 200km, or 0.005mb/km. This is based on discussions with +c Morris & Bob, who have had good results using a 2mb/200km +c requirement. Since their model has a much finer resolution than +c all of the models we run the tracker on AND a much better +c depiction of the hurricane vortex, we do not use a requirement +c as strict as theirs, and so make the requirement only half as +c strong as theirs. +c +c If the input cparm is v850, then check to see that there is +c a circulation at 850 mb. We will do this by calculating the +c tangential wind of all points within a specified radius of +c the 850 minimum wind center, and seeing if there is a net +c average tangential wind speed of at least 5 m/s. +c +c UPDATE APRIL 2000: I've relaxed the thresholds slightly from +c 0.005 mb/km to 0.003 mb/km, and the wind threshold from +c 5 m/s to 3 m/s. Also, note that a special case for GDAS has +c been hardwired in that is weaker (0.002 mb/km and 2 m/s). +c That weaker GDAS requirement is for Qingfu's relocation stuff. +c +c UPDATE JULY 2001: The relaxed requirement put in place in +c April 2000 for the GDAS relocation has also been put in place +c for the GFS ensemble relocation. + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the loop. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, ilonfix= ',ilonfix + & ,' jlatfix= ',jlatfix + print *,'ibeg jbeg iend jend = ',ibeg,jbeg,iend,jend + print *,'cparm= ',cparm,' parmlon parmlat = ',parmlon,parmlat + print *,'parmval= ',parmval + print *,' ' + endif + + vtavg = 0.0 + ivt = 0 + + xmaxpgrad = -999.0 + + jloop: do jix = jbeg,jend,bskip + iloop: do iix = ibeg,iend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine is_it_a_storm' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + i = iix - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine ' + print *,'!!! is_it_a_storm for a non-global grid.' + print *,'!!! STOPPING....' + print *,'!!! i= ',i,' imax= ',imax + print *,' ' + endif + + stop 97 + endif + endif + + call calcdist(parmlon,parmlat,glon(i),glat(j),dist,degrees) + + if (dist > radinf .or. dist == 0.0) cycle + + if (defined_pt(i,j)) then + + if (cparm == 'slp') then + pgradient = (slp(i,j) - parmval) / dist + if (pgradient > xmaxpgrad) xmaxpgrad = pgradient + + if ( verb .ge. 3 ) then + write (6,93) i,j,glon(i),glat(j),dist,slp(i,j),pgradient + endif + + if (pgradient > pthresh) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, valid pgradient found.' + print '(a23,f8.5)',' pgradient threshold = ',pthresh + print '(a23,f8.5)',' pgradient found = ',pgradient + print *,'mslp center = ',parmlon,parmlat,parmval + print *,'pgrad loc = ',glon(i),glat(j),slp(i,j) + endif + + stormcheck = 'Y' + exit jloop + endif + endif + + if (cparm == 'v850') then + call getvrvt (parmlon,parmlat,glon(i),glat(j) + & ,u(i,j,nlev850),v(i,j,nlev850),vr,vt,igvtret) + if ( verb .ge. 3 ) then + write (6,91) i,j,glon(i),glat(j),u(i,j,nlev850) + & ,v(i,j,nlev850),vr,vt + endif + + vtavg = vtavg + vt + ivt = ivt + 1 + endif + + endif + + enddo iloop + enddo jloop + + 91 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' u= ',f8.4,' v= ',f8.4,' vr= ',f9.5,' vt= ',f9.5) + + 93 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' dist= ',f8.2,' slp= ',f10.2,' pgradient= ',f8.5) + + if (stormcheck /= 'Y' .and. cparm == 'slp') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, valid pgradient NOT FOUND.' + write (6,94) '!!! (Max pgradient less than ',pthresh,' mb/km)' + 94 format (1x,a29,5x,f8.5,a7) + write (6,95) '!!! Max pgradient (mb/km) found = ',xmaxpgrad + 95 format (1x,a34,f8.5) + print *,' ' + endif + + endif + + if (cparm == 'v850') then + + if (ivt > 0) then + vtavg = vtavg / float(ivt) + else + vtavg = 0.0 + endif + + if (parmlat > 0) then + if (vtavg >= vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (>= +',vthresh,' m/s for a NH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed +',vthresh + & ,' m/s (NH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + else + if (vtavg <= -vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (<= -',vthresh,' m/s for a SH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed -',vthresh + & ,' m/s (SH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + endif + + endif +c + return + end +c +c----------------------------------------------------------------------- +ccPENG----2018-06-07 ------------------------ +c----------------------------------------------------------------------- +c subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm +c & ,cps_vals,wcore_flag,igpret) + subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag + & ,wcore_mean_val,wcore_point_max,igpret) + +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure or phase of a cyclone. Initially, we +c will just have it use the Hart cyclone phase space (CPS) scheme. + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trkrparms; USE grid_bounds + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character wcore_flag*1,okay_to_call_cps_routines*1 +cPENG----2018-06-07 ------------------------ + real wcore_mean_val,wcore_point_max + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real cps_vals(3) + real dx,dy,paramb,vtl_slope,vtu_slope + integer imax,jmax,igpret,igcpret,ist,ifh,maxstorm + integer igvpret,igcv1ret,igcv2ret + logical(1) valid_pt(imax,jmax) +c + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,611) + write (6,613) + write (6,615) + write (6,*) ' ' + + 611 format(1x,'#-----------------------------------------------#') + 613 format(1x,'# start of routine to determine cyclone phase...#') + 615 format(1x,'#-----------------------------------------------#') + endif + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + if (ifh > 1 .or. (ifh == 1 .and. trkrinfo%type == 'tracker')) + & then + + ! This condition that ifh > 1 is so that we *not* do the cps + ! stuff for fhour=0 if it's a tcgen or midlat case, since we + ! don't know the model storm motion direction for the + ! analysis. For a regular case where type = 'tracker', we + ! have the observed storm's heading direction from tc vitals, + ! so we can use that (even though the model's storm direction + ! may differ slightly from the observed storm). This current + ! if statement and the ones below carefully check for these + ! various instances. + + okay_to_call_cps_routines = 'n' + + if (ifh > 1) then + if (fixlon(ist,ifh-1) > -990.0 .and. + & fixlat(ist,ifh-1) > -990.0) then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level ' + print *,' >< since the fixlon and fixlat at the ' + print *,' >< previous lead time are undefined.' + print *,' >< This is likely the first found position' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + endif + endif + elseif (ifh == 1 .and. trkrinfo%type == 'tracker') then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level.' + print *,' >< The likely reason is that ifh=0 and' + print *,' >< this is a genesis case, so we do not ' + print *,' >< know the storm motion direction.' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + print *,' >< trkrinfo%type ',trkrinfo%type + endif + endif + + if (okay_to_call_cps_routines == 'y') then + + ! Similarly, these next two conditions (previous lat and + ! previous lon > -999) are in there in case we're doing a + ! tcgen or midlat case and this is the *first* time level + ! within a forecast that the storm has been detected (again, + ! we don't yet know the storm heading). + + call get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'lower',vtl_slope + & ,maxstorm,igcv1ret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'upper',vtu_slope + & ,maxstorm,igcv2ret) + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh) + & ,paramb,vtl_slope,vtu_slope + endif + + cps_vals(1) = paramb + cps_vals(2) = vtl_slope + cps_vals(3) = vtu_slope + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diagnostics were requested but will NOT' + print *,' >< be performed for this time level since we ' + print *,' >< are either at the first time level for a ' + print *,' >< genesis case (type = midlat or tcgen), or' + print *,' >< we are at any time level in which for some' + print *,' >< reason the fixlon and fixlat at the' + print *,' >< previous time level are not defined.' + print *,' >< ifh= ',ifh + endif + + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diags were requested but will NOT be' + print *,' >< performed for this time level since we are at' + print *,' >< time level 1 for a genesis case ' + print *,' >< (type = midlat or tcgen) and we cannot' + print *,' >< diagnose the model direction of storm' + print *,' >< movement. ifh= ',ifh + endif + + endif + + endif + + 73 format ('cps_stats: ',a4,' lead time= ',i3,':',i2,' paramb= ' + & ,f8.2,' vtl= ',f9.2,' vtu= ',f9.2) + + + if (phasescheme == 'vtt' .or. phasescheme == 'both') then +c call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +cPENG----2018-06-07 ------------------------ + call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag + & ,wcore_mean_val,wcore_point_max,igvpret) + + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + + 631 format(1x,'#-------------------------------------------------#') + 633 format(1x,'# End of routine to determine cyclone phase... #') + 635 format(1x,'#-------------------------------------------------#') + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines "Parameter B", which determines +c the degree of thermal symmetry between the "left" and "right" +c hemispheres of a storm, in the layer between 900 and 600 mb. +c We evaluate only those points that are within 500 km of the +c storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zthicksum(2) + real rlonc,rlatc,rlonb,rlatb,xdist,degrees,d,cosarg + real st_heading,st_heading_rad,ricps,dx,dy + real pt_dir,pt_dir_rad,zthick,hemval,paramb + real zthick_right_mean,zthick_left_mean + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer left_ct,right_ct,hemis,icount,maxstorm,ip + logical(1) valid_pt(imax,jmax) +c + ricps = 500.0 + +c ----------------------------------------------------------------- +c First, determine the angle that the storm took getting from the +c last position to the current one. If this is for ifh=1 for a +c regular type=tracker case, we will just use the storm direction +c as read from the tcvitals card. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + if (d == 0.0) then + + ! Storm is stationary... + st_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + print '(a43,f9.3)' + & ,' In get_cps_paramb, model storm heading = ' + & ,st_heading + print *,' ' + endif + +c ----------------------------------------------------------------- +c Now call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_paramb from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + igcpret = 92 + return + endif + +c ----------------------------------------------------------------- +c Now loop through all of the points of the subdomain. If the +c point is further than 500 km from the storm center, discard it. +c Otherwise, evaluate the angle from the storm center to this point +c to determine the hemisphere of the point, that is, if the point +c is to the left or the right of the storm track. +c ----------------------------------------------------------------- + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the + ! loop for the evaluation of parameter B. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + left_ct = 0 + right_ct = 0 + zthicksum = 0 + icount = 0 + +c print *,'CPS CORE: ibeg= ',ibeg,' iend= ',iend +c print *,'CPS CORE: jbeg= ',jbeg,' jend= ',jend + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + icount = icount + 1 + +c print *,'CPS CORE: ist= ',ist,' ifh= ',ifh,' j= ',j,' i= ',i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_paramb, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Parameter B will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_paramb' + print *,'!!! for a non-global grid.' + print *,'!!! Parameter B will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_PARAMB....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon= ',glon(ip),' glat= ',glat(j) + print *,'!!! Parameter B will not be computed.' + print *,'!!! EXITING GET_CPS_PARAMB....' + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + + !---------------------------------------------------------- + ! Calculate angle from storm center to point, in a 0-360 + ! framework, clockwise positive. + !---------------------------------------------------------- + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-fixlon(ist,ifh)) * dtr + rlatb = fixlat(ist,ifh) * dtr + d = degrees * dtr + + if (d > 0.) then + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_dir_rad = acos(cosarg) + else + pt_dir_rad = 2*pi - acos(cosarg) + endif + else + pt_dir_rad = 0.0 + endif + + pt_dir = pt_dir_rad / dtr + + !------------------------------------------------------------ + ! Based on the angle that the point is from the storm center, + ! determine if the point is to the left or the right of the + ! storm track. + !------------------------------------------------------------ + + if (st_heading >= 180.0) then + if ((st_heading - pt_dir) > 0.0 .and. + & (st_heading - pt_dir) <= 180) then + hemis = 2 + left_ct = left_ct + 1 + else + hemis = 1 + right_ct = right_ct + 1 + endif + else + if ((pt_dir - st_heading) > 0.0 .and. + & (pt_dir - st_heading) <= 180) then + hemis = 1 + right_ct = right_ct + 1 + else + hemis = 2 + left_ct = left_ct + 1 + endif + endif + + !------------------------------------------------------------ + ! Calculate the 600-900 mb thickness at this point and add + ! the thickness value to the array for the correct "storm + ! hemisphere". + !------------------------------------------------------------ + + zthick = cpshgt(ip,j,7) - cpshgt(ip,j,1) + zthicksum(hemis) = zthicksum(hemis) + zthick + + if ( verb .ge. 3 ) then + write (6,51) rlonb/dtr,rlatb/dtr,rlonc/dtr,rlatc/dtr + & ,st_heading,pt_dir,hemis,zthick + endif + + enddo iloop + enddo jloop + + 51 format (1x,'stlon stlat = ',2(f6.2,2x),' ptlon ptlat = ' + & ,2(f6.2,2x),' sthead= ',f6.2,' ptdir= ',f6.2,' hemis= ' + & ,i1,' zthick= ',f7.2) + +c ------------------------------------------------------------------ +c Now calculate parameter B. The hemval parameter = +1 for storms +c in the Northern Hemisphere and -1 for Southern Hemisphere storms. +c ------------------------------------------------------------------ + + zthick_right_mean = zthicksum(1) / float(right_ct) + zthick_left_mean = zthicksum(2) / float(left_ct) + + if (fixlat(ist,ifh) < 0.0) then + hemval = -1.0 + else + hemval = 1.0 + endif + + paramb = hemval * (zthick_right_mean - zthick_left_mean) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' right_ct= ',right_ct,' left_ct= ',left_ct + print *,' zthicksum(1)= ',zthicksum(1) + print *,' zthicksum(2)= ',zthicksum(2) + print *,' zthick_right_mean= ',zthick_right_mean + print *,' zthick_left_mean= ',zthick_left_mean + print *,' hemval= ',hemval + print *,' END of get_cps_paramb, paramb= ',paramb + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,clayer,vth_slope,maxstorm,igcvret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines the thermal wind profile for +c either the lower troposphere (i.e., between 600 and 900 mb) or the +c upper troposphere (i.e., between 300 and 600 mb). We evaluate +c only those points that are within 500 km of the storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character clayer*5 + real tmp1,tmp2,tmp3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zmax(7),zmin(7),zdiff(7),xlolevs(7),xhilevs(7),plev(7) + real dlnp(7),dzdlnp(7),dz(7),lnp(7) + real vth_slope,xdist,degrees,d,cosarg + real ricps,dx,dy,R2 + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j,k,kix + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igcvret,igiret + integer kbeg,kend,maxstorm,ip + logical(1) valid_pt(imax,jmax) + + data xlolevs /900.,850.,800.,750.,700.,650.,600./ + data xhilevs /600.,550.,500.,450.,400.,350.,300./ +c data xlolevs /90000.,85000.,80000.,75000.,70000.,65000.,60000./ +c data xhilevs /60000.,55000.,50000.,45000.,40000.,35000.,30000./ +c + ricps = 500.0 + plev = 0.0 + + if (clayer == 'lower') then + kbeg = 1 + kend = 7 + plev = xlolevs + else + kbeg = 7 + kend = 13 + plev = xhilevs + endif + +c ----------------------------------------------------------------- +c First, call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_vtl from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + igcvret = 92 + return + endif + +c ------------------------------------------------------------------ +c Now loop through all of the points of the subdomain at each level. +c If a point is further than 500 km from the storm center, discard +c it. Otherwise, evaluate the gp height at the point to determine +c if it is a max or a min for the given level. Store the max and +c min height at each level in an array. +c ------------------------------------------------------------------ + +c ! We will want to speed things up for finer resolution grids. +c ! We can do this by skipping some of the points in the +c ! loop for the evaluation of parameter B. +c +c if ((dx+dy)/2. > 0.20) then +c bskip = 1 +c else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then +c bskip = 2 +c else if ((dx+dy)/2. <= 0.10) then +c bskip = 3 +c endif + + bskip = 1 ! Don't do any skipping for now.... + + zmax = -9999999.0 + zmin = 9999999.0 + zdiff = 0.0 + lnp = 0.0 + + levloop: do k = kbeg,kend + + if (kbeg == 7) then + ! processing upper layers (600-300 mb) + kix = k - 6 + else + ! processing lower layers (900-600 mb) + kix = k + endif + + lnp(kix) = log(plev(kix)) + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_vth, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_vth' + print *,'!!! for a non-global grid.' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_VTH....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j,' k= ',k + & ,' clayer= ',clayer + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon(ip)= ',glon(ip),' glat= ',glat(j) + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! EXITING GET_CPS_VTH....' + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + + tmp1 = zmax(kix) + tmp2 = cpshgt(ip,j,k) + tmp3 = zmin(kix) + + zmax(kix) = max(tmp1,tmp2) + zmin(kix) = min(tmp3,tmp2) + +c zmax(kix) = max(zmax(kix),cpshgt(ip,j,k)) +c zmin(kix) = min(zmin(kix),cpshgt(ip,j,k)) + + enddo iloop + enddo jloop + + zdiff(kix) = zmax(kix) - zmin(kix) + + enddo levloop + +c ------------------------------------------------------------------ +c Now calculate the vertical derivative of the gp height, that is, +c d(dz)/d(ln(p)). Here, zdiff is the gp height perturbation at a +c given level, calculated in the loop above; dz is the vertical +c change in that perturbation from one level to the next. +c ------------------------------------------------------------------ + + dz = 0.0 + dlnp = 0.0 + dzdlnp = 0.0 + + do k = 2,7 + dz(k) = zdiff(k) - zdiff(k-1) + dlnp(k) = log(plev(k)) - log(plev(k-1)) + dzdlnp(k) = dz(k) / dlnp(k) + enddo + +c ------------------------------------------------------------------ +c Now call a correlation routine to get the slope of a regression +c line. The independent variable that we input is dlnp, the change +c in log of pressure with height. The dependent variable is +c dzdlnp, the vertical change in the height perturbation with +c respect to the change in pressure. The slope that is returned +c defines whether we've got a cold core or warm core system. +c See Hart (MWR, April 2003, Vol 131, pp. 585-616) for more +c details, specifically his Fig. 3 and the discussion surrounding. +c Note that in the call to calccorr, we are sending only 6 of the +c 7 elements of the dlnp and dzdlnp arrays, beginning with the +c 2nd element of each. That's because the first array value for +c each of those arrays is empty, since in the loop just above, we +c start with kbeg+1, not kbeg. +c ------------------------------------------------------------------ + + call calccorr(lnp(2),zdiff(2),6,R2,vth_slope) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ In get_cps_vth, values for vth follow for ' + & ,'lead time= ',ifhours(ifh),':',ifclockmins(ifh),' ' + & ,storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' ... clayer = ',clayer + print *,' ' + endif + + do k = kbeg,kend + + if (kbeg == 7) then + kix = k - 6 + else + kix = k + endif + + if ( verb .ge. 3 ) then + print *,' ' + write (6,31) k,plev(kix),zmax(kix),zmin(kix),zdiff(kix) + if (kix > 1) then + write (6,32) plev(kix),log(plev(kix)) + & ,plev(kix-1),log(plev(kix-1)) + write (6,33) dz(kix),dlnp(kix),dzdlnp(kix) + else + write (6,34) + endif + endif + + enddo + + 31 format (1x,' +++ k= ',i2,' press= ',f8.1,' zmax= ',f7.2 + & ,' zmin= ',f7.2,' zdiff= ',f7.2) + 32 format (1x,' ln(',f7.1,')= ',f9.6,' ln(',f7.1,')= ',f9.6) + 33 format (1x,' dz= ',f10.2,' dlnp= ',f13.6,' dzdlnp= ',f12.3) + 34 format (1x,' --- First level... no derivatives done...') +c + return + end +c +C---------------------------------------------------- +C +C---------------------------------------------------- + subroutine calccorr(xdat,ydat,numpts,R2,slope) +c +c This subroutine is the main driver for a series of +c other subroutines below this that will calculate the +c correlation between two input arrays, xdat and ydat. +c +c INPUT: +c xdat array of x (independent) data points +c ydat array of y (dependent) data points +c numpts number of elements in each of xdat and ydat +c +c OUTPUT: +c R2 R-squared, the coefficient of determination +c slope Slope of regression line +c +c xdiff array of points for xdat - xmean +c ydiff array of points for ydat - ymean +c yestim array of regression-estimated points +c yresid array of residuals (ydat(i) - yestim(i)) + + USE verbose_output + + implicit none + + real xdat(numpts),ydat(numpts) + real xdiff(numpts),ydiff(numpts) + real yestim(numpts),yresid(numpts) + real xmean,ymean,slope,yint,R2 + integer numpts,i + +c + call getmean(xdat,numpts,xmean) + call getmean(ydat,numpts,ymean) +c + call getdiff(xdat,numpts,xmean,xdiff) + call getdiff(ydat,numpts,ymean,ydiff) +c + call getslope(xdiff,ydiff,numpts,slope) + yint = ymean - slope * xmean +c + call getyestim(xdat,slope,yint,numpts,yestim) + call getresid(ydat,yestim,numpts,yresid) +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * CPS Thermal wind regression details * ' + print *,' *--------------------------------------------------* ' + endif + + call getcorr(yresid,ydiff,numpts,R2) + + if ( verb .ge. 3 ) then + print *,' i ydat xdat ydiff xdiff e' + & ,' e2 ydiff2' + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + do i = 1,numpts + write(6,'(2x,i3,2x,f7.2,2x,f7.4,2x,f7.2,2x,f7.4,3(2x,f7.2))') + & i,ydat(i),xdat(i),ydiff(i) + & ,xdiff(i),yresid(i),yresid(i)*yresid(i) + & ,ydiff(i)*ydiff(i) + enddo + + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + print *,' ' + write (6,'(1x,a13,f9.3,3x,a5,f7.2)') ' means: y: ',ymean + & ,' x: ',xmean + + write (6,*) ' ' + write (6,30) 'slope= ',slope,' y-intercept = ',yint + 30 format (2x,a7,f10.3,a23,f10.3) + if (slope .gt. 0.0) then + write(6,40) 'Regression equation: Y = ',yint,' + ',slope + else + write(6,40) 'Regression equation: Y = ',yint,' - ' + & ,abs(slope) + endif + 40 format (2x,a27,f8.2,a3,f8.2,'X') +c + print *,' ' + write (6,'(1x,a17,f7.4,5x,a7,f7.4)') ' R2(r_squared) = ',R2 + & ,' r = ',sqrt(R2) + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * End of regression details * ' + print *,' *--------------------------------------------------* ' + endif + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getmean(xarr,inum,zmean) +c +c This subroutine is part of the correlation calculation, +c and it simply returns the mean of the input array, xarr. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c +c OUTPUT: +c zmean mean of data values in xarr + + implicit none + + real xarr(inum) + real xsum,zmean + integer i,inum +c + xsum = 0.0 + do i = 1,inum + xsum = xsum + xarr(i) + enddo +c + zmean = xsum / float(MAX(inum,1)) +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getdiff(xarr,inum,zmean,zdiff) +c +c This subroutine is part of the correlation calculation, +c and it returns in the array zdiff the difference values +c between each member of the input array xarr and the +c mean value, zmean. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c zmean mean of input array (xarr) +c +c OUTPUT: +c zdiff array containing xarr(i) - zmean + + implicit none + + real xarr(inum),zdiff(inum) + real zmean + integer i,inum +c + do i = 1,inum + zdiff(i) = xarr(i) - zmean + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + + subroutine getslope(xarr,yarr,inum,slope) +c +c This subroutine is part of the correlation calculation, +c and it returns the slope of the regression line. +c +c INPUT: +c xarr input array of xdiffs (x - xmean) +c yarr input array of ydiffs (y - ymean) +c inum number of points in x & y arrays +c +c OUTPUT: +c slope slope of regression line + + real xarr(inum),yarr(inum) + real slope,sumxy,sumx2 + integer i,inum + +c First sum up the xarr*yarr products.... + + sumxy = 0.0 + do i = 1,inum + sumxy = sumxy + xarr(i) * yarr(i) + enddo + +c Now sum up the x-squared terms.... + + sumx2 = 0.0 + do i = 1,inum + sumx2 = sumx2 + xarr(i) * xarr(i) + enddo + +c Now get the slope.... + + slope = sumxy / sumx2 + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getyestim(xarr,slope,yint,inum,yestim) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the predicted y-values using the +c regression equation that has been calculated. +c +c INPUT: +c xarr array of x data points +c slope slope of the calculated regression line +c yint y-intercept of the calculated regression line +c inum number of input points +c +c OUTPUT: +c yestim array of y pts estimated from regression eqn. + + implicit none + + real xarr(inum),yestim(inum) + real slope,yint + integer i,inum +c + do i = 1,inum + yestim(i) = yint + xarr(i) * slope + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getresid(yarr,yestim,inum,yresid) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the residual values between the +c input y data points and the y-estim predicted y values. +c +c INPUT: +c yarr array of y data points +c yestim array of y pts estimated from regression eqn. +c inum number of input points +c +c OUTPUT: +c yresid array of residuals (ydat(i) - yestim(i)) + + implicit none + + real yarr(inum),yestim(inum),yresid(inum) + integer i,inum +c + do i = 1,inum + yresid(i) = yarr(i) - yestim(i) + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getcorr(yresid,ydiff,inum,R2) +c +c This subroutine is part of the correlation calculation, +c and it does the actual correlation calculation. +c +c INPUT: +c yresid array of residuals (ydat(i) - yestim(i)) +c ydiff array of points for ydat - ymean +c inum number of points in the arrays +c +c OUTPUT: +c R2 R-squared, the coefficient of determination + + USE verbose_output + + implicit none + + real yresid(inum),ydiff(inum) + real R2,sumyresid,sumydiff + integer i,inum +c + sumyresid = 0.0 + sumydiff = 0.0 + + do i = 1,inum + sumyresid = sumyresid + yresid(i) * yresid(i) + sumydiff = sumydiff + ydiff(i) * ydiff(i) + enddo + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,30) 'Sum of y-residuals squared (e2) = ',sumyresid + write (6,30) 'Sum of y-diffs squared (ydiff2) = ',sumydiff + write (6,*) ' ' + 30 format (1x,a35,f15.2) + endif + +c if (sumydiff == 0.0) then +c R2=1.0 +c else +c R2 = 1 - sumyresid / sumydiff +c endif +c PENG 05/14/2018 Bug-fixed for R2 calculation with FENS job crashed. + if (sumyresid .lt. sumydiff) then + if (sumydiff .le. 0.000001) then + R2 = 1.0 + else + R2 = 1 - sumyresid / sumydiff + endif + else + R2=0.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- +c subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +cPENG----2018-06-07 ------------------------ + subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag + & ,wcore_mean_val,wcore_point_max,igvpret) + +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. Here, we are only looking +c at the mid-to-upper tropospheric warm anomaly at the center of +c the storm. The temperature data that we are searching through in +c the tmean array should be the 300-500 mb mean temperature data. +c The criteria in this algorithm are based loosely on Vitart's +c criteria for warm core checking, but the nuts & bolts of the +c subroutine use algorithms from this tracker, including the barnes +c analysis. First, we locate the warm core with the find_maxmin +c routine. Then we use the check_closed_contour routine to see if +c there is a closed temperature contour surrounding the warm core. +c +c INPUT: +c inp +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c inp contains input date and model number information +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c ist integer storm number (internal to the tracker) +c ifh integer index for lead time +c trkrinfo derived type containing grid info on user boundaries +c fixlon array containing found fix longitudes +c fixlat array containing found fix latitudes +c valid_pt Logical; bitmap indicating if valid data at that pt. +c maxstorm maximum # of storms to be handled +c +c OUTPUT: +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c igvpret Return code for this subroutine. +c +c LOCAL: +c wcore_mean_val barnes-averaged value of the temperature at the +c location where the tracker found the warm core. +c wcore_point_max max temperature found at a gridpoint near the +c location where the tracker found the warm core using +c barnes analysis. + + USE set_max_parms; USE grid_bounds; USE trkrparms; USE contours + USE tracked_parms; USE gen_vitals; USE def_vitals; USE inparms + USE phase + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo,wcore_trkrinfo + type (cint_stuff) wcore_contour_info + type (datecard) inp + + character*1 get_last_contour_flag,wcore_flag + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dx,dy,wcore_mean_val,wcore_mean_lon,wcore_mean_lat + real wcore_point_max,tlastcont,rlastcont,tlastout,rlastout + integer imax,jmax,igvpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer icount,maxstorm,ip,ifmret,ifilret,ifix,jfix,icccret + integer num_check_conts + logical(1) valid_pt(imax,jmax),compflag,wcore_mask(imax,jmax) + logical(1) output_file_open +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of get_vtt_phase *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for warm core at hour ',i4,':',i2.2) + write (6,103) wcore_depth + 103 format (1x,'* Warm core depth threshold (wcore_depth) = ',f7.2) + print *,'*-------------------------------------------------*' + endif + +c ------------------------------------------------------------ +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + + wcore_mask = .false. + wcore_mean_lon = -999.0 + wcore_mean_lat = -999.0 + wcore_trkrinfo = trkrinfo ! set equal to values from trkrinfo... + wcore_trkrinfo%contint = wcore_depth ! ...except use the warm + ! core contour interval specified by + ! the user in the extrkr.sh script. + +c ------------------------------------------------------------ +c First, call find_maxmin to locate the warm core + + call find_maxmin (imax,jmax,dx,dy,'tmp' + & ,tmean,'max',ist,fixlon(ist,ifh),fixlat(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,compflag + & ,wcore_mean_lon,wcore_mean_lat,wcore_mean_val + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + + if (verb .ge. 3) then + print *,' ' + print *,'After call to find_maxmin for wcore, ifmret= ',ifmret + print *,' wcore_mean_val= ',wcore_mean_val + endif + +c ------------------------------------------------------------ +c Once find_maxmin returns a value and a location for the +c barnes-averaged value of a warm core, then make a call to +c fix_latlon_to_ij to (1) get the actual gridpoint value of the +c temperature (the value stored in wcore_mean_val is an +c area-averaged value coming from the barnes analysis), and +c (2) to get the (i,j) indeces for this gridpoint to be used in +c the call to check_closed_contour below. + + if (wcore_mean_lat > -99.0 .and. wcore_mean_lon > -990.0) then + call fix_latlon_to_ij (imax,jmax,dx,dy,tmean,'max' + & ,valid_pt,wcore_mean_lon,wcore_mean_lat + & ,wcore_mean_val,ifix,jfix,wcore_point_max,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Warm core stats: ' + write (6,105) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_mean_lon,360.-wcore_mean_lon + & ,wcore_mean_lat,wcore_mean_val + write (6,106) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,ifix,jfix,wcore_point_max + endif + + else + ! Search went out of regional grid bounds.... + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN get_vtt_phase. The call to ' + print *,'!!! fix_latlon_to_ij returned a non-zero return ' + print *,'!!! code, which means that the search for the fix' + print *,'!!! i and j went out of bounds for a regional ' + print *,'!!! grid. This should have been caught in a ' + print *,'!!! previous call to find_maxmin for one of the ' + print *,'!!! various fix parms. In any event, we will not' + print *,'!!! search for a warm core for this storm and ' + print *,'!!! lead time.' + print *,' ' + write (6,115) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,'U',-999.99,-9999.99 + endif + + igvpret = 95 + wcore_flag = 'u' + return + endif + endif + + 105 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' mean_lon: ',f7.2,'E' + & ,1x,'(',f7.2,'W)',2x,'mean_lat: ',f7.2,2x + & ,'wcore_mean_val(K): ',f12.3) + 106 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' ifix: ',i5,2x + & ,' jfix: ',i5,2x,'wcore_point_max(K): ',f12.3) + + +c ------------------------------------------------------------ +c The Vitart scheme specifies that the temperature must decrease +c by at least 1.0C in all directions from the warm core center +c within a distance of 8 deg. A rigorous check of this criterion +c is performed here by utilizing the check_closed_contour routine. +c If we have a closed contour in the temperature field +c surrounding the warm core (using a 1 deg K interval), that +c criterion is satisfied. For diagnostic purposes, we set the +c value of num_check_conts to 999 in order to keep searching for +c all contours surrounding the warm core, and this allows us to +c get an idea of the "depth" or magnitude of the warm core when +c the tlastcont and rlastcont values are returned. + + wcore_contour_info%numcont = maxconts + num_check_conts = 999 + + get_last_contour_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,tmean + & ,valid_pt,wcore_mask,wcore_flag,'max',wcore_trkrinfo + & ,num_check_conts,wcore_contour_info,get_last_contour_flag + & ,tlastcont,rlastcont,icccret) + + if (wcore_flag == 'y') then + tlastout = tlastcont + rlastout = rlastcont/0.539638 + else + tlastout = -999.0 + rlastout = -9999.0 + endif + + if ( verb .ge. 3 ) then + write (6,115) storm(ist)%tcv_storm_id,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_flag,tlastout,rlastout + + 115 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2 + & ,' wcore_flag= ',a1,2x,' Temp of last contour(K) = ' + & ,f7.2,2x,'Radius of last contour(km) = ',f8.2) + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_sfc_center (xmeanlon,xmeanlat,clon + & ,clat,ist,ifh,calcparm,xsfclon,xsfclat + & ,maxstorm,igscret) +c +c ABSTRACT: This subroutine computes a modified lat/lon fix position +c to use as the input center position for the subroutines that +c follow which calculate surface-wind related values. The reason +c for this is that since we are concerned with the positioning of +c low-level wind features (e.g., rmax), we want the center position +c to be based solely on low-level features. We'll use mslp and the +c min in the sfc wind speed. If a center fix was unable to be made +c at this forecast hour for mslp and low-level winds, then we will +c stick with just using the mean position we got using all the other +c parameters. +c +c INPUT: +c xmeanlon The mean center longitude computed from all the various +c parameter fixes found in array clon +c xmeanlat The mean center latitude computed from all the various +c parameter fixes found in array clat +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Index for storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c (if a parameter fix could not be made at this forecast +c hour, then calcparm is set to false for this time for +c that parameter). +c maxstorm Maximum number of storms that can be tracked +c +c OUTPUT: +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c igscret Return code from this subroutine + + USE set_max_parms + USE verbose_output + + implicit none + + integer ist,ifh,ipct,igscret,maxstorm + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmeanlon,xmeanlat + real xsfclon,xsfclat,xlonsum,xlatsum + logical(1) calcparm(maxtp,maxstorm) + + ipct = 0 + xlonsum = 0.0 + xlatsum = 0.0 + + ! Do NOT include MSLP for the surface center at this time. +c if (calcparm(9,ist)) then +c ipct = ipct + 1 +c xlonsum = xlonsum + clon(ist,ifh,9) +c xlatsum = xlatsum + clat(ist,ifh,9) +c endif + + if (calcparm(10,ist)) then +c ! NOTE: Put double weighting on surface wind center if +c ! the tracker was able to find a center for it.... +c ipct = ipct + 2 +c xlonsum = xlonsum + 2.*clon(ist,ifh,10) +c xlatsum = xlatsum + 2.*clat(ist,ifh,10) + ! Just use single weighting for the sfc wcirc fix + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,10) + xlatsum = xlatsum + clat(ist,ifh,10) + endif + + if (calcparm(11,ist)) then + ! This is for the sfc vorticity center.... + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,11) + xlatsum = xlatsum + clat(ist,ifh,11) + endif + + if (ipct > 0) then + xsfclon = xlonsum / float(ipct) + xsfclat = xlatsum / float(ipct) + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In get_fract_wind_cov, CANNOT get modified fix ' + print *,'!!! position because the parameter fixes for mslp' + print *,'!!! and the sfc winds could not be obtained at this' + print *,'!!! forecast hour. ist= ',ist,' ifh= ',ifh + print *,'!!! We will use the fixlon and fixlat values for' + print *,'!!! this forecast hour.' + endif + + xsfclon = xmeanlon + xsfclat = xmeanlat + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ In get_sfc_center, modified fix (mslp + sfc_winds)' + print *,'+++ position follows: ' + print *,'+++ ' + print *,'+++ mslp: lon: ',clon(ist,ifh,9),' lat: ' + & ,clat(ist,ifh,9) + print *,'+++ sfc_winds: lon: ',clon(ist,ifh,10),' lat: ' + & ,clat(ist,ifh,10) + print *,'+++ sfc_vorticity: lon: ',clon(ist,ifh,11),' lat: ' + & ,clat(ist,ifh,11) + print *,'+++ multi-parm mean: lon: ',xmeanlon,' lat: ' + & ,xmeanlat + print *,'+++ sfc-only mean: lon: ',xsfclon,' lat: ',xsfclat + endif + + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,er_wind,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm + & ,trkrinfo,igwsret) +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure of the low level winds of a cyclone. +c The algorithm will search out at specified distances from the +c storm center along arcs in each quadrant of the storm, +c evaluating the winds every 15 degrees along the arc. In each +c arc, start 7.5 degrees in, then make stops at 22.5, 37.5, +c 52.5, 67.5, and 82.5 degrees. At each of those points, we +c will bilinearly interpolate the winds to the points along those +c arcs. Then we compute a quadrant average of the wing magnitude, +c as well as the mean Vt and Vr values. This will be done +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 earth-relative +c quadrants: NE, SE, SW and NW. For the storm-relative estimates, +c these mean values of the wind will be computed for the same +c relative quadrants (front-right, back-right, back-left, front- +c left, but with respect (positive clockwise) to the +c direction of storm motion. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated +c er_wind: Quadrant winds in earth-relative framework +c sr_wind: Quadrant winds in storm-relative framework +c er_vr: Quadrant radial winds in earth-relative framework +c sr_vr: Quadrant radial winds in storm-relative framework +c er_vt: Quadrant tangential winds in earth-relative framework +c sr_vt: Quadrant tangential winds in storm-relative framework + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,num_qtr_azim=6 + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igvtret,ipct,maxstorm,iazim,azimuth_ct + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,xsfclon,xsfclat,wmag,wmag_sum + real vr,vt,vr_sum,vt_sum + logical(1) valid_pt(imax,jmax) +c + data rdist/10.,25.,50.,75.,100.,125.,150.,200.,250.,300.,350. + & ,400.,450.,500./ + + igwsret = 0 + + er_wind = 0.0 + sr_wind = 0.0 + er_vr = 0.0 + er_vt = 0.0 + sr_vr = 0.0 + sr_vt = 0.0 + +c ----------------------------------------------------------------- +c Now determine the angle that the storm took getting from the +c last position to the current one. If this is the initial time, +c use the observed direction of motion from the TC Vitals. This +c may not match up with the model storm's initial direction of +c motion, but it is all we have available to us in order to get +c a heading estimate for the initial time. This storm heading +c information will be used for the storm-relative profiles. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_wind_structure, fhr= ',fhreal(ifh) + & ,' ',storm(ist)%tcv_storm_id + & ,' ',storm(ist)%tcv_storm_name + print '(a25,a23,f9.3)',' In get_wind_structure, ' + & ,' model storm heading = ',st_heading + print *,' ' + endif + + endif + +c ----------------------------------------------------------------- +c Get the profiles for the earth-relative coordinate system. +c Start with NE, then SE, SW, and NW. First go through +c radiusloop, which goes from one radial distance to the next, +c then do the quadloop, which goes through each quadrant, and +c then within each quadrant, the qtr_azimloop goes through for +c six points along an arc, spaced 15 degrees apart, starting at +c 7.5 degrees clockwise from the north. +c ----------------------------------------------------------------- + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *****************************************************' + print *,' Wind Structure: distbear bilin interp starts here.' + print *,' *****************************************************' + print *,' ' + endif + + radiusloop1: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- ER structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop1: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + ! In each quadrant, run through six points along an + ! arc and evaluate the winds. + + qtr_azimloop1: do iazim = 1,num_qtr_azim + + bear = ((iquad-1) * 90.) + ((iazim-1) * 15.) + 7.5 + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' earth-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,' ' + print '(5(a10,f7.2))',' sfclat= ',xsfclat + & ,' sfclon= ',xsfclon + & ,' rdist= ',rdist(idist),' targlat= ',targlat + & ,' targlon= ',targlon + print '(19x,a8,f7.2,35x,a9,f7.2)','sfclon= ',360.-xsfclon + & ,'targlon= ',360.-targlon + endif + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop1 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + er_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + er_vr(iquad,idist) = vr_sum / float(azimuth_ct) + er_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + er_wind(iquad,idist) = -999.0 + er_vr(iquad,idist) = -999.0 + er_vt(iquad,idist) = -999.0 + endif + + enddo quadloop1 + + enddo radiusloop1 + +c ----------------------------------------------------------------- +c Get the profiles for the storm-relative coordinate system. +c Start with the front-right quadrant and go clockwise through +c back-right, back-left and front-left. +c ----------------------------------------------------------------- + + radiusloop2: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- SR structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop2: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + qtr_azimloop2: do iazim = 1,num_qtr_azim + +c temp_bear = st_heading + ((iquad-1) * 90.) + 45. + + temp_bear = st_heading + ((iquad-1) * 90.) + & + ((iazim-1) * 15.) + 7.5 + bear = mod(temp_bear,360.) + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' storm-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop2 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + sr_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + sr_vr(iquad,idist) = vr_sum / float(azimuth_ct) + sr_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + sr_wind(iquad,idist) = -999.0 + sr_vr(iquad,idist) = -999.0 + sr_vt(iquad,idist) = -999.0 + endif + + enddo quadloop2 + + enddo radiusloop2 +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,calcparm,wfract_cov,pdf_ct_bin,pdf_ct_tot,maxstorm + & ,trkrinfo,igfwret) +c +c ABSTRACT: This subroutine determines the fractional areal coverage +c of winds exceeding various thresholds within specified arcs +c (e.g., 200 km, 400 km, etc) in each quadrant of a storm. The bins +c that are used go as follows: (1) 0-100; (2) 0-200; (3) 0-300; +c (4) 0-400; (5) 0-500. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real wfract_cov(numquad+1,numbin,numthresh) + real area_total_quad_bin(numquad,numbin) + real area_exceed_quad_bin(numquad,numbin,numthresh) + real xintlon,xintlat + real :: windthresh(numthresh) = (/17.5,25.74,32.94/) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,conv_ms_knots,vmagkts + real rads,ri,dell,vmag,xarea,grdintincr,xsfclon,xsfclat + real sum_exceed_area(numbin,numthresh) + real sum_total_area(numbin,numthresh) + integer pdf_ct_bin(16) + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igfwret,ipct,i,j,numinterp,ixoa,ixaa,iq,ib,it,ii + integer jlatfix,ilonfix,npts,ibeg,iend,jbeg,jend,ngridint,ni,nj + integer itret,igiret,idistbin,ipdfbin,pdf_ct_tot,maxstorm + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) + character got_pdf*6 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*5 :: cbin(5) = + & (/'0-100','0-200','0-300','0-400','0-500'/) + character*2 :: cthresh(3) = (/'34','50','64'/) +c + igfwret = 0 + conv_ms_knots = 1.9427 + rads = 500.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + + wfract_cov = 0.0 + area_total_quad_bin = 0.0 + area_exceed_quad_bin = 0.0 + sum_exceed_area = 0.0 + sum_total_area = 0.0 + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_fract_wind_cov from call to ' + print *,'!!! get_ij_bounds, stopping processing for storm' + print *,'!!! number ',ist + endif + + igfwret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_fract_wind_cov calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igfwret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + +c When evaluating the winds at a gridpoint, keep in mind that each +c gridpoint represents area around it. There are 2 special cases +c we need to watch out for. The first is for cases in which the +c area of a gridpoint straddles across a distance threshold, so +c that some of the gridpoint's area is in the "<200" bin, while +c some is in the "<100" bin. The other is for the case in which +c the area of a gridpoint straddles between 2 adjacent quadrants +c (e.g., a gridpoint exactly to the north of the center would have +c half its area in the NW quadrant and half in the NE quadrant). +c +c To properly "partition" and assign gridpoint areas, we need to +c interpolate the current grid down to a fine resolution. +c +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the guidelines that +c will be used, keeping in mind that we want the final grid spacing +c to be on the order of between 0.05 and 0.10 degree (finer than +c 0.05 deg is superfluous, and coarser than 0.10 deg is too coarse). +c +c Original grid size (deg) # of interps +c ------------------------- ------------ +c 0.8 <= g 4 +c 0.4 <= g < 0.8 3 +c 0.2 <= g < 0.4 2 +c 0.1 <= g < 0.2 1 +c g < 0.1 0 + + + if ((dx+dy)/2. >= 0.8) then + numinterp = 4 + else if ((dx+dy)/2. < 0.8 .and. (dx+dy)/2. >= 0.4) then + numinterp = 3 + else if ((dx+dy)/2. < 0.4 .and. (dx+dy)/2. >= 0.2) then + numinterp = 2 + else if ((dx+dy)/2. < 0.2 .and. (dx+dy)/2. >= 0.1) then + numinterp = 1 + else + numinterp = 0 + endif + + grdintincr = (dx+dy)/2. + do i = 1,numinterp + grdintincr = 0.5 * grdintincr + enddo + +c Now loop through the points in this subdomain, determine if any +c are within 500 km of the center, and then determine what quadrant +c the point is in relative to the center, and then calculate the +c fractional area coverage for winds. + + pdf_ct_tot = 0 + pdf_ct_bin = 0 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_fract_wind_cov, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_fract_wind_cov' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle iloop ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > (rads+(0.75*((dx+dy)/2.)*dtk*cos(glat(j)*dtr)))) + & then + + ! If the distance is greater than "rads" (500 km at initial + ! writing) plus another 3/4 of a gridpoint, then cycle. + ! The extra 3/4 of a gridpoint is to allow for the case of + ! some portion of the area around a gridpoint (whose + ! center point > 500 km) being within the 500 km arc... + ! although that is only factored in for grids with spacing + ! >= 0.1 deg. For smaller grids, where no interpolation is + ! done in this subroutine, then the distance to that point + ! is considered representative and the point is ignored if + ! it is not less than 500 km from the center. + + cycle iloop + + else + + ! First interpolate the area surrounding each grid point to + ! get fine resolution of lats & lons for determining how to + ! partition the area of a gridpoint among quadrants as well + ! as among distance thresholds. + + vmag = sqrt (u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + vmagkts = vmag * conv_ms_knots + + if (numinterp > 0) then + + grdintincr = ((dx+dy)/2.) / 2**numinterp ! "grid spacing" + ! of interpolated grid + ngridint = (2**numinterp) / 2 + + got_pdf = 'notyet' + + njloop: do nj= ngridint,-ngridint,-1 + + xintlat = glat(j) + float(nj) * grdintincr + + niloop: do ni= -ngridint,ngridint + + xintlon = glon(ii) + float(ni) * grdintincr + + call calcdist (xintlon,xintlat,xsfclon + & ,xsfclat,xdist,degrees) + + if (xdist <= 350. .and. got_pdf == 'notyet') then + ! The got_pdf flag is needed because in these loops + ! for niloop & njloop, we are actually looking at + ! tiny areas around the same grid point. So we + ! want to make sure we only count each gridpoint + ! once. + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + got_pdf = 'got_it' + endif + + if (xdist < 500.) then + + ! Compute area of this fraction of a grid box + xarea = (grdintincr * 111195) * + & (grdintincr * 111195 + & * cos(xintlat * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Go through a loop of the bins. The purpose of + ! this is that these "bins" all go from the + ! the center out to a specified radius, they are + ! NOT 100-km wide bins. So if we are dealing with + ! a point at r = 250 km, then that falls in the + ! 0-300 km bin, but it also falls in the 0-400 and + ! 0-500 km bins as well. So we need to run through + ! this binloop multiple times to get the area data + ! into multiple bins. Here are the bins & indices: + ! 1: 0-100 km + ! 2: 0-200 km + ! 3: 0-300 km + ! 4: 0-400 km + ! 5: 0-500 km + + binloop: do ib = idistbin,numbin + + if (xintlon >= xsfclon .and. + & xintlat >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (xintlon >= xsfclon .and. + & xintlat < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop + + endif + + enddo niloop + + enddo njloop + + else + + ! In this else statement is the case for a grid whose + ! resolution is already fine enough that we don't need + ! to interpolate any further. For example, we will have + ! the H*Wind data on a 0.05 degree grid, so that's already + ! fine enough. + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat + & ,xdist,degrees) + + if (xdist <= 350.) then + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + endif + + if (xdist < 500.) then + + ! Compute area of this grid box + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Why the binloop2? See explanation above in the "if" + ! part of this if-then block, where binloop is. + + binloop2: do ib = idistbin,numbin + + if (glon(ii) >= xsfclon .and. + & glat(j) >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (glon(ii) >= xsfclon .and. + & glat(j) < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop2 + + endif + + endif + + endif + + enddo iloop + + enddo jloop + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different quadrants, bins and thresholds... +c ------------------------------------------------- + + if ( verb .ge. 3 ) then + write (6,109) ' ' + & ,' ' + & ,' ' + write (6,109) ' Quadrant Bin Wind_Thresh ' + & ,'Fract_coverage (%) Area_exceeded' + & ,' Area_total' + write (6,109) ' -------- --- ----------- ' + & ,'------------------ -------------' + & ,' ----------' + write (6,109) ' ' + & ,' ' + & ,' ' + + do iq = 1,numquad + do ib = 1,numbin + do it = 1,numthresh + wfract_cov(iq,ib,it) = area_exceed_quad_bin(iq,ib,it) / + & area_total_quad_bin(iq,ib) + write (6,117) cquad(iq),cbin(ib),cthresh(it) + & ,wfract_cov(iq,ib,it)*100.0 + & ,area_exceed_quad_bin(iq,ib,it) + & ,area_total_quad_bin(iq,ib) + enddo + enddo + enddo + endif + + + 109 format (1x,a33,a37,a16) + 117 format (5x,a2,5x,a5,7x,a2,13x,f6.2,10x,f16.1,2x,f16.1) + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different bins and thresholds, but for the +c entire "disc" of the storm, that is, summing all +c quadrants together. +c ------------------------------------------------- + + do it = 1,numthresh + do ib = 1,numbin + do iq = 1,numquad + sum_total_area(ib,it) = sum_total_area(ib,it) + & + area_total_quad_bin(iq,ib) + sum_exceed_area(ib,it) = sum_exceed_area(ib,it) + & + area_exceed_quad_bin(iq,ib,it) + enddo + wfract_cov(5,ib,it) = sum_exceed_area(ib,it) + & / sum_total_area(ib,it) + enddo + enddo + + if ( verb .ge. 3 ) then + do ib = 1,numbin + do it = 1,numthresh + write (6,117) 'TT',cbin(ib),cthresh(it) + & ,wfract_cov(5,ib,it)*100.0 + & ,sum_exceed_area(ib,it) + & ,sum_total_area(ib,it) + enddo + enddo + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_ike_stats (imax,jmax,inp,dx,dy,ist,ifh + & ,fixlon,fixlat,xsfclon,xsfclat,valid_pt,calcparm + & ,ike,sdp,wdp,maxstorm,trkrinfo,igisret) +c +c ABSTRACT: This subroutine computes the Integrated Kinetic Energy +c (IKE) and Storm Surge Damage Potential (SDP) values, based on +c Powell (BAMS, 2007). At this time, we are only computing the IKE +c values for TS threshold (17.5 m/s) and above. We are not yet +c computing wind damage potential (WDP) since, per Mark Powell +c (4/2008), he is currently re-formulating an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c sdp Storm surge damage potential + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4 + integer npts,ipct,igisret,imax,jmax,ist,ifh,ilonfix,jlatfix + integer ibeg,jbeg,iend,jend,igiret,i,j,maxstorm,ii + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real ike(max_ike_cats) + real dx,dy,degrees,rads,ri,dell,xdist,vmag,xarea + real xsfclon,xsfclat,sdp,wdp + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) +c + igisret = 0 + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + rads = 400.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_ike_stats from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for storm ' + print *,'!!! number ',ist + endif + + igisret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_ike_stats calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igisret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + +c Search a grid of points near the storm center, evaluate if the +c storm is within the "rads" distance threshold. If so, compute +c the IKE values for all applicable thresholds (10, 18, 33 m/s). + + do j = jbeg,jend + do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ike_stats, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_ike_stats' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > rads) then + cycle + else + + vmag = sqrt(u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + + if (vmag > 10.0) then + ! Add gridpoint to IKE_10. Compute area first... + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + ike(1) = ike(1) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 18.0) then + ! Add gridpoint to IKE_ts. Area already computed for 10 + ike(2) = ike(2) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 33.0) then + ! Add gridpoint to IKE_h. Area already computed for 10 + ike(3) = ike(3) + (0.5 * (vmag**2) * xarea) + endif + + endif + + enddo + enddo + + ike(1) = ike(1) * 1.e-12 ! Convert from J to TJ + ike(2) = ike(2) * 1.e-12 ! Convert from J to TJ + ike(3) = ike(3) * 1.e-12 ! Convert from J to TJ + +c Compute the storm surge damage potential (sdp) + + if (ike(2) >= 0.0) then + sdp = 0.676 + (0.43 * sqrt(ike(2))) + & - (0.0176 * ((sqrt(ike(2)) - 6.5)**2) ) + else + sdp = -99.0 + endif + +c Print out the IKE and SDP statistics... + + if ( verb .ge. 3 ) then + print *,' IKE_10 (storm energy) = ',ike(1) + print *,' IKE_TS (tropical storm) = ',ike(2) + print *,' IKE_H (hurricane) = ',ike(3) + print *,' SDP = ',sdp + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine distbear (xlatin,xlonin,dist,bear,xlatt,xlont) +c +c ABSTRACT: Given an origin at latitude, longitude=xlato,xlono, +c this subroutine will locate a target point at a distance dist in +c km or nautical miles (depends on what you use for "rad_earth..." +c below), at bearing bear (degrees clockwise from north). +c Returns latitude xlatt and longitude xlont of target point. +c +c *** NOTE *** +c This subroutine was written to handle input lats & lons as this: +c All latitudes are in degrees, north positive and south negative. +c All longitudes are in degrees, west positive and east negative. +c *** **** *** +c +c However, for the longitudes, the rest of the tracker uses all +c 0-360 longitudes. Therefore, we need to convert the input lons +c and then once again convert the lons that are returned back to +c the calling routine. +c +c NOTE-- When origin is at north or south pole, bearing is no +c longer measured from north. Instead, bearing is measured +c clockwise from the longitude opposite that specified in xlono. +c Example-- if xlato=90., xlono=80., the opposite longitude is +c -100 (100 East), and a target at bearing 30. will lie on the +c -70. (70 East) meridian. +c +c AUTHOR: The core of this subroutine was written by Albion +c Taylor, another NOAA employee, in 1981. +c + USE trig_vals + + implicit none +c + real, parameter :: rad_earth_nm = 3440.170 ! radius of earth + real, parameter :: rad_earth_km = 6372.797 ! radius of earth + real xlato,xlono,dist,bear,xlatt,xlont,xlatin,xlonin + real cdist,sdist,clato,slato,clono,slono,cbear,sbear + real z,y,x,r,xlattz,xlontz,ddist,dbear,dxlato,dxlono +c + xlato = xlatin + xlono = xlonin + +cstr print *,' ' +cstr print *,'+++ At top of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlon= ',xlono,'E ',360.-xlono +cstr & ,'W xlat=',xlato +cstr print '(a6,f7.2,a8,f7.2)','dist= ',dist,' bear= ',bear + + if (xlono > 180.) then + ! Longitude input for this subroutine must be positive west + xlono = 360. - xlono + else + ! Longitude input for this subroutine must be negative east + xlono = -1. * xlono + endif + +cstr print '(a31,a8,f8.2)','After conversion for distbear, ' +cstr & ,' xlono= ',xlono + + ddist = dist + dbear = bear + dxlato = xlato + dxlono = xlono + + cdist = cos(ddist/rad_earth_km) + sdist = sin(ddist/rad_earth_km) + clato = cos(dtr*dxlato) + slato = sin(dtr*dxlato) + +cstr print *,'cdist= ',cdist,' sdist= ',sdist,' clato= ',clato +cstr & ,' slato= ',slato + + clono = cos(dtr*dxlono) + slono = sin(dtr*dxlono) + +cstr print *,'dxlono= ',dxlono,' clono= ',clono +cstr & ,' slono= ',slono + + cbear = cos(dtr*dbear) + sbear = sin(dtr*dbear) + +cstr print *,'cbear= ',cbear,' sbear= ',sbear + + z=cdist*slato + clato*sdist*cbear + y=clato*clono*cdist + sdist*(slono*sbear - slato*clono*cbear) + x=clato*slono*cdist - sdist*(clono*sbear + slato*slono*cbear) + +cstr print *,'z= ',z,' y= ',y,' x= ',x + + r = sqrt(x**2 + y**2) + +cstr print *,'r = sqrt(x**2 + y**2) = ',r + + xlattz = atan2(z,r)/dtr + +cstr print *,'xlattz = datan2(z,r)/dtr = ',xlattz + + xlatt = xlattz + + if (r <= 0.) go to 20 + + xlontz = atan2(x,y)/dtr + +cstr print *,'xlontz = atan2(x,y)/dtr = ',xlontz + +c xlont = xlontz + + ! Return the target longitude back to the calling routine + ! as a 0-360 positive east longitude.... + + xlont = mod(360.-xlontz,360.) + +c xlont = mod(360.+xlontz,360.) + +cstr print *,' ' +cstr print *,'At end of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlont= ',xlont,'E ' +cstr ,360.-xlont,'W xlatt=',xlatt + + return + 20 xlont=0. +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_uneven (targlat,targlon,dx,dy + & ,imax,jmax,trkrinfo,level,cparm,xintrp_val,ibiret) +c +c ABSTRACT: This subroutine performs a bilinear interpolation to get +c a data value at a given lat/lon that may be anywhere within a box +c defined by the four surrouding grid points. In the diagram below, +c remember that for our grids we are using in the tracker, the +c latitude index starts at the north pole and increases southward. +c The point "X" indicates the target lat/lon location of the value +c for which we are bilinearly interpolating. The values to and ta +c below are ratios that determine how geographically close the +c target location is to the point of origin (pt.1 (i,j)) in terms +c of both longitude (to) and latitude (ta). +c +c +c pt.1 pt.2 +c (i,j) (i+1,j) +c +c +c +c X +c +c pt.4 pt.3 +c (i,j+1) (i+1,j+1) +c + + USE grid_bounds; USE tracked_parms; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + character cparm*1 + real targlat,targlon,xintrp_val,dx,dy + real to,ta,d1,d2,d3,d4,z,eastlon + integer ie,iw,jn,js,ibiret,imax,jmax,level,nlev + + ibiret = 0 + +c -------------------------------------------------------------- +c For the latitudes and longitudes surrounding our target +c lat/lon location, convert the lat/lon values into i- and +c j-indices. +c -------------------------------------------------------------- + +c Find the j-indices for the points just to the north and the +c south of targlat.... + + if (targlat >= 0.0) then + ! For a northern hemisphere storm, jn is the j-index for the + ! point just to the *NORTH* (poleward) of targlat. + jn = int((glatmax - targlat)/dy + 1.) + js = jn + 1 + else + ! For a southern hemisphere storm, js is the j-index for the + ! point just to the *SOUTH* (poleward) of targlat. + js = ceiling((glatmax - targlat)/dy + 1.) + jn = js - 1 + endif + + ! Check to make sure that points are not being requested beyond + ! the northern or southern boundaries of the grid. This is most + ! likely to happen for a smaller, regional grid. + + if (jn > jmax .or. js > jmax) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jmax exceeded in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + + if (jn < 1 .or. js < 1) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jn < 0 or js < 0 in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + +c Find the i-indices for the points just to the east and the +c west of targlon.... + + ie = int((targlon - glonmin)/dx + 2.) + iw = ie - 1 + + ! Check for GM wrapping. Check ie to see if it is between the + ! most eastward gridpoint and the GM (i.e., on a 1-deg global + ! grid (360x181), it would be if targlon was between 359.0 (i=360) + ! and the GM (i=1, not i=361)). Similarly then, if we adjust ie + ! to then be 1, then we have a problem with iw, + ! since iw = 1 - 1 = 0. + + if (ie > imax) then + if (trkrinfo%gridtype == 'global') then + ie = ie - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ie > imax in subroutine ' + print *,'!!! bilin_int_uneven for a non-global grid. ' + print *,'!!! Returning to calling routine after ' + print *,'!!! assigning missing wind value of -99.' + print *,'!!! ie= ',ie,' imax= ',imax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + + if (iw < 1) then + if (trkrinfo%gridtype == 'global') then + iw = iw + imax + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: iw < 1 in subroutine bilin_int_uneven' + print *,'!!! for a non-global grid. Returning to calling ' + print *,'!!! routine after assigning missing wind value ' + print *,'!!! of -99. iw= ',iw + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' +++ Interpolating winds for cparm= ',cparm +ctmwc print '(6x,4(a4,i3))','jn= ',jn,' js= ',js,' iw= ',iw,' ie= ',ie +ctmwc endif + +c ---------------------------------------------------------------- +c Calculate the longitude (to) and latitude (ta) location ratios. +c Check for GM wrapping, as we can run into a problem here if +c interpolating for points that are just west of the GM, since we +c would be interpolating using values of longitude just west of +c GM (say, glon(iw)=359.5) and the GM (glon(ie) = 0.0). This +c makes for an incorrect "to" ratio below, with 0-359.5 in the +c denominator. We have to account for this.... +c ---------------------------------------------------------------- + + if (glon(iw) > 300.0 .and. + & (glon(ie) < 10. .and. glon(ie) >= 0.)) then + eastlon = 360. - glon(ie) + else + eastlon = glon(ie) + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,'glat(js)= ',glat(js),' glat(jn)= ',glat(jn) +ctmwc endif + + to = (targlon - glon(iw)) / (eastlon - glon(iw)) + ta = (targlat - glat(jn)) / (glat(js) - glat(jn)) + +c -------------------------------------------------------------- +c Copy the data values at the 4 known points into simple scalar +c variables +c -------------------------------------------------------------- + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cparm == 'u') then + d1 = u(iw,jn,nlev) + d2 = u(ie,jn,nlev) + d3 = u(ie,js,nlev) + d4 = u(iw,js,nlev) + else if (cparm == 'v') then + d1 = v(iw,jn,nlev) + d2 = v(ie,jn,nlev) + d3 = v(ie,js,nlev) + d4 = v(iw,js,nlev) + else if (cparm == 'm') then + d1 = lsmask(iw,jn) + d2 = lsmask(ie,jn) + d3 = lsmask(ie,js) + d4 = lsmask(iw,js) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in bilin_int_uneven.' + print *,'!!! Input cparm not recognized.' + print *,'!!! cparm= ',cparm + print *,'!!! EXITING....' + endif + + stop 95 + endif + + z = 1.9427 + +cstr print '(2x,4(a4,f8.2))',' d1= ',d1*z,' d2= ',d2*z +cstr & ,' d3= ',d3*z,' d4= ',d4*z + +c ------------------------------------------------------------- +c Compute the interpolated value +c ------------------------------------------------------------- + + xintrp_val = (1.-to) * (1.-ta) * d1 + & + to * (1.-ta) * d2 + & + to * ta * d3 + & + (1.-to) * ta * d4 + +cstr print '(2x,2(a11,f8.2))',' xintrp= ',xintrp_val,' (in kts)= ' +cstr & ,xintrp_val*z +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine sort_storms_by_pressure (gridprs,ifh,maxstorm,sortindex + & ,issret) +c +c ABSTRACT: This subroutine sorts storms by mslp. It is called by +c subroutine tracker just before the loop for "stormloop" is done +c for all the storms at a particular forecast hour. It is only +c called for the "midlat" and "tcgen" cases. The end result of +c this sort is an array (prsindex) that contains the indeces of +c the storms, arranged from lowest pressure to highest (and note +c that the "undefined" storms have a pressure of 9999.99 mb and +c thus get sorted to the bottom of the array). The purpose of +c doing this is so that we track the most intense storms first. +c Why go to the trouble? Imagine a scenario in which we are +c tracking a complex system in which there are 2 low pressure +c centers. Let's say that one is becoming dominant and +c intensifying, while the other is weakening. Now, let's assume +c that the weakening one eventually gets absorbed into the +c stronger, more dominant low. Now we only have 1 low, but if in +c the tracker stormloop, we first process the data for the +c weakening low, we will attribute the track to that storm, and +c then when we get to the point in the loop where we are trying +c to get the track for the stronger storm, we will (erroneously) +c stop the tracking for that storm since the storm center has +c already been attributed to the weaker storm. But by using this +c subroutine, we will track the stronger storm first, and thus +c avoid this problem. +c +c NOTE: The pressures used in the sort are those obtained at the +c previous forecast hour. At forecast hour = 0, just use the +c values as they were input to this routine, since they were +c found in first_ges_center from strongest to weakest already. +c +c INPUT: +c gridprs real array of storm mslp values +c ifh integer index for the current forecast hour +c maxstorm max num of storms that can be handled in this run +c +c OUTPUT: +c sortindex contains a sorted array of indeces. The orders +c sort routine does NOT rearrange the data. Rather, it +c returns this array of sorted indeces which point to +c the correct order of data values in the data array. +c issret return code from this subroutine +c + USE set_max_parms + USE verbose_output + + real, allocatable :: iwork(:) + real gridprs(maxstorm,maxtime) + integer ifh,maxstorm + integer sortindex(maxstorm) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: prstemp(:) +c + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iva /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub sort_storms_by_pressure allocating' + print *,'!!! prstemp or iwork arrays: ' + print *,'!!! iva= ',iva,' iwa= ',iwa + endif + + STOP 94 + return + endif + + if (ifh > 1) then + +c print *,' ' +c print *,'--- Before sort, original prs values follow:' +c print *,' ' + + do ist = 1,maxstorm + prstemp(ist) = gridprs(ist,ifh-1) +c write (6,81) ist,prstemp(ist)/100.0 + enddo + + imode = 2 + sortindex = 0 + call qsort (prstemp,sortindex,maxstorm) + +ccccc call orders (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) +ccccc call orders_4byte (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Pressure-sorted storm list:' + print *,' ' + + do ist = 1,maxstorm + if (prstemp(sortindex(ist))/100.0 < 9999.0) then + write (6,82) ist,sortindex(ist) + & ,prstemp(sortindex(ist))/100.0 + endif + enddo + + 81 format (1x,'ist= ',i5,' Original (unsorted) prstemp= ',f7.2) + 82 format (1x,'ist= ',i5,' sortindex(ist)= ',i5 + & ,' prstemp= ',f7.2) + endif + + else + do ist = 1,maxstorm + sortindex(ist) = ist + enddo + endif + + deallocate (prstemp); deallocate (iwork) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getvrvt (centlon,centlat,xlon,xlat + & ,udat,vdat,vr,vt,igvtret) +c +c ABSTRACT: This subroutine takes as input a u-wind and v-wind value +c at an input (xlon,xlat) location and returns the tangential and +c radial wind components relative to the input center lat/lon +c position (centlon,centlat). The only trick to this whole +c subroutine is figuring out the angle from the center point to the +c data point, and we do this by creating a triangle with the leg +c from the center point to the data point being the hypotenuse. +c +c NOTE: All longitudes must be in positive degrees east (0-360) !!! +c +c INPUT: +c centlon Longitude of center point +c centlat Latitude of center point +c xlon Longitude of pt at which vr & vt will be computed +c xlat Latitude of pt at which vr & vt will be computed +c udat u-value of wind at the point (xlon,xlat) +c vdat v-value of wind at the point (xlon,xlat) +c +c OUTPUT: +c vr Radial wind component at (xlon,xlat) wrt (centlon,centlat) +c vt Tang wind component at (xlon,xlat) wrt (centlon,centlat) +c igvtret Return code from this subroutine +c + USE trig_vals + USE verbose_output + + implicit none + + real centlon,centlat,xlon,xlat,udat,vdat,vr,vt,degrees,tmpxlon + real angle,xlondiff,xlatdiff,opp_dist,hyp_dist,sin_value + real cos_value,adj_dist,tmpangle,sin_angle,cos_angle + real uvrcomp,vvrcomp,uvtcomp,vvtcomp + integer igvtret +c + call calcdist(centlon,centlat,xlon,xlat,hyp_dist,degrees) + +c xxxx + + tmpxlon = xlon + + if (centlon > 330.0) then + + if (xlon > 360.0) then + + tmpxlon = xlon ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (xlon < 30.0) then + + tmpxlon = xlon + 360. ! In this case, the fix center is just + ! to the west of the GM with a lon (centlon) + ! > 330, while the point being evaluated + ! (xlon) is just east of the GM, but with a + ! lon (centlon) < 30. Need to adjust here to + ! to get the xlon in the 330+ frame of + ! reference. + + endif + + elseif (centlon >= 0 .and. centlon < 30.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = 360. - xlon + + endif + + elseif (centlon < 0.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = -1 * (360. - xlon) + + endif + + endif + + xlatdiff = abs(centlat - xlat) + xlondiff = abs(centlon - tmpxlon) + + if (centlon > 355.0) then + write (6,91) centlon,tmpxlon,hyp_dist,degrees,xlondiff + 91 format (1x,'centlon= ',f8.3,' tmpxlon= ',f8.3,' hyp_dist= ' + & ,f10.2,' degrees= ',f10.2,' xlondiff= ',f12.2) + endif + + if (xlondiff == 0 .and. xlatdiff > 0) then + + if (centlat > xlat) angle = 180 ! pt directly south of ctr + if (centlat < xlat) angle = 0 ! pt directly north of ctr + + else if (xlondiff > 0 .and. xlatdiff == 0) then + + if (centlon > tmpxlon) angle = 270 ! pt directly west of ctr + if (centlon < tmpxlon) angle = 90 ! pt directly east of ctr + + else + + ! This next part figures out the angle from the center point + ! (centlon,centlat) to the data point (tmpxlon,xlat). It does + ! this by setting up a triangle and then using inverse trig + ! functions to get the angle. Since this is a kludgy way to + ! do it that doesn't account for the curvature of the earth, + ! we'll do it 2 ways, using asin and then acos, then take the + ! average of those 2 for the angle. hyp_dist, calculated just + ! above, is the distance from the center pt to the data pt. + + opp_dist = xlatdiff/360. * ecircum + sin_value = opp_dist / hyp_dist + if (sin_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, sin_value > 1, setting to 1.' + print *,'!!! opp_dist= ',opp_dist,' hyp_dist= ',hyp_dist + print *,'!!! sin_value = ',sin_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + sin_value = 0.99999 + endif + sin_angle = asin(sin_value) / dtr + + call calcdist(centlon,centlat,tmpxlon,centlat,adj_dist,degrees) + cos_value = adj_dist / hyp_dist + if (cos_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, cos_value > 1, setting to 1.' + print *,'!!! adj_dist= ',adj_dist,' hyp_dist= ',hyp_dist + print *,'!!! cos_value = ',cos_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + cos_value = 0.99999 + endif + cos_angle = acos(cos_value) / dtr + + tmpangle = 0.5 * (sin_angle + cos_angle) + + ! The previous lines of code just calculated an angle between + ! 0 and 90. This next if structure adjusts that angle to + ! instead be between 0 and 360. + + if (centlat <= xlat .and. centlon <= tmpxlon) then + angle = 90 - tmpangle + else if (centlat > xlat .and. centlon <= tmpxlon) then + angle = 90 + tmpangle + else if (centlat >= xlat .and. centlon >= tmpxlon) then + angle = 270 - tmpangle + else if (centlat < xlat .and. centlon >= tmpxlon) then + angle = 270 + tmpangle + endif + + endif + + uvrcomp = udat * sin(angle * dtr) + vvrcomp = vdat * cos(angle * dtr) + vr = uvrcomp + vvrcomp + + uvtcomp = (-udat) * cos(angle * dtr) + vvtcomp = vdat * sin(angle * dtr) + vt = uvtcomp + vvtcomp + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcfunix (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in the new ATCF UNIX format. +c Unlike the old atcf DOS format in which you waited until the +c whole tracking was over to write the output for all forecast +c hours, with this atcfunix format, each time we are calling this +c subroutine, it is to only write out 1 record, which will be the +c fix info for a particular storm at a given time. Also, even +c though we have some data (GFS, NAM) at 6-hour intervals, Jim +c Gross informed me that TPC does not need the positions at such +c frequency, and keeping the reporting at 12 hour intervals is fine. +c +c While this new atcfunix format contains much more information than +c the old 1-line atcf dos message, for our purposes we will use the +c slots for mslp and wind radii. An example set of output records +c will look like the following: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c plastbar pressure of the outermost closed isobar +c rlastbar radius (nm) of the outermost closed isobar +c rmax radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c cps_vals real array with the values for the 3 cyclone phase +c space parameters: (1) is for Parameter B (thermal +c asymmetry); (2) is for lower level (600-900 mb) thermal +c wind; (3) is for upper level (300-600 mb) thermal wind. +c wcore_flag character for value of 300-500 mb warm core: y, n, or +c 'u' for undetermined. +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE phase + USE verbose_output + + type (datecard) inp + type (trackstuff) trkrinfo + + real cps_vals(3) + real outlon,outlat,rmax,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,irmax,output_fhr,ic,iplastbar,irlastbar + integer vradius(3,4),icps_vals(3) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + character comma_fill1*48,comma_fill2*31,comma_filler*79 + + if ( verb .ge. 3 ) then + print *,'TTT top of atcfunix, ist= ',ist,' ifh= ',ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcfunix. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'in output_atcfunix, tcv_storm_id= ' + & ,storm(ist)%tcv_storm_id + print *,'in output_atcfunix, tcv_storm_id(3:3)= ' + & ,storm(ist)%tcv_storm_id(3:3) + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' +!zhang case ('A','a'); basinid = 'NA' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + if (trkrinfo%want_oci) then + if (plastbar > 0.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -99 + endif + if (rlastbar > 0.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -99 + endif + else + iplastbar = -99 + irlastbar = -99 + endif + + if ( verb .ge. 3 ) then + print *, 'output: rlastbar=',rlastbar,' irlastbar=',irlastbar + print *, 'output: plastbar=',plastbar,' iplastbar=',iplastbar + endif + +c Now convert all of the cyclone phase space parameter values from +c real to integer. + + do ic = 1,3 + if (cps_vals(ic) > -9999.0) then + if (cps_vals(ic) >= 0.0) then + icps_vals(ic) = int(cps_vals(ic)*10. + 0.5) + else + icps_vals(ic) = int(cps_vals(ic)*10. - 0.5) + endif + else + icps_vals(ic) = -9999 + endif + enddo + + if (wcore_flag == 'y'.or. wcore_flag == 'Y') then + wcore_flag = 'Y' + elseif (wcore_flag == 'n' .or. wcore_flag == 'N') then + wcore_flag = 'N' + elseif (wcore_flag == 'u' .or. wcore_flag == 'U') then + wcore_flag = 'U' + else + wcore_flag = 'U' + endif + + comma_fill1 = ', 0, 0, , 0, , 0, 0, ,' + comma_fill2 = ' , , , 0, 0, 0, 0' + comma_filler = comma_fill1//comma_fill2 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + else + + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,'rmax= ',rmax,' irmax= ',irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,a79,', THERMO PARAMS' + & ,3(', ',i7),', ',a1,', ',i2,', DT, -999') + 91 format (a2,', ',a4,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,2(', ',i3),', ',a3) + +c bug fix for IBM: flush the output stream so it actually writes + flush(64) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each +c storm. This message contains the model identifier, the forecast +c initial date, and the positions for 0, 12, 24, 36, 48, 60 and 72 +c hours. In the case of the regional models (NGM, Eta), which +c only go out to 48h, zeroes are included for forecast hours +c 60 and 72. +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by Steve Lord for plotting purposes, and his +c plotting routines need the longitudes in 0 - 360, increasing +c westward. Thus, a necessary adjustment is made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + print *,'top of output_all' + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + intlon(ifh) = 0 + intlat(ifh) = 0 + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + endif + + enddo ifhloop + + print *,'before select case, atcfname= ' + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(5),intlon(5),intlat(9),intlon(9),intlat(13) + & ,intlon(13),intlat(17),intlon(17),intlat(21),intlon(21) + & ,0,0,storm(ist)%tcv_storm_id + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(3),intlon(3),intlat(5),intlon(5),intlat(7) + & ,intlon(7),intlat(9),intlon(9),intlat(11),intlon(11) + & ,intlat(13),intlon(13),storm(ist)%tcv_storm_id + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3),intlat(4) + & ,intlon(4),intlat(5),intlon(5),intlat(6),intlon(6) + & ,intlat(7),intlon(7),storm(ist)%tcv_storm_id + + case ('GDA','HDA') ! GDAS, HDAS + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),0,0,0,0,0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case default +c print *,'!!! ERROR in subroutine output_all. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + print *,'!!! Model name is not identified: ',atcfname + + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + 81 format (i2,a4,4i2.2,14i4,1x,a3) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm + & ,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each storm +c in ATCF format. This message contains the model identifier, the +c forecast initial date, and the positions for 12, 24, 36, 48 +c and 72 hours. This message also contains the intensity +c estimates (in knots) for those same hours. The conversion for +c m/s to knots is to multiply m/s by 1.9427 (3.281 ft/m, +c 1 naut mile/6080 ft, 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by the atcf system at TPC for plotting +c purposes, and the atcf plotting routines need the longitudes in +c 0 - 360, increasing westward. Thus, a necessary adjustment is +c made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real xmaxwind(maxstorm,maxtime) + real conv_ms_knots + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4,basinid*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + conv_ms_knots = 1.9427 + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + + intlon(ifh) = 0 + intlat(ifh) = 0 + + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + + endif + + enddo ifhloop + + basinid = ' ' + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid(1:2) = 'AL' + case ('E','e'); basinid(1:2) = 'EP' + case ('C','c'); basinid(1:2) = 'CP' + case ('W','w'); basinid(1:2) = 'WP' + case ('O','o'); basinid(1:2) = 'SC' + case ('T','t'); basinid(1:2) = 'EC' + case ('U','u'); basinid(1:2) = 'AU' + case ('P','p'); basinid(1:2) = 'SP' + case ('S','s'); basinid(1:2) = 'SI' + case ('B','b'); basinid(1:2) = 'BB' +cPENG case ('A','a'); basinid(1:2) = 'NA' + case ('A','a'); basinid(1:2) = 'AA' + case default; basinid(1:2) = '**' + end select + basinid(3:4) = storm(ist)%tcv_storm_id(1:2) + + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(5),intlon(5) + & ,intlat(9),intlon(9),intlat(13),intlon(13),intlat(17) + & ,intlon(17),0,0 + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,17)*conv_ms_knots) + 0.5) + & ,0 + & ,basinid,inp%byy + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble, ECMWF hi-res + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4),intlat(5) + & ,intlon(5),intlat(7),intlon(7) + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('GDA','HDA') ! GDAS, HDAS + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4) + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,0,0,0,0 + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case default +c print *,'!!! ERROR in subroutine output_atcf. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + end select + + enddo stormloop + + 82 format (i2,a4,4i2.2,10i4,5i3,1x,a4,i2.2) +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_hfip (outlon,outlat,inp,ist + & ,ifh,vmaxwind,xminmslp,vradius,rmax,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified ATCF UNIX format. +c The modification is to allow for sub-hourly output. That is, +c instead of just integer output hours, we can have output at +c 10, 15 or 20 past an hour. This necessitates a change in the +c "forecast hour" placeholder in the ATCF format. Instead of it +c being an I3, we'll make it an I5, with something like a lead time +c of 36.25h being rounded and truncated to 03625 for output. +c +c An example set of output records using the standard atcf format +c looks like the following: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c An example set of modified output records will look like the +c following, for the case of a lead time of 36:15 (36.25): +c +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifh index for the lead time array +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c rmax Radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms + USE verbose_output + + type (datecard) inp + + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,rmax + integer intlon,intlat,output_fhr,irmax,ileadtime + integer vradius(3,4) + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_hfip. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + ! ST: ifcsthour does not exist, so output_fhr is always + ! filled with invalid data here. However, output_fhr is + ! never used, so it is safe to remove. + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + ! output_fhr = ifcsthour + 3 + ileadtime = nint((fhreal(ifh) + 3.0) * 100.0) + else + ! output_fhr = ifcsthour + ileadtime = nint(fhreal(ifh) * 100.0) + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4),irmax + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4),irmax + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4),irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i5.5,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', 0, 0, ',i3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(69) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_fract_wind (outlon,outlat,xsfclon,xsfclat + & ,inp,ist,ifcsthour,vmaxwind,xminmslp,wfract_cov + & ,wfract_type,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values for the fractional areal coverage of various wind +c thresholds. In addition, this subroutine also writes out +c records to a file containing data on the PDF of wind magnitudes +c within r=350 km. +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with areal coverage thresholds. +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, NEE, 981, 857, 629, 810 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, NEE, 874, 732, 319, 610 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, NEE, 454, 327, 99, 270 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, AAE, 721, 721, 721, 721 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, AAE, 465, 465, 465, 465 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, AAE, 298, 298, 298, 298 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the pctgs for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind pctg info; all the +c other info is identical for each entry. +c +c Listed after the "XX" in each record is the radius from which +c the coverage is valid (000 km in this case); Next is the radius +c at which the coverage stops (100 km in this case). Next is the +c wind threshold (34, 50, 64). Next is an identifier for which +c quadrant the coverage starts in (first 2 characters are NE, SE, +c SW, NW); the last character indicates if the coverages are +c computed in the quadrants as earth-relative ("E") or +c storm-motion relative ("R"). The ones listed there as "AAE" +c are for the full disc (i.e., 4-quadrant average), earth-relative. +c Next are the wind coverage percentages, listed as percentage * 10 +c (e.g., 981 = 98.1%). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c wfract_cov percent areal coverage for various wind thresholds +c wfract_type 'earth' or 'storm' relative analysis +c pdf_ct_bin array for pdf of wind magnitudes within r=350 km +c pdf_ct_tot total count of pdf points for r < 350 km +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,pdfval + real wfract_cov(numquad+1,numbin,numthresh) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer :: windthresh(numthresh) = (/34,50,64/) + integer pdf_ct_bin(16) + integer intlon,intlat,output_fhr,intlon100,intlat100,pdf_ct_tot + integer maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (wfract_type == 'earth') then + wt = 'E' + else if (wfract_type == 'storm') then + wt = 'R' + else + wt = 'X' + endif + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'NE',wt + & ,int((1000.*wfract_cov(1,ib,it))+0.5) + & ,int((1000.*wfract_cov(2,ib,it))+0.5) + & ,int((1000.*wfract_cov(3,ib,it))+0.5) + & ,int((1000.*wfract_cov(4,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'AA',wt + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a6,i3.3,', ',i3.3,', ' + & ,i3,', ',a2,a1,4(', ',i4),', ',i4,a1,', ',i5,a1) + +c -------------------------------------------------- +c Now compute and write out the pdf values for the +c wind magnitude.... +c -------------------------------------------------- + + do ip = 1,16 + pdfval = float(pdf_ct_bin(ip)) / float(pdf_ct_tot) + write (76,85) atcfymdh,basinid,storm(ist)%tcv_storm_id(1:2) + & ,output_fhr,10*(ip-1),10*ip,pdf_ct_bin(ip) + & ,pdf_ct_tot,pdfval + enddo + + 85 format (1x,i10.10,3x,a2,a2,3x,i3,3x,i3.3,'_',i3.3,3x,i7,2x,i7 + & ,2x,f6.3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(73) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_wind_structure (outlon,outlat,xsfclon + & ,xsfclat,inp,ist,ifcsthour,vmaxwind,xminmslp,er_wind + & ,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values of the winds at specified distances along 45-degree +c radials in each storm quadrant. These are output +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 radials: NE, SE, +c SW and NW (45,135,225,315). For the storm-relative estimates, +c these radials will be computed at the same relative angles (i.e., +c 45,135,225,315), but with respect (positive clockwise) to the +c direction of storm motion. For example, for a storm moving with +c a heading of 280, the wind structure is evaluated at these +c radials: 325 (front-right; 45 deg CW from heading), 55 (back- +c right; 135 deg CW from heading), 145 (back-left; 225 deg CW from +c heading), 235 (front-left; 315 deg CW from heading). +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with wind values at the 13 specified distances +c (10, 25, 50, 75, 100, 150, 200, 250, 300, 350, 400, 450, 500 km) +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NEE, 1137, 1221, 854, 655, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SEE, 947, 982, 474, 396, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SWE, 645, 683, 328, 277, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NWE, 725, 753, 619, 429, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FRR, 1134, 1224, 852, 654, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BRR, 944, 984, 472, 393, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BLR, 649, 686, 321, 272, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FLR, 729, 756, 613, 421, etc., ... out to 500 km +c +c NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text. +c NOTE: These winds are in m/s coming into this routine and will +c be converted to knots*10 for output (e.g., 1221 = 122.1 kts) +c +c The "71" ID indicates earth-relative winds, the "72" ID indicates +c storm-relative winds. Here are the other IDs that will be used: +c 81: Tangential winds, earth-relative (m/s) +c 82: Tangential winds, storm-relative (m/s) +c 91: Radial winds, earth-relative (m/s) +c 92: Radial winds, storm-relative (m/s) +c +c Note that in this example, for this 36h forecast hour, there are +c 8 entries. This is so that we can include the wind values for +c the 4 different quadrants, for both the earth relative analyses +c (NEE, SEE, SWE, NWE) and the storm-relative analyses (FRR, BRR, +c BLR, FLR). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + integer ioutwind(numdist) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real outlon,outlat + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,id,intlon100,intlat100,ir + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*2 :: crel(4) = (/'FR','BR','BL','FL'/) + + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + +c Total wind (converted to knots*10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 71, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Total wind (converted to knots*10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 72, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 81, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 82, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 92, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a10,a2,a1,14(', ',i4) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(72) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_ike (outlon,outlat,xsfclon,xsfclat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,ike,sdp,wdp,maxstorm + & ,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the Integrated Kinetic Energy (IKE) and Storm Surge Damage +c Potential (SDP), based on Powell (BAMS, 2007). At this time, we +c are only computing the IKE values for TS threshold (17.5 m/s) and +c above. We are not yet computing wind damage potential (WDP) +c since, per Mark Powell (4/2008), he is currently re-formulating +c an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with WDP, SDP and IKE values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, 340, 560, 212, 174, 42, 93, 12, 0 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, WDP, SDP, I10, ITS, IH ,I2540,I4154, I55 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Values for WDP and SDP are multiplied by 10 in this routine +c before being written out. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c ike integrated kinetic energy, in units of TJ +c sdp storm surge damage potential +c wdp wind damage potential +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,sdp,wdp + real ike(max_ike_cats) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,intlon100,intlat100,maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (74,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, IKE',int((wdp*10)+0.5),int((sdp*10)+0.5) + & ,int(ike(1)+0.5),int(ike(2)+0.5),int(ike(3)+0.5) + & ,int(ike(4)+0.5),int(ike(5)+0.5),int(ike(6)+0.5) + & ,intlat100,clatns,intlon100,clonew +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a14,8(',',i5) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(74) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_phase (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,paramb,vtl_slope + & ,vtu_slope,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the three parameters that comprise Bob Hart's cyclone phase +c space (CPS). These parameters are his "parameter B", which +c assesses the left-right thermal asymmetry, and the upper +c troposphere (300-600 mb) and lower troposphere (900-600 mb) +c thermal wind values. +c +c LOCAL: +c +c Arrays: +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with paramb, vtl_slope and vtu_slope values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, 340, 560, 212 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, B, VTL, VTU +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c paramb thermal asymmetry +c vtl_slope thermal wind value for lower troposphere (900-600 mb) +c vtu_slope thermal wind value for upper troposphere (600-300 mb) +c +c OUTPUT: +c ioiret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + real outlon,outlat,paramb,vtl_slope,vtu_slope + real vmaxwind,conv_ms_knots,xminmslp + integer intlon,intlat,output_fhr + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (71,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 95, CPS',int(paramb+0.5),int(vtl_slope+0.5) + & ,int(vtu_slope+0.5) +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a14,3(',',i6)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(71) + + return + end +c +c----------------------------------------------------------------------- + subroutine output_atcf_gen1 (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different. Here's an example of the +c TPC standard atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c be the one that is pulled from the tcvitals or gen_vitals +c record: +c +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 50, NEQ, 155, 000, 000, 000 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 64, NEQ, 000, 000, 000, 000 +c +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd storm translation speed +c istmdir direction of storm movement +c plastbar pressure of last closed isobar +c rlastbar radius of last closed isobar +c rmax radius of max winds +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals; USE level_parms + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real outlon,outlat,plastbar,rlastbar,rmax + real vmaxwind,conv_ms_knots,xminmslp,mslp_outp_adj + real cps_vals(3) + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar,irmax + integer ivtl,ivtu,iparamb,output_fhr + integer vradius(3,4) + integer imeanzeta(nlevgrzeta),igridzeta(nlevgrzeta) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + + if ( verb .ge. 3) then + print *,'+++ Top of output_atcf_gen, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (rmax > -90.0) then + irmax = int(rmax + 0.5) + else + irmax = -99 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(66) + + return + end +c +c----------------------------------------------------------------------- +cPENG---2018-06-07------------------------------------------------- +c----------------------------------------------------------------------- +c subroutine output_atcf_gen (outlon,outlat,inp,ist +c & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm +c & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax +c & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + subroutine output_atcf_gen (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different. Here's an example of the +c TPC standard atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c be the one that is pulled from the tcvitals or gen_vitals +c record: +c +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 50, NEQ, 155, 000, 000, 000 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 64, NEQ, 000, 000, 000, 000 +c +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd storm translation speed +c istmdir direction of storm movement +c plastbar pressure of last closed isobar +c rlastbar radius of last closed isobar +c rmax radius of max winds +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real outlon,outlat,plastbar,rlastbar,rmax + real vmaxwind,conv_ms_knots,xminmslp + real cps_vals(3) + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar,irmax + integer ivtl,ivtu,iparamb,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + +cJ.Peng----2014-10-01----------------------------- + real wcore_mean_val,wcore_point_max + real imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + + if ( verb .ge. 3) then + print *,'+++ Top of output_atcf_gen, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + + if (xminmslp == 999999.0) xminmslp = 0.0 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/100. + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (rmax > -90.0) then + irmax = int(rmax + 0.5) + else + irmax = -99 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- +c & ,imeanzeta(1),abs(igridzeta(1)),imeanzeta(2),abs(igridzeta(2)) + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- +c & ,imeanzeta(1),abs(igridzeta(1)),imeanzeta(2),abs(igridzeta(2)) + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + endif + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4)) +cJ.Peng----2010-10-01----------------------------------------------- + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4) + & ,8(', ',f8.1)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(66) + + return + end +c +c----------------------------------------------------------------------- + subroutine output_atcf_sink (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta,igridzeta + & ,cps_vals,plastbar,rlastbar,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The "sink" in the subroutine name indicates that this output +c contains the whole kitchen sink of forecast storm info. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different, and the part after the radii +c data is different. Here's an example of the TPC standard +c atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c indicate the lat/lon at which the storm was *first* found in +c the model. The position may be either found within this run +c of the tracker, or that position may have been pulled from the +c tcvitals or gen_vitals record: +c +c 2000092500_F000_206N_0623W_13L, 2000092500, 03, GFSO, 036 +c , 243N, 675W, 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c , PLAS, RLAS, RMX, DIR, SPD, B, VTU, VTL +c , Z8MN, Z8MX, Z7MN, Z7MX +c +c As noted above, there is extra info at the end, after the +c "34, NEQ, 242, 163, 124, 208" radii info. Here is a key +c to indicate what these items are: +c +c PLAS: Pressure (mb) of last closed isobar +c RLAS: Radius of the last closed isobar in nm, 0 - 9999 nm. +c RMX: Radius of max winds, 0 - 999 nm. +c DIR: Direction of storm motion. +c SPD: Speed of storm motion (m/s * 10). +c B: Hart's CPS "Parameter B" thickness asymmetry value (m). +c VTL: Hart's CPS thermal wind (Lower, 900-600) value. +c VTU: Hart's CPS thermal wind (Upper, 600-300) value. +c Z8MN: Mean value of 850 mb zeta surrounding storm. +c Z8MX: Max value of 850 mb zeta near storm. +c Z7MN: Mean value of 700 mb zeta surrounding storm. +c Z7MX: Max value of 700 mb zeta near storm. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd speed of storm translation +c istmdir direction of storm motion +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c plastbar pressure of last closed isobar (pa) +c rlastbar radius of last closed isobar (nm) +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real cps_vals(3) + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar + integer iparamb,ivtl,ivtu,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1 + + if ( verb .ge. 3 ) then + print *,'+++ Top of output_atcf_sink, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4)) + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',2(i4,', '),4(i3,', '),2(i5,', '),4(i4,', '),a9) + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',2(i4,', '),i3,', ',2(i4,', '),3(i6,', '),4(i6,', ') + & ,a9) + +c write (68,87) gstm%gv_gen_date,gstm%gv_gen_lat +c & ,gstm%gv_gen_latns,gstm%gv_gen_lon +c & ,gstm%gv_gen_lonew,gstm%gv_gen_type +c & ,inp%bcc,inp%byy,inp%bmm,inp%bdd,inp%bhh +c & ,adjustr(atcfname),ifcsthour,intlat,clatns,intlon,clonew +c & ,int((vmaxwind*conv_ms_knots) + 0.5) +c & ,int(xminmslp/100.0 + 0.5) +c & ,'XX, 34, NEQ' +c & ,istmspd,istmdir,imeanzeta(1),igridzeta(1) +c & ,imeanzeta(2),igridzeta(2) +c +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,6(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(68) + + return + end + +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_tcvitals (xlon,xlat,inp,ist,iovret) +c +c ABSTRACT: This subroutine outputs a tcvitals record. The +c lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE inparms; USE set_max_parms + USE verbose_output + + type (tcvcard) stm + type (datecard) inp + real xlon,xlat +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "storm" +c components for this storm, then we will change the specific +c components that we need to. + + stm = storm(ist) + + stm%tcv_center = 'AEAR' + + stm%tcv_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + stm%tcv_latns = 'S' + else + stm%tcv_latns = 'N' + endif + + if (xlon >= 180.) then + stm%tcv_lon = 3600 - int(xlon * 10. + 0.5) + stm%tcv_lonew = 'W' + else + stm%tcv_lon = int(xlon * 10. + 0.5) + stm%tcv_lonew = 'E' + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) stm + endif + + write (65,21) stm + + 21 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + +c +c bug fix for IBM: flush the output stream so it actually writes + flush(65) + + return + end + +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_gen_vitals (xlon,xlat,inp,ist,istmspd,istmdir + & ,iovret) +c +c ABSTRACT: This subroutine outputs a modified vitals record. +c The lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c The storm identifier is different than that for a standard +c tcvitals. The storm identifier contains the date/time that +c the storm was first identified, and the lat/lon position at +c which it was first identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE gen_vitals; USE inparms; USE set_max_parms + USE verbose_output + + implicit none + + type (gencard) gstm + type (datecard) inp + real xlon,xlat + integer ist,iovret,istmspd,istmdir +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the vitals record. + + if (gstm%gv_gen_date /= 99999) then + + if (gstm%gv_gen_type /= 'FOF') then + ! If this is not a 'FOF' storm (found on the fly storm), then + ! it must be a TC vitals storm, or a tropical cyclone, and we + ! don't want to create a vitals record for a tropical cyclone, + ! since we will rely on reading them from the TC Vitals + ! database instead. + return + endif + + else + + ! This storm is new in this forecast/analysis and was found on + ! the fly in the first time level for this run and there was no + ! previous vitals record for this system + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = 0 + + gstm%gv_gen_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_gen_latns = 'S' + else + gstm%gv_gen_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_gen_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'W' + else + gstm%gv_gen_lon = int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'E' + endif + + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + gstm%gv_obs_ymd = inp%bcc * 1000000 + & + inp%byy * 10000 + & + inp%bmm * 100 + & + inp%bdd + + gstm%gv_obs_hhmm = inp%bhh * 100 + + gstm%gv_obs_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_obs_latns = 'S' + else + gstm%gv_obs_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_obs_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'W' + else + gstm%gv_obs_lon = int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'E' + endif + + gstm%gv_stdir = istmdir + gstm%gv_stspd = istmspd + + gstm%gv_depth = 'U' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) gstm + endif + + write (67,21) gstm + + 21 format (i10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1,'_',a3,1x,i8,1x + & ,i4.4,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x + & ,i3,4(1x,i4),1x,a1) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(67) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_tracker_mask (masked_outc,lb,ifh,ifcsthour + & ,imax,jmax,iotmret) +c +c ABSTRACT: This subroutine outputs a GRIB record that contains the +c "mask" used to mask out areas surrounding low pressure centers +c that have been found during the search at each forecast hour. This +c mask is written out purely for diagnostic purposes. The GRIB +c identifier given to the mask in the pds is 850 mb height (you can +c make it anything you want). This is only done for the "midlat" +c and "tcgen" cases, since the runs for those cases use a mask while +c the regular "tracker" run (that is, the run which strictly tracks +c only those storms in the TC vitals file) does not. +c +c INPUT: +c masked_outc logical array containing mask +c ifh integer counter for current forecast hour +c ifcsthour integer current forecast hour +c imax num points is i-direction of input grid +c jmax num points is j-direction of input grid +c +c OUTPUT: +c iotmret return code from this subroutine + + implicit none +c + integer ifh,imax,jmax,iotmret,kf,igoret,iix,jjx,ipret + integer ifcsthour + integer kpds(200),kgds(200) + logical(1) masked_outc(imax,jmax),lb(imax,jmax) + real xmask(imax,jmax) +c + if (ifh == 1) then + call baopenw (77,"fort.77",igoret) + print *,'baopenw: igoret= ',igoret + + if (igoret /= 0) then + print *,' ' + print *,'!!! ERROR in sub output_tracker_mask opening' + print *,'!!! **OUTPUT** grib files. baopenw return codes:' + print *,'!!! grib file 1 return code = igoret = ',igoret + STOP 95 + return + endif + endif + + xmask = 0.0 + do jjx = 1,jmax + do iix = 1,imax + if (masked_outc(iix,jjx)) then + xmask(iix,jjx) = 1.0 + else + xmask(iix,jjx) = 0.0 + endif + enddo + enddo + + kf = imax * jmax + +c kpds(5) = 7 +c kpds(6) = 100 +c kpds(7) = 850 +c kpds(22) = 0 + + kpds(1) = 7 ; kpds(2) = 80 + kpds(3) = 255 ; kpds(4) = 192 + kpds(5) = 7 ; kpds(6) = 100 + kpds(7) = 850 ; kpds(8) = 99 + kpds(9) = 7 ; kpds(10) = 20 + kpds(11) = 12 ; kpds(12) = 0 + kpds(13) = 1 ; kpds(14) = ifcsthour + kpds(15) = 0 ; kpds(16) = 10 + kpds(17) = 0 ; kpds(18) = 1 + kpds(19) = 2 ; kpds(20) = 0 + kpds(21) = 20 ; kpds(22) = 0 + kpds(23) = 0 ; kpds(24) = 0 + kpds(25) = 0 + kgds(1) = 0 ; kgds(2) = imax + kgds(3) = jmax ; kgds(4) = -90000 + kgds(5) = 0 ; kgds(6) = 128 + kgds(7) = 90000 ; kgds(8) = 359750 + kgds(9) = 250 ; kgds(10) = 250 + kgds(11) = 64 ; kgds(12) = 0 + kgds(13) = 0 ; kgds(14) = 0 + kgds(15) = 0 ; kgds(16) = 0 + kgds(17) = 0 ; kgds(18) = 0 + kgds(19) = 0 ; kgds(20) = 255 + + write(*,980) kpds(1),kpds(2) + write(*,981) kpds(3),kpds(4) + write(*,982) kpds(5),kpds(6) + write(*,983) kpds(7),kpds(8) + write(*,984) kpds(9),kpds(10) + write(*,985) kpds(11),kpds(12) + write(*,986) kpds(13),kpds(14) + write(*,987) kpds(15),kpds(16) + write(*,988) kpds(17),kpds(18) + write(*,989) kpds(19),kpds(20) + write(*,990) kpds(21),kpds(22) + write(*,991) kpds(23),kpds(24) + write(*,992) kpds(25) + write(*,880) kgds(1),kgds(2) + write(*,881) kgds(3),kgds(4) + write(*,882) kgds(5),kgds(6) + write(*,883) kgds(7),kgds(8) + write(*,884) kgds(9),kgds(10) + write(*,885) kgds(11),kgds(12) + write(*,886) kgds(13),kgds(14) + write(*,887) kgds(15),kgds(16) + write(*,888) kgds(17),kgds(18) + write(*,889) kgds(19),kgds(20) + write(*,890) kgds(21),kgds(22) +c + 980 format('tmow kpds(1) = ',i7,' kpds(2) = ',i7) + 981 format('tmow kpds(3) = ',i7,' kpds(4) = ',i7) + 982 format('tmow kpds(5) = ',i7,' kpds(6) = ',i7) + 983 format('tmow kpds(7) = ',i7,' kpds(8) = ',i7) + 984 format('tmow kpds(9) = ',i7,' kpds(10) = ',i7) + 985 format('tmow kpds(11) = ',i7,' kpds(12) = ',i7) + 986 format('tmow kpds(13) = ',i7,' kpds(14) = ',i7) + 987 format('tmow kpds(15) = ',i7,' kpds(16) = ',i7) + 988 format('tmow kpds(17) = ',i7,' kpds(18) = ',i7) + 989 format('tmow kpds(19) = ',i7,' kpds(20) = ',i7) + 990 format('tmow kpds(21) = ',i7,' kpds(22) = ',i7) + 991 format('tmow kpds(23) = ',i7,' kpds(24) = ',i7) + 992 format('tmow kpds(25) = ',i7) + 880 format('tmow kgds(1) = ',i7,' kgds(2) = ',i7) + 881 format('tmow kgds(3) = ',i7,' kgds(4) = ',i7) + 882 format('tmow kgds(5) = ',i7,' kgds(6) = ',i7) + 883 format('tmow kgds(7) = ',i7,' kgds(8) = ',i7) + 884 format('tmow kgds(9) = ',i7,' kgds(10) = ',i7) + 885 format('tmow kgds(11) = ',i7,' kgds(12) = ',i7) + 886 format('tmow kgds(13) = ',i7,' kgds(14) = ',i7) + 887 format('tmow kgds(15) = ',i7,' kgds(16) = ',i7) + 888 format('tmow kgds(17) = ',i7,' kgds(18) = ',i7) + 889 format('tmow kgds(19) = ',i7,' kgds(20) = ',i7) + 890 format('tmow kgds(20) = ',i7,' kgds(22) = ',i7) +c + print *,'just before call to putgb, kf= ',kf + call putgb (77,kf,kpds,kgds,lb,xmask,ipret) + print *,'just after call to putgb, kf= ',kf + if (ipret == 0) then + print *,' ' + print *,'+++ IPRET = 0 after call to putgb' + print *,' ' + else + print *,' ' + print *,'!!!!!! ERROR: IPRET NE 0 AFTER CALL TO PUTGB !!!' + print *,' ' + endif +c +c bug fix for IBM: flush the output stream so it actually writes + flush(6) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_next_ges (fixlon,fixlat,ist,ifh,imax,jmax + & ,dx,dy,modelid,valid_pt,readflag,maxstorm,istmspd + & ,istmdir,ctype,trkrinfo,ignret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. It does this by using two different +c methods and averaging the results from those two. The +c first method is a simple linear extrapolation made by +c basically drawing a line from the previous position +c through the current fix position and assuming straight +c line motion. The second method is to do a barnes +c smoothing of u & v in the vicinity of the storm at 850, +c 700 & 500 mb to get an average environmental wind +c vector at each level, and then move the storm according +c to the vector at each level. Then a weighted average is +c taken of all these positions from methods 1 & 2 to get +c the consensus for the guess position. NOTE: For a +c regional model and a storm that is relatively close to +c the model boundary, there is a strong possibility that +c the barnes analysis subroutine will fail due to trying +c to access grid points beyond the model's lateral +c boundary. In this case, the redlm & ridlm are halved +c and barnes is called again. If it still fails, then +c just use the result from method 1 as a default. +c +c INPUT: +c fixlon Array with longitudes of fix positions +c fixlat Array with latitudes of fix positions +c ist Storm number currently being processed +c ifh Forecast hour currently being processed +c imax Max number of pts in x-direction for this grid +c jmax Max number of pts in y-direction for this grid +c dx grid-spacing of the model in the i-direction +c dy grid-spacing of the model in the j-direction +c modelid Integer indicating what model's data is being processed +c valid_pt Logical; bitmap indicating if valid data at that pt. +c readflag Logical; Tells whether or not a variable was read in +c for this model +c maxstorm Max # of storms that can be handled in this run +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, eventually +c in the barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c istmspd The speed that the storm would have to move to get from +c the current position to the next guess position +c istmdir The direction in which the storm would have to move to +c get from the current position to the next guess position +c +c LOCAL: +c dt Number of seconds between successive forecast times +c for this particular model. +c dtkm Distance in meters of 1 degree latitude +c icutmax Max number of times to cut the ridlm and redlm in half, +c for use in calling barnes. If you're using a regional +c model and on the first call to barnes you try to access +c a point that's outside the model grid boundary, we'll +c call barnes again, but not before cutting the redlm and +c ridlm in half. icutmax says how many times to allow +c this cutting in half before giving up and just going +c with the extrapolation method. At first writing, we'll +c set icutmax to 2, so that it will allow the ridlm to +c get down to 500 km (originally 2000 km) and the redlm +c to 125 km (originally 500 km). +c *** NOTE: After testing the system, it was found that if +c we cut these radii, the u and v values that are +c calculated from barnes are too strongly influenced by +c the near-storm environment and, especially for asymmetric +c systems, resulted in u and v values being much too strong. +c As such, we will not allow these values to be cut, and if +c we hit the boundaries in barnes, we'll just use the +c extrapolation method, which has seemed to work just fine. +c +c OTHER: (slonfg, slatfg & storm defined in module def_vitals) +c slonfg Array containing first guess longitude positions +c slatfg Array containing first guess latitude positions +c storm Contains tcvitals information +c + USE radii; USE def_vitals; USE set_max_parms; USE grid_bounds + USE tracked_parms; USE level_parms; USE trig_vals; USE trkrparms + USE gen_vitals + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer icutmax,istmspd,istmdir,bskip,ileadtime,ifcsthour + integer ifh,ist,npts,ilonfix,jlatfix,ibeg,jbeg,iend,jend + integer igiret,ignret,icut,iuret,ivret,ibarnct,n,ix1,ix2 + integer icount,imax,jmax,modelid,maxstorm + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real barneswt,extrapwt,dtkm,dt,ucomp,vcomp,xdist,ydist,ydeg + real extraplat,avglat,cosfac,xdeg,extraplon,ylatdegmove_last + real xlondegmove_last,xnumh_last,ylatdegmove_last_perhour + real xlondegmove_last_perhour,xnumh_next,yoldavglat + real yoldcosfac,xdistmove_last,xdistmove_last_perhour + real ynewavglat,ynewcosfac,xdegnew,dx,dy,re,ri,ubar,vbar + real wgttot,uavg,vavg,reold,riold,barnlat,barnlon,wt_total + real tmp_fix_lon_curr,tmp_fix_lon_prev + character*1 :: in_grid, extrap_flag, barnes_flag + character(*) ctype +c logical(1) valid_pt(imax,jmax),readflag(14) +cPENG----2018-06-07 ------------------------ + logical(1) valid_pt(imax,jmax),readflag(19) +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c For updating the first guess, if Method 1 and Method 2 are both +c able to be done, give the following weights to the 2 methods. +c + data barneswt /0.50/, extrapwt /0.50/ +c +c ------------------------------- +c METHOD 1: LINEAR EXTRAPOLATION +c ------------------------------- +c First, just do a simple linear extrapolation from the previous +c fix position through the current fix position. If it's the +c first time (vt=0), then use the storm motion vector and storm +c speed information from the TC Vitals card. +c + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(ist)%tcv_stdir == -99 .or. + & storm(ist)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GET_NEXT_GES, at fcst hour = 0, either ' + print *,'!!! storm motion or storm speed = -99 on TCV card.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(ist)%tcv_stdir + print *,'!!! storm motion speed= ',storm(ist)%tcv_stspd + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + extrap_flag = 'n' + else + ucomp = sin(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + vcomp = cos(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(ist,ifh) + ydeg + avglat = 0.5 * (extraplat + fixlat(ist,ifh)) + if (avglat > 89.5) avglat = 89.0 + if (avglat < -89.5) avglat = -89.0 + cosfac = cos(avglat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(ist,ifh) + xdeg + endif + else + +c Do a simple linear extrapolation of the current motion of the +c storm. Follow a line from the fix position from the last fix +c through the current fix and extrapolate out. To figure out the +c new latitude, just see how many deg lat the storm moved since +c last time and add it to the current fix latitude. To calculate +c the new fix longitude, though, we need to see how many deg lon +c the storm moved since the last time, convert that to the +c distance (km) the storm travelled in the x-direction (at an +c average latitude between the current and previous latitudes), +c and then add that distance on to the current longitude and +c convert that distance to the num of degrees the storm has +c travelled in the x-direction (at an average latitude between +c the current and next(extrap) latitudes). +c +c UPDATE Feb 2009: To account for the possibility of using +c irregularly spaced forecast hours (e.g., 6,10,10.5,...etc), +c I had to modify this linear extrapolation. + + print *,' ' + print *,'xxxx get_next_ges, prev fix lon= ',fixlon(ist,ifh-1) + print *,'xxxx get_next_ges, curr fix lon= ',fixlon(ist,ifh) + print *,' ' + + if (fixlat(ist,ifh-1) > -900.0 .and. + & fixlon(ist,ifh-1) > -900.0) then + + ylatdegmove_last = fixlat(ist,ifh) - fixlat(ist,ifh-1) + + tmp_fix_lon_curr = fixlon(ist,ifh) + tmp_fix_lon_prev = fixlon(ist,ifh-1) + + if (tmp_fix_lon_prev < 0.0 .and. tmp_fix_lon_prev > -25.0) + & then + ! previous lon position is within 25 deg west of the GM + ! and is listed in negative degrees. + if (tmp_fix_lon_curr < 0.0 .and. tmp_fix_lon_curr > -25.0) + & then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 1 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both negative. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr < 25.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 2 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous time' + print *,' is negative, while lon for current time' + print *,' is positive, but within 0-25 deg East.' + print *,' All ok!' + endif + endif + + elseif (tmp_fix_lon_prev > 335.0 .and. + & tmp_fix_lon_prev <= 360.0) then + ! previous lon position is within 25 deg west of the GM + ! and is listed in positive degrees. + if (tmp_fix_lon_curr > 335.0 .and. + & tmp_fix_lon_curr <= 360.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 3 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both positive. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr <= 25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 4 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between 0 & 25. Current tmp_lon' + print *,' has been adjusted to be > 360 for the' + print *,' purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < 0.0 .and. + & tmp_fix_lon_curr >= -25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 5 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is west of the GM and' + print *,' is between 0 & -25. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < -335.0 .and. + & tmp_fix_lon_curr >= -360.0) then + tmp_fix_lon_curr = 720.0 - tmp_fix_lon_curr + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 6 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between -335 & -360. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + + endif + + endif + + xlondegmove_last = tmp_fix_lon_curr - tmp_fix_lon_prev + + xnumh_last = fhreal(ifh) - fhreal(ifh-1) + + ylatdegmove_last_perhour = ylatdegmove_last / xnumh_last + xlondegmove_last_perhour = xlondegmove_last / xnumh_last + + xnumh_next = fhreal(ifh+1) - fhreal(ifh) + + extraplat = fixlat(ist,ifh) + & + (ylatdegmove_last_perhour * xnumh_next) + + yoldavglat = 0.5 * (fixlat(ist,ifh) + fixlat(ist,ifh-1)) + yoldcosfac = cos (dtr * yoldavglat) + xdistmove_last = xlondegmove_last * dtk * yoldcosfac + + xdistmove_last_perhour = xdistmove_last / xnumh_last + + ynewavglat = 0.5 * (extraplat + fixlat(ist,ifh)) + ynewcosfac = cos(dtr * ynewavglat) + xdegnew = (xdistmove_last_perhour * xnumh_next) + & / (dtk * ynewcosfac) + extraplon = tmp_fix_lon_curr + xdegnew + + else + + if ( verb .ge. 3 ) then + print *,' ' + write(6,92) '!!! IN GET_NEXT_GES, at fcst hour = ' + & ,ifhours(ifh),ifclockmins(ifh) + print *,'!!! the lon and lat positions for the previous' + print *,'!!! forecast hour are -999, meaning that this is a' + print *,'!!! new storm, so we cannot use the extrap method.' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + 92 format (1x,a36,i4,':',i2.2) + + extrap_flag = 'n' + + endif + + endif + +c ------------------------------- +c METHOD 2: Barnes analysis +c ------------------------------- +c Do a barnes analysis on the u & v components of the wind near the +c storm to get an average u & v, then advect the storm according to +c the average wind vector obtained. The call to get_ij_bounds is +c needed in order to restrict the number of grid points that are +c searched in the barnes subroutine. See Abstract from this +c subroutine for further details. + + npts = ceiling(ridlm/(dtk*((dx+dy)/2))) + + call get_ij_bounds (npts,0,ridlm,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for ' + print *,'!!! storm number ',ist + endif + + ignret = 92 + return + endif + + if (verb >= 3) then + print *,' ' + print *,' +++ In get_next_ges after call to get_ij_bounds,' + print *,' getting bounds for the barnes analysis...' + print *,' glatmax= ',glatmax,' glatmin= ',glatmin + print *,' glonmax= ',glonmax,' glonmin= ',glonmin + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + print *,' ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + endif + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if ((dx+dy)/2 > 0.20) then + bskip = 1 + else if ((dx+dy)/2 > 0.10 .and. (dx+dy)/2 <= 0.20) then + bskip = 2 + else if ((dx+dy)/2 > 0.05 .and. (dx+dy)/2 <= 0.10) then + bskip = 3 + else if ((dx+dy)/2 > 0.03 .and. (dx+dy)/2 <= 0.05) then + bskip = 5 + else if ((dx+dy)/2 <= 0.03) then + bskip = 10 + endif + +c Calculate average wind at each level (currently: 850, 700 & 500) + + re = redlm + ri = ridlm + icut = 0 + + if (trkrinfo%type == 'midlat') then + icutmax = 2 + else + icutmax = 1 + endif + + radmaxloop: do while (icut <= icutmax .and. in_grid == 'n') + + ubar = 0.0; vbar = 0.0 + iuret = 0; ivret = 0 + wgttot = 0.0 + ibarnct = 0 + barnes_flag = 'n' + + levelloop: do n=1,nlevg + + select case (n) + case (1); ix1=3; ix2=4 ! For 850 mb readflags + case (2); ix1=5; ix2=6 ! For 700 mb readflags + case (3); ix1=12; ix2=13 ! For 500 mb readflags + end select + + if (readflag(ix1) .and. readflag(ix2)) then + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,u(1,1,n),valid_pt + & ,bskip,re,ri,uavg,icount,ctype,trkrinfo,iuret) + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,v(1,1,n),valid_pt + & ,bskip,re,ri,vavg,icount,ctype,trkrinfo,ivret) + + if (iuret /= 0 .or. ivret /= 0) then + +c ...barnes probably tried to access a pt outside the grid +c domain. So, reduce by half the distance from the center +c of the farthest pt that barnes tries to access, exit this +c loop, and try it again with the smaller re and ri. + + iuret = 96; ivret = 96 + reold = re + riold = ri + re = 0.5 * re + ri = 0.5 * ri + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: While attempting to use the barnes ' + print *,'method to update the first guess, the ' + print *,'algorithm tried to access a grid point that ' + print *,'does not have valid data, meaning that too ' + print *,'large a radius is being searched. So, the 2 ' + print *,'radii, re and ri, are being halved and, if the' + print *,'value of icutmax > 0, the algorithm will be ' + print *,'run again. Otherwise, if icutmax = 0, only ' + print *,'the extrapolation method will be used.' + print *,'iuret= ',iuret,' ivret= ',ivret,' icut= ',icut + print *,'Old re = ',reold,' New re = ',re + print *,'Old ri = ',riold,' New ri = ',ri + endif + + exit levelloop + + else + ubar = ubar + wgts(n) * uavg + vbar = vbar + wgts(n) * vavg + wgttot = wgttot + wgts(n) + ibarnct = ibarnct + 1 + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, ix1= ',ix1,' ix2= ',ix2 + print *,' uavg= ',uavg,' vavg= ',vavg + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' n= ',n,' wgts(n)= ',wgts(n),' wgttot= ' + & ,wgttot + print *,' ibarnct= ',ibarnct + print *,' ' + print *,' ' + endif + endif + + endif + + enddo levelloop + + if (ibarnct > 0 .and. wgttot > 0.0) then + barnes_flag = 'y' + in_grid = 'y' + ubar = ubar / wgttot + vbar = vbar / wgttot + barnlat = fixlat(ist,ifh) + (vbar * dt)/dtkm + cosfac = cos (dtr * 0.5 * (fixlat(ist,ifh) + barnlat)) + barnlon = fixlon(ist,ifh) + (ubar * dt)/(dtkm * cosfac) + + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, mean stats follow: ' + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' wgttot= ',wgttot + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' barnlon= ',barnlon,' barnlat= ',barnlat + print *,' dt= ',dt,' dtkm= ',dtkm,' cosfac= ',cosfac + endif + + +c This next if statement says that if we've had to reduce the +c size of the barnes analysis domain twice already, then we've +c only done the analysis on a much smaller area, and this +c doesn't give us as good a picture of the average winds in the +c area of the storm, so reduce the emphasis we place on the +c barnes method. + + if (icut >= 2) barneswt = barneswt / 2. + + else + barnes_flag = 'n' + endif + + icut = icut + 1 + + enddo radmaxloop + +c --------------------- +c Average the results +c --------------------- +c Now do a weighted average of the positions obtained from the +c linear extrapolation and the barnes analysis methods. + + if (extrap_flag == 'y' .and. barnes_flag == 'y') then + wt_total = barneswt + extrapwt + slatfg(ist,ifh+1) = (barneswt * barnlat + extrapwt * extraplat) + & / wt_total + + ! Note that in any of these statements just below, in order for + ! any of these to be > 360, the original fixlon must be close + ! to 360, i.e., in the far eastern part of the grid, as opposed + ! to being in the far western part (e.g., 0-2 deg East or so). + ! Conversely, for any of these to be < 0, the original fixlon + ! must be close to 0, i.e., in the far *western* part of the + ! grid. + +c yyyy + + if (fixlon(ist,ifh) > 330.0) then + + ! In this part of the IF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being 330+, to be consistent with the fixlon for + ! this time. + + if (extraplon > 330. .and. barnlon > 330.) then + + continue ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (extraplon > 330. .and. + & (barnlon >= 0.0 .and. barnlon < 30.)) then + + ! extraplon > 330, but barnlon is in the 0-30 range, so + ! we need to convert the barnlon value to be 360+ + + barnlon = barnlon + 360. + + elseif (extraplon > 330. .and. barnlon < 0.) then + + ! extraplon > 330, but barnlon is < 0, so + ! we need to convert the barnlon value to be positive... + + barnlon = barnlon + 360. + + elseif (barnlon > 330. .and. + & (extraplon >= 0.0 .and. extraplon < 30.)) then + + ! barnlon > 330, but extraplon is in the 0-30 range, so + ! we need to convert the extraplon value to be 360+ + + extraplon = extraplon + 360. + + elseif (barnlon > 330. .and. extraplon < 0.) then + + ! barnlon > 330, but extraplon is < 0, so + ! we need to convert the extraplon value to be positive... + + extraplon = extraplon + 360. + + endif + + elseif (fixlon(ist,ifh) >= 0. and. fixlon(ist,ifh) < 30.0) then + + ! In this part of the ELSEIF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being in the reference of >360 since that is what the + ! code below this is expecting with the computation of + ! slonfg for the next lead time. + + if ((extraplon >= 0. .and. extraplon < 60.) .and. + & (barnlon >= 0. .and. barnlon < 60.)) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon < 0. .and. extraplon > -60.) .and. + & (barnlon < 0. .and. barnlon > -60.)) then + + ! convert extraplon and barnlon to be positive + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon < 0.) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon < 0.) then + + extraplon = extraplon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon > 330.) then + + barnlon = barnlon + 360. + + elseif (barnlon >= 330. .and. extraplon < 60.) then + + extraplon = extraplon + 360. + + elseif (extraplon >= 330. .and. barnlon < 60.) then + + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon > 330.) then + + extraplon = extraplon + 360. + + endif + + else + + continue ! extraplon and barnlon do not need to be modified + ! since there should be no way that a storm + ! currently east of 30E and west of 30W could make + ! it to the Greenwich Mer in one forecast interval + + endif + + print *,' ' + print *,'+++ In get_next_ges, before averaging the 2 methods, ' + print *,' Raw (no conversion for GM wrap) barnlon= ' + & ,barnlon + print *,' Raw (no conversion for GM wrap) extraplon= ' + & ,extraplon + + slonfg(ist,ifh+1) = (barneswt * barnlon + extrapwt * extraplon) + & / wt_total + + if (slonfg(ist,ifh+1) > 360.) then + ! If we've GM-wrapped past 360, adjust it to be 0-360... + slonfg(ist,ifh+1) = slonfg(ist,ifh+1) - 360. + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'y' .and. barnes_flag == 'n') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_next_ges, barnes method was not ' + print *,'!!! done for updating the first guess for this ' + print *,'!!! storm. Only the linear extrapolation method ' + print *,'!!! was used.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + slatfg(ist,ifh+1) = extraplat + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 0.0,0.0,0.0 + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'n' .and. barnes_flag == 'y') then + slatfg(ist,ifh+1) = barnlat + if (barnlon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (barnlon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = barnlon + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 360.-barnlon,barnlat + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges, new position guess not' + print *,'!!! made. Could not get guess using either barnes' + print *,'!!! method or extrapolation method.' + print *,'!!! extrap_flag = ',extrap_flag + print *,'!!! barnes_flag = ',barnes_flag + print *,'!!! Storm number = ',ist,' ifh = ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + write (6,41) 0.0,0.0,0.0 + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 95 + endif + + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| Current fix & updated fix positions |' + print *,'-------------------------------------------------- ' + print *,'| In get_next_ges, current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',ist + print *,'| Return code from get_next_ges = ',ignret + print *,'| Storm Name = ',storm(ist)%tcv_storm_name + print *,'| Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + write (6,21) fixlat(ist,ifh) + write (6,23) 360.-fixlon(ist,ifh),fixlon(ist,ifh) + write (6,25) slatfg(ist,ifh+1) + write (6,27) 360.-slonfg(ist,ifh+1),slonfg(ist,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current fix lat is ',f7.2) + 23 format (' | Current fix lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') +c 41 format (' --- barnlon= ',f7.2,'W barnlat= ',f7.2) +c 43 format (' --- extraplon= ',f7.2,'W extraplat= ',f7.2) + + 41 format (' --- barnlon= ',f7.2,'E (',f7.2 + & ,'W) barnlat= ',f7.2) + 43 format (' --- extraplon= ',f7.2,'E (',f7.2 + & ,'W) extraplat= ',f7.2) + +c Now calculate the speed that the storm would have to move at in +c order to make it to the next forecast position. We will use +c this information in writing out the "gen_vitals" record, if this +c is requested. + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh) + & ,slonfg(ist,ifh+1),slatfg(ist,ifh+1),dist,degrees) + + ! convert distance from km to meters, then get speed in m/s. + + distm = dist * 1000. + stmspd = distm / dt + istmspd = int ((stmspd * 10) + 0.5) + + xincr = slonfg(ist,ifh+1) - fixlon(ist,ifh) + yincr = slatfg(ist,ifh+1) - fixlat(ist,ifh) + + if ( verb .ge. 3 ) then + print *,'iocheck, dist= ',dist,' distm= ',distm + print *,'iocheck, stmspd= ',stmspd,' istmspd= ',istmspd + print *,'iocheck, xincr= ',xincr,' yincr= ',yincr + endif + + if (xincr < 0.0 .and. slonfg(ist,ifh+1) < 30.0 .and. + & fixlon(ist,ifh) > 300.0) then + ! This means we have a storm moving east across the GM, and + ! so we are subtracting, for example, something like + ! 0.5 - 359.5, so redo xincr, but add 360 to slonfg first... + xincr = (slonfg(ist,ifh+1) + 360.0) - fixlon(ist,ifh) + else if (xincr > 300.0) then + ! This means we have a storm moving west across the GM, and + ! so we are subtracting, for example, something like + ! 359.5 - 0.5, so redo xincr, but add 360 to fixlon first... + xincr = slonfg(ist,ifh+1) - (fixlon(ist,ifh) + 360.0) + endif + + if (xincr == 0.0) then + if (yincr == 0.0) then + stmdir = 0.0 + else if (yincr > 0) then + stmdir = 360.0 + else if (yincr < 0) then + stmdir = 180.0 + endif + else if (xincr > 0.0) then + if (yincr == 0.0) then + stmdir = 90.0 + else + arct = atan(yincr/xincr) + stmdir = 90. - arct / dtr + endif + else if (xincr < 0.0) then + if (yincr == 0.0) then + stmdir = 270.0 + else + arct = atan(yincr/xincr) + stmdir = 270. - arct / dtr + endif + endif + + istmdir = int (stmdir + 0.5) + if (istmdir > 360) then + istmdir = 360 + else if (istmdir < 0) then + istmdir = 0 + endif + + if ( verb .ge. 3 ) then + print *,'iocheck, stmdir= ',stmdir,' istmdir= ',istmdir + endif + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine advect_tcvitals_from_hour0 (fixlon,fixlat,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. As of 11/2016, it is called only for the case in +c which we've got NetCDF data and no hour0 data, and so we want to +c simply take the TC Vitals data and advect the current position to +c a position at the next lead time. We can't use the code in +c subroutine get_next_ges because there are certain allocatable +c arrays in that subroutine that need to have been allocated first, +c and at this point prior to the first lead time in hour0, they +c haven't been allocated. +c +c INPUT: +c inctcv Index for storm number currently being processed +c ifh Forecast hour currently being processed +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c iatret Return code from this subroutine +c + USE def_vitals; USE trkrparms; USE tracked_parms + USE verbose_output; USE trig_vals; USE set_max_parms + USE gen_vitals + + type (trackstuff) trkrinfo + integer iatret,inctcv + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real ucomp,vcomp,xdist,ydist,ydeg,dt,extraplat + real cosfac + real dtkm +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c ------------------------------------------------------------------ +c Using the storm motion vector and storm translation speed as read +c from the TC Vitals card, do a simple linear extrapolation from the +c current observed (TC Vitals) position and advect the storm to a +c position at the next lead time. +c ------------------------------------------------------------------ + + iatret = 0 + + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(inctcv)%tcv_stdir == -99 .or. + & storm(inctcv)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In advect_tcvitals_from_hour0, at fcst hour= 0' + print *,'!!! either storm motion or storm speed = -99 on ' + print *,'!!! TCV card, ist= inctcv= ',inctcv,' ifh= ',ifh + print *,'!!! Storm name = ',storm(inctcv)%tcv_storm_name + print *,'!!! Storm ID = ',storm(inctcv)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(inctcv)%tcv_stdir + print *,'!!! storm motion speed= ',storm(inctcv)%tcv_stspd + print *,'... CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS ...' + print *,' ' + print *,'... Instead, we will simply use the current ' + print *,'... observed position from TC Vitals and hope that' + print *,'... it is close enough at the next lead time for ' + print *,'... the tracker to be able to still track it.' + print *,' ' + endif + extraplat = slatfg(inctcv,ifh) + extraplon = slonfg(inctcv,ifh) + else + ucomp = sin(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + vcomp = cos(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(inctcv,ifh) + ydeg + cosfac = cos(extraplat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(inctcv,ifh) + xdeg + endif + else + print *,' ' + print *,'!!! ERROR: In advect_tcvitals_from_hour0, the value of' + print *,' ifh is > 1, and this routine should only be called' + print *,' if ifh=1 (i.e., for hour0). STOPPING....' + print *,' ' + stop 95 + endif + + slatfg(inctcv,ifh+1) = extraplat + + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon >360 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon < 0 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + else + slonfg(inctcv,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| In advect_tcvitals_from_hour0, info on the ' + print *,'| positions for the current and next lead times ' + print *,'| follow: ' + print *,'-------------------------------------------------- ' + print *,'| current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',inctcv + print *,'| Return code from advect_tcvitals_from_hour0= ',iatret + print *,'| Storm Name = ',storm(inctcv)%tcv_storm_name + print *,'| Storm ID = ',storm(inctcv)%tcv_storm_id + write (6,420) gstorm(inctcv)%gv_gen_date + & ,gstorm(inctcv)%gv_gen_fhr + & ,gstorm(inctcv)%gv_gen_lat + & ,gstorm(inctcv)%gv_gen_latns,gstorm(inctcv)%gv_gen_lon + & ,gstorm(inctcv)%gv_gen_lonew,gstorm(inctcv)%gv_gen_type + write (6,21) fixlat(inctcv,ifh) + write (6,23) 360.-fixlon(inctcv,ifh),fixlon(inctcv,ifh) + write (6,25) slatfg(inctcv,ifh+1) + write (6,27) 360.-slonfg(inctcv,ifh+1),slonfg(inctcv,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current TC Vitals lat is ',f7.2) + 23 format (' | Current TC Vitals lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') + + + return + end +c +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getradii (xcenlon,xcenlat,imax,jmax,dx,dy,valid_pt + & ,cstormid,ifcsthr,vmaxwind,vradius,trkrinfo + & ,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) +c +c ABSTRACT: This subroutine looks through the wind data near an +c input storm center (fixlon,fixlat) and gets the radii of various +c surface winds in each of the 4 storm quadrants (NE,NW,SE,SW). +c The wind thresholds that are sought are gale force (34kt|17.5m/s), +c storm force (50kt|25.7m/s), and hurricane force (64kt|32.9m/s). +c This subroutine calls the Cray subroutine orders, which is a +c Cray-optimized sort routine. +c +c UPDATE (AUG 2001): The Cray subroutine orders was ported to the +c SP by NCEP personnel. On the SP version, some changes were +c apparently made so that the size of the arrays for calling +c arguments 2, 3 and 4 (iwork, dtemp and isortix in my calling +c routine) must be the same. This was not the case on the Crays, +c and this was causing the tracker to crash for cases far north +c on fine grids (GFDL 1/3 grid). +c +c UPDATE (AUG 2012): The call to the Cray subroutine orders was +c replaced with a call to qsort, which uses a quicksort sorting +c algorithm. While this is not the fastest sorting routine out +c there, we don't do a lot of sorting here, and qsort is simple +c and it is portable. +c +c UPDATE (April 2013): For the radii, we encountered a problem with +c radmax being too small. It was set at 650 km. Hurricane Sandy +c exceeded this in the models, so the values returned from getradii +c were close to the default radmax value of 650 km (350 nm), instead +c of much higher as they should have been. To fix it, we now use an +c iterative technique, where we start with radmax as a small value +c (450 km). If getradii returns a value for R34 in a quadrant that +c does not exceed 0.97*radmax, then that value is ok. If it does +c exceed 0.97*radmax, then we bump up radmax by 50 km and call +c getradii again, looking to diagnose radii only in those quadrants +c where the need_to_expand_r34 flag = 'n'. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c cstormid 3-character storm ATCF ID (e.g., 03L, 11E, etc) +c ifcsthr integer value for current forecast hour +c trkrinfo derived type containing various info on the storm +c need_to_expand_r34 1-character array that specifies which of the +c 4 quadrants still need to be expanded on this time +c through getradii in order to get an R34 value that is +c not right at the outermost boundary. +c vmaxwind max wind (in m/s) that was reported from the +c get_max_wind subroutine +c radmax input max radius (km) that will be used for this +c iteration of getradii. +c first_time_thru_getradii logical flag. It is used so that any +c checking for 50- or 64-kt radii is only done on the +c first time through getradii. Only the checking for +c 34-kt radii is done on multiple iterations. +c igrct integer that indicates what iteration of getradii this +c call is. +c +c OUTPUT: +c +c igrret return code from this subroutine +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c +c LOCAL: +c +c radmax the maximum radius to look for winds for the various +c thresholds. +c quadinfo This array contains the magnitude of the near-surface +c winds and the distance from the gridpoint to the fix +c position for each point in each quadrant that is within +c the maximum allowed radius, radmax. quadinfo is +c allocated within this subroutine, and is allocated as +c (quadrant, num_pts_in_quadrant, data_type), where +c data_type is either windspeed(1) or distance(2) from +c storm center to grid point. +c quadmax This array contains the max surface wind in each +c quadrant, plus the location of it and the distance from +c the storm center. This information is critical to +c identifying when this subroutine is malfunctioning. + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE level_parms + USE trkrparms + USE verbose_output + +c + type (trackstuff) trkrinfo +c + logical(1) valid_pt(imax,jmax) + logical(1) first_time_thru_getradii +c dimension iwork(257) + real, allocatable :: quadinfo(:,:,:),iwork(:) + real quadmax(4,4) + real exactdistnm,exactdistkm,radmax,degrees,cosarg + real rlonb,rlonc,rlatb,rlatc,vmaxwind + real pt_heading_rad,pt_heading,d + integer, allocatable :: isortix(:) + integer iwindix,ipoint,ifcsthr,igrct + integer quadct(4),vradius(3,4) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: dtemp(:) + real :: windthresh(3) = (/17.5,25.7,32.9/) + character cstormid*3 + character :: need_to_expand_r34(4)*1 + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *************************************************** ' + print *,' AT BEGINNING OF GETRADII, input radmax= ',radmax + print *,' *************************************************** ' + print *,' ' + print *,'xcenlon= ',xcenlon,' xcenlat= ',xcenlat + print *,'imax= ',imax,' jmax= ',jmax,' dx= ',dx,' dy= ',dy + endif + + igrret = 0 + +c ----------------------------------------------------------- +c PART 1: Define the maximum radius for which you'll search +c for the wind values, and then get the beginning and ending +c i and j points for that sub-region to search. Define this +c maximum radius (radmax) in terms of km. +c ----------------------------------------------------------- + +c radmax = 650.0 ! This value is in units of km. With April 2013 +c ! update, this is now defined in calling routine + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-A....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine getradii' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-B....' + stop 98 + endif + + igrret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmax is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmax/(dtk*dx))/cosfac) + numjpts = ceiling(radmax/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (ibeg < 1) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-C...' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jbeg < 1) jbeg = 1 + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in getradii calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Wind radii will not be calculated for this time.' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-D....' + stop 98 + endif + + igrret = 99 + return + endif + + if (iend > imax) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-E....' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getradii, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + +c ----------------------------------------------------------- +c PART 2: Within the area of grid points defined by jbeg, +c jend, ibeg and iend, (1) calculate all the wind speeds at +c each grid point, (2) calculate all of the distances from +c each grid point to the storm center, (3) assign each grid +c point to one of the 4 quadrants (NE,NW,SE,SW), (4) in each +c quadrant, sort the points, based on windspeed. +c ----------------------------------------------------------- + + jnum = jend - jbeg + 1 + inum = iend - ibeg + 1 +c numalloc = ((jnum * inum) / 2) + inum/2 + jnum/2 + numalloc = jnum * inum + inum/2 + jnum/2 + + if ( verb .ge. 3 ) then + print *,'in getradii, numalloc= ',numalloc,' radmax= ',radmax + endif + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + allocate (quadinfo(4,numalloc,2),stat=iqa) + + if (iqa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub getradii allocating quadinfo array.' + print *,'!!! iqa = ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-F....' + stop 98 + endif + + igrret = 94 + return + endif + + quadct = 0 + +c Calculate the distances and wind speeds at each grid point. If +c the distance is < radmax, include that wind info in the +c appropriate quadinfo array location for that quadrant. + + quadmax = 0.0 + + jloop: do j=jbeg,jend + iloop: do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point in question = ',i + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-G...' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub getradii' + print *,'!!! for a non-global grid. i= ',i + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-H....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + if (dist > radmax) cycle iloop + + if (valid_pt(ip,j)) then + + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + +cc print *,'i= ',i,' j= ',j,' dist= ',dist,' vmag= ',vmag + + ! Calculate the angle from the center point to this point + ! and then assign this point to the appropriate quadrant bin + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-xcenlon) * dtr + rlatb = xcenlat * dtr + d = degrees * dtr + +c write (6,59) 360.-xcenlon,xcenlat,360.-glon(ip),glat +c +c write (6,61) d/dtr,rlatc/dtr,360.-(rlonc/dtr),rlatb/dtr +c & ,360.-(rlonb/dtr),sin(rlatc),sin(rlatb),cos(d) +c & ,sin(d),cos(rlatb) +c +c +c 59 format (1x,'+++ gr, xcenlon= ',f8.3,'W xcenlat= ' +c & ,f8.3,' glon= ',f8.3,'W glat= ',f8.3) +c +c 61 format (1x,'+++ gr, d rlatc rlonc rlatb rlonb= ',5f9.4 +c & ,' sin(rlatc)= ',f8.6,' sin(rlatb)= ',f8.6 +c & ,' cos(d)= ',f8.6,' sin(d)= ',f8.6 +c & ,' cos(rlatb)= ',f8.6) + + if (d == 0.0) then + + pt_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d)) / + & (sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_heading_rad = acos(cosarg) + else + pt_heading_rad = 2*pi - acos(cosarg) + endif + + pt_heading = pt_heading_rad / dtr + + endif + + if (pt_heading >= 0.0 .and. pt_heading < 90.) then + ! NE quadrant + iq = 1 + else if (pt_heading >= 90.0 .and. pt_heading < 180.) then + ! SE quadrant + iq = 2 + else if (pt_heading >= 180.0 .and. pt_heading < 270.) then + ! SW quadrant + iq = 3 + else if (pt_heading >= 270.0 .and. pt_heading <= 360.) then + ! NW quadrant + iq = 4 + endif + +c write (6,73) xcenlat,360.-xcenlon,j,i,ip,glat(j) +c & ,360.-glon(ip),pt_heading,iq + + 73 format (1x,'+++ getradii clat clon: ',f6.2,' ',f7.2,'W',3i4 + & ,' plat plon: ',f6.2,' ',f7.2,'W Dir: ',f7.2 + & ,' Quad: ',i2) + + quadct(iq) = quadct(iq) + 1 + quadinfo(iq,quadct(iq),1) = vmag + quadinfo(iq,quadct(iq),2) = dist + if (vmag > quadmax(iq,4)) then + quadmax(iq,1) = glon(ip) + quadmax(iq,2) = glat(j) + quadmax(iq,3) = dist + quadmax(iq,4) = vmag + endif + + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After loop, quadct(1)= ',quadct(1),' quadct(2)= ' + & ,quadct(2) + print *,' quadct(3)= ',quadct(3),' quadct(4)= ' + & ,quadct(4) + print *,' ' + + write (6,110) cstormid,ifcsthr,'NE',quadmax(1,1),quadmax(1,2) + & ,quadmax(1,3)*0.539638,quadmax(1,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SE',quadmax(2,1),quadmax(2,2) + & ,quadmax(2,3)*0.539638,quadmax(2,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SW',quadmax(3,1),quadmax(3,2) + & ,quadmax(3,3)*0.539638,quadmax(3,4)*1.9427 + write (6,110) cstormid,ifcsthr,'NW',quadmax(4,1),quadmax(4,2) + & ,quadmax(4,3)*0.539638,quadmax(4,4)*1.9427 + print *,' ' + + 110 format (' quadmax: ',a3,1x,i3.3,1x,a2,1x,' lon: ',f6.2,'E',1x + & ,' lat: ',f6.2,' radius: ',f7.2,' nm',2x,' vmag: ' + & ,f6.2,' kts') + endif + +c Now go through each quadrant and put the wind speed distance info +c into a temporary array (dtemp), sort that array, and then scan +c through that array to find the various thresholds. + + quadrantloop: do k=1,4 + + if (need_to_expand_r34(k) == 'y') then + print *,'---> R34 search underway for quadrant ',k + & ,' radmax= ',radmax + continue + else + print *,'+ R34 okay for quadrant ',k,'... skipping...' + cycle quadrantloop + endif + + if (allocated(isortix)) deallocate (isortix) + if (allocated(dtemp)) deallocate (dtemp) + if (allocated(iwork)) deallocate (iwork) + allocate (isortix(quadct(k)),stat=iisa) + allocate (dtemp(quadct(k)),stat=idta) + allocate (iwork(quadct(k)),stat=iwa) + if (iisa /= 0 .or. idta /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii allocating isortix, dtemp' + print *,'!!! or iwork array for quadrant= ',k + print *,'!!! iisa = ',iisa,' idta= ',idta,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-I....' + stop 98 + endif + + itret = 94 + return + endif + +c ------------------- + + do m=1,quadct(k) + dtemp(m) = quadinfo(k,m,2) + enddo + + imode = 2 + isortix = 0 + + call qsort (dtemp,isortix,quadct(k)) + +ccccc call orders (imode,iwork,dtemp,isortix,quadct(k),1,8,1) +cccc call orders_4byte (imode,iwork,dtemp,isortix +cccc & ,quadct(k),1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' +c ************************************************************** +c--- mf 20100609 +c CAUSE OF SEG FAULT!!!!!!!! -- not sure still an issue if dtemp +c properly allocated +c + !print *,' dtemp(isortix(1)) = ',dtemp(isortix(1)) + print *,' dtemp(isortix(quadct(k)))= ' + & ,dtemp(isortix(quadct(k))) + print *,' isortix(1) = ',isortix(1) + print *,' isortix(quadct(k)) = ',isortix(quadct(k)) + endif + +c ! Uncomment these next lines to see a listing in the output of +c ! all wind values & distances in this quadrant less than radmax +c do iqq = 1,quadct(k) +c print *,' iqq= ',iqq,' vmag= ',quadinfo(k,isortix(iqq),1) +c & ,' dist= ',quadinfo(k,isortix(iqq),2) +c enddo + +c ------------------- + + if (quadct(k) < 2) then ! not enough members in array + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GETRADII, NOT ENOUGH MEMBERS IN ARRAY FOR' + print *,'!!! QUADRANT #',k,' .... # members = quadct(k)= ' + & ,quadct(k) + print *,'!!! SETTING ALL VRADII = 0 for quadrant = ',k + endif + + vradius(1,k) = 0 + vradius(2,k) = 0 + vradius(3,k) = 0 + cycle quadrantloop + endif + +c Within this quadrant, go through the sorted array of wind +c magnitudes and compare those wind values against the set +c wind thresholds to get the wind radii. The array has +c been sorted by distance from the storm center in order of +c closest (ipoint=1) to farthest (ipoint=quadct(k)). We +c analyze these wind values by starting at the farthest +c point and moving inward until we hit a point that has a +c wind value of at least 34-knot winds (17.5 m/s). When +c we find that point, we interpolate between that point and +c the next farthest out point to get the distance that would +c be for the exact 17.5 m/s value. We then continue searching +c through the wind values down closer to the storm center to +c see if we can find values for the 50- and 64-knot winds. + + iwindix = 1 + ipoint = quadct(k) + 1 + +c print *,'drp: quad= ',k,' quadct= ',quadct(k) + + threshloop: do while (iwindix <= 3 .and. ipoint > 1) + + if (iwindix > 1) then + if (first_time_thru_getradii) then + + ! We are only doing the wind radii for 50 and 64 kts on + ! the first time through subroutine getradii (we only + ! need to do the multiple call iterations for 34 kts). + ! + ! Make sure vmax for this lead time exceeds the radii + ! threshold being diagnosed. The check below avoids, + ! for example, reporting 50-kt wind radii when the max + ! wind diagnosed was only 44 kts. This can happen since + ! the radius for searching for radii is larger than the + ! radius for searching for the max wind. + if (vmaxwind >= windthresh(iwindix)) then + if (verb >= 3) then +c print *,' ' +c print *,' +++ vmaxwind of ',vmaxwind,' m/s exceeds' +c print *,' +++ threshold of ',windthresh(iwindix) +c print *,' +++ (m/s), so radii checking will continue' +c print *,' +++ for this threshold.' +c print *,' +++ igrct= ',igrct,' ipoint= ',ipoint +c & ,' iwindix= ',iwindix + continue + endif + continue + else + if (verb >= 3) then + print *,' ' + print *,' --- vmaxwind of ',vmaxwind,' m/s does NOT' + print *,' - - exceed threshold of ' + & ,windthresh(iwindix) + print *,' - - (m/s), so radii checking will NOT be ' + print *,' - - performed for this threshold.' + endif + iwindix = iwindix + 1 + cycle threshloop + endif + else + iwindix = iwindix + 1 + cycle threshloop + endif + endif + + ipoint = ipoint - 1 + + if (quadinfo(k,isortix(ipoint),1) < windthresh(iwindix)) then + cycle threshloop + else + if (ipoint == quadct(k)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In getradii, a max wind radius was' + print *,'!!! found at the maximum radius checked, so ' + print *,'!!! you may want to make sure that you are' + print *,'!!! checking at a far enough distance from ' + print *,'!!! the fix position, that is, you may want to' + print *,'!!! increase the value of radmax in subroutine' + print *,'!!! getradii. Currently, radmax (km) = ',radmax + print *,'!!! iwindix = ',iwindix,' quadrant= ',k + endif + + vradius(iwindix,k) = int( ((quadinfo(k,isortix(ipoint),2) + & * 0.5396) / 5.0) + 0.5) * 5 + else + +c Interpolate between the 2 closest distances to each wind +c threshold to get "exact" distance to that wind threshold +c radius, convert from km to nm, and then round to the +c nearest 5 nm (since TPC uses this precision). +c 7/23/98 UPDATE: Jim Gross has asked that values not be +c rounded to the nearest 5 nm, but rather only to the +c nearest 1 nm. + + exactdistkm = quadinfo(k,isortix(ipoint),2) + + & ( (quadinfo(k,isortix(ipoint),1) - windthresh(iwindix)) / + & (quadinfo(k,isortix(ipoint),1) - + & quadinfo(k,isortix(ipoint+1),1)) * + & ( (quadinfo(k,isortix(ipoint+1),2) - + & quadinfo(k,isortix(ipoint),2)) ) ) + + exactdistnm = exactdistkm * 0.5396 ! Convert km to nm + vradius(iwindix,k) = int(exactdistnm + 0.5) + +cc vradius(iwindix,k) = int( (exactdistnm / 5.0) + 0.5) * 5 + + + if ( verb .ge. 3 ) then + print *,'iwindix= ',iwindix,' exactdistnm = ' + & ,exactdistnm + print *,'vradius(iwindix,k) =',vradius(iwindix,k) + endif + + endif + +c The possibility exists, especially for coarse output +c grids, that there could be a jump over more than 1 wind- +c thresh category when going from 1 grid point to the next, so +c we need to account for this. For example, if 1 point has +c vmag = 15 m/s and the next point closer in has vmag = 28 +c m/s, then between those 2 points you have the thresholds +c for gale force AND storm force winds, so to be safe, we +c actually need to add 1 to ipoint and re-check the current +c point, if the wind value at that point is found to be +c greater than a wind threshold value (which it has if you've +c gotten to this point in threshloop). + + ipoint = ipoint + 1 + + iwindix = iwindix + 1 + + endif + + enddo threshloop + + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (idta /= 0 .or. iisa /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating isortix or' + print *,'!!! dtemp or work for quadrant= ',k + print *,'!!! idta= ',idta,' iisa= ',iisa,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-J....' + stop 98 + endif + + itret = 94 + return + endif + + enddo quadrantloop + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating quadinfo array.' + print *,'!!! iqa= ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-K....' + stop 98 + endif + + itret = 94 + return + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_max_wind (xcenlon,xcenlat,imax,jmax,dx,dy + & ,valid_pt,levsfc,vmax,trkrinfo,rmax,igmwret) +c +c ABSTRACT: This subroutine looks for the maximum near-surface wind +c near the storm center. This subroutine is only concerned with the +c value of the max wind, NOT where it's located radially with +c respect to the center. The value that's returned in vmax is the +c max wind speed in m/s, which are the units the data are stored in. +c However, when the max wind values are output in output_atcf, they +c will be converted from m/s to knots. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c levsfc integer holding the value of the array member that holds +c the near-surface winds in the u and v arrays (at orig +c writing, it's = 4). +c +c OUTPUT: +c +c vmax value of maximum near-surface wind near the storm ctr +c rmax radius of max winds +c igmwret return code from this subroutine +c +c LOCAL: +c +c radmaxwind the maximum radius to look for a max wind near the +c storm center. You have to allow this to be bigger for +c model grids with coarse resolution (ECMWF 2.5 degree). + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real radmaxwind,degrees,dx,dy,rmax + logical(1) valid_pt(imax,jmax) +c + igmwret = 0 + rmax = -99.0 + + if ((dx+dy)/2. <= 1.25) then + if ((dx+dy)/2. <= 0.25) then + radmaxwind = 300.0 + else + radmaxwind = 300.0 + endif + else + radmaxwind = 500.0 + endif + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmaxwind is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmaxwind/(dtk*dx))/cosfac) + numjpts = ceiling(radmaxwind/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_max_wind calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Value of vmax will be set to 0 for this time.' + endif + + vmax = 0.0 + igmwret = 99 + return + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + + print *,' ' + print *,'In get_max_wind, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + + vmax = 0.0 + do j=jbeg,jend + do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point = ',i + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + + if (dist > radmaxwind) cycle + + if (valid_pt(ip,j)) then + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + if (vmag > vmax) then + vmax = vmag + rmax = dist * 0.539638 ! convert from km to nm + endif + endif + + enddo + enddo + + if ( verb .ge. 3 ) then + print *,'At end of get_max_wind, vmax= ',vmax,' rmax= ',rmax + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine fixcenter (clon,clat,ist,ifh,calcparm,geslon,geslat + & ,inp,stderr,fixlon,fixlat,xvalues,maxstorm,ifret) +c +c ABSTRACT: This subroutine loops through the different parameters +c for the input storm number (ist) and calculates the +c center position of the storm by taking an average of +c the center positions obtained for those parameters. +c First we check to see which parameters are within a +c max error range (errmax), and we discard those that are +c not within that range. Of the remaining parms, we get +c a mean position, and then we re-calculate the position +c by giving more weight to those estimates that are closer +c to this mean first-guess position estimate. +c +c INPUT: +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c geslon Initial guess longitude for this storm at this fcst hour +c geslat Initial guess latitude for this storm at this fcst hour +c inp contains the input date and model number information +c xvalues The actual max or min data values for each parameter +c maxstorm max # of storms to be handled in this run +c +c INPUT/OUTPUT: +c stderr Standard deviation of the position "error" of the parms +c relative to the guess storm position. As long as the +c distance of a parm center to the guess center is <= +c errpmax, it is included in the std dev calculation. +c +c OUTPUT: +c fixlon Best approximation of storm center's longitude +c fixlat Best approximation of storm center's latitude +c ifret Return code from this subroutine +c +c LOCAL: +c storm Contains tcvitals info for the storms (def_vitals) +c trkerr_avg Sum/avg of the track errors for all parms for this +c fcst hour, regardless of whether or not the error was +c > errmax. It's used for getting the std deviation of +c the position error for this forecast time, to be used +c as part of the errmax calculation for the next fcst +c time. +c iclose Number of parameters whose position estimates are +c found to be within a distance errmax of the guess pos +c wtpos The weight given to each position estimate. It's +c based on the distance from the average position. +c errdist The "error" of the parameter center position relative +c to the storm's guess position. +c avgerr Average "error" of the parameter center positions +c relative to the storm's guess position. +c use4next Logical; If a parm center has been calculated but its +c distance from the guess position is > errmax, we don't +c use this center in calculating the new guess position, +c however we will use this position in calculating the +c standard deviation of the current time's guess +c positions, to be used in calculating the new errmax +c for the next forecast time. So in this subroutine, +c calcparm may be set to FALSE if errdist > errmax, but +c use4next will not be set to FALSE (Actually, it is +c only set to FALSE if errdist > errpmax, which is +c defined in error_parms and is roughly 600km). +c stderr_close Standard deviation of position errors for parms that +c have center estimates that are within a distance +c errmax of the guess position. +c clon_fguess These are the first-guess mean position estimates, +c clat_fguess which are the means of the position estimates that +c are within a distance errmax. These first-guess mean +c positions will be refined by giving more weight to +c individual parameter estimates that are closer to +c this first-guess mean position. +c dist_from_mean Contains the "error" distance of each parameter +c from the first-guess mean position (clon_fguess, +c clat_fguess). NOTE: If a parameter is not within +c a distance errmax of the guess position for this +c time (geslon,geslat), then there will be NO +c dist_from_mean calculated for that parm. +c + USE error_parms; USE set_max_parms; USE inparms; USE def_vitals + USE atcf; USE gen_vitals; USE tracked_parms + USE verbose_output + + type (datecard) inp + + real clon(maxstorm,maxtime,maxtp),temp_clon(maxtp) + real clat(maxstorm,maxtime,maxtp),temp_clat(maxtp) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real trkerr(maxtp),errdist(maxtp),xvalues(maxtp) + real stderr(maxstorm,maxtime),devia(maxtp),wtpos(maxtp) + real dist_from_mean(maxtp) + real degrees,errtmp + integer gt345_ct,lt15_ct + logical(1) calcparm(maxtp,maxstorm),use4next(maxtp) + character charparm(maxtp)*8,charmaxmin(maxtp)*8 +c + data charparm /'zeta 850','zeta 700','circ 850','NOT USED' + & ,'circ 700','NOT USED',' gph 850',' gph 700',' MSLP' + & ,'circ sfc','zeta sfc',' thk 5-8',' thk 2-5',' thk 2-8'/ + data charmaxmin /' Max ',' Max ',' Min ','NOT USED' + & ,' Min ','NOT USED',' Min ',' Min ',' Min ' + & ,' Min ',' Max ',' Max ',' Max ',' Max '/ +c + ifret=0 +c +c We need to judge whether each parameter position is reasonable, +c so we'll check to make sure that the dist from each parameter's +c estimate to the guess position is less than a maximum allowable +c error. If it's the first forecast time, use the initial error max +c (defined as errinit in error_parms) as errmax. Otherwise, the +c max error criterion is that the distance error must not exceed 3 +c times the previous forecast time's standard deviation (after a +c small growth factor has been applied). +c UPDATE 3/5/98: During testing, it was found that just using the +c previous time's stdev made errmax too "jumpy" (i.e., at vt=48h, +c errmax could = 380, and then at vt=54h, errmax could jump down +c to 190, so we've changed it so that it uses an average of the +c stdev's from the 3 previous forecast times to maintain some +c continuity between successive forecast times). +c + if (ifh == 1) then + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR' ) then + errmax = err_gfs_init + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errmax = err_ecm_max + errinit = err_ecm_max + else + errmax = err_reg_init + errinit = err_reg_init + endif + else + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR') then + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errinit = err_ecm_max + else + errinit = err_reg_max + endif + + if (ifh >= 4) then + xavg_stderr = (stderr(ist,ifh-3) + stderr(ist,ifh-2) + & + stderr(ist,ifh-1)) / 3.0 + else if (ifh == 3) then + xavg_stderr = (stderr(ist,ifh-2) + stderr(ist,ifh-1)) / 2.0 + else if (ifh == 2) then + xavg_stderr = stderr(ist,ifh-1) + endif + +c The following errmax statement was replaced by the ensuing 4 +c lines due to a compiler bug on some other platforms: +c errmax = amin1(amax1(3.0*xavg_stderr*errpgro,errinit) +c & ,errpmax) + + errtmp = 3.0*xavg_stderr*errpgro + errmax = max(errtmp,errinit) + errtmp = errpmax + errmax = min(errmax,errtmp) + + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (ifh > 1) then + print '(a42,f8.2,a15,f8.2)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = ' + & ,stderr(ist,ifh-1),' xavg_stderr= ',xavg_stderr + else + print '(a45,a18)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = N/A' + & ,' xavg_stderr= N/A' + endif + print *,'At beg of fixcenter, errpgro = ',errpgro + print *,'At beg of fixcenter, errinit = ',errinit + print *,'At beg of fixcenter, errpmax = ',errpmax + print *,'At beg of fixcenter, ifh= ',ifh,' errmax= ',errmax + endif + + trkerr_avg = 0.0 + iclose = 0; itot4next = 0 + clonsum = 0.0; clatsum = 0.0 + errdist = 0.0 + use4next = .FALSE. + gt345_ct = 0 + lt15_ct = 0 + +c For each parm, check to see if the estimated center is within +c distance errmax of the guess center. If it's within errmax, +c then use that parm for locating the center. If it's NOT +c within errmax, but IS within errpmax, then we still use this +c in calculating the standard deviation of the parameters for +c helping to determine the errmax for the next forecast hour. + +c OLD NOTE: For calculating the std dev to be used for the next +c OLD forecast hour, do NOT use vmag 850, vmag 700 or vmag sfc, since +c OLD those parms are always guaranteed to be within a short range of +c OLD the guess, due to the nature of the algorithm (see subroutine +c OLD get_uv_center for further details on that). + + do ip=1,maxtp + + if (ip == 4 .or. ip == 6) then ! Parms 4 & 6 not defined. + calcparm(ip,ist) = .FALSE. + cycle + endif + if (calcparm(ip,ist)) then + call calcdist (geslon,geslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + errdist(ip) = dist + if (dist <= errpmax) then + use4next(ip) = .TRUE. + trkerr_avg = trkerr_avg + dist + itot4next = itot4next + 1 + endif + if (dist <= errmax) then + iclose = iclose + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 + endif + clonsum = clonsum + clon(ist,ifh,ip) + clatsum = clatsum + clat(ist,ifh,ip) + else + calcparm(ip,ist) = .FALSE. + endif + endif + + enddo + + if (iclose > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (clonsum) + clon_fguess = (clonsum + (360.*float(lt15_ct)))/ iclose + else + clon_fguess = clonsum / float(iclose) + endif + if (clon_fguess >= 360.0) then + clon_fguess = clon_fguess - 360. + endif + clat_fguess = clatsum / float(iclose) + endif + +c Print out a table listing of the locations of the fixes for +c the individual parameters. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'--------------------------------------------------' + write (6,95) 'Individual fixes follow..., fhr= ',ifhours(ifh) + & ,ifclockmins(ifh),' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + write (6,97) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,'Model name = ',atcfname + print *,'Values of -99.99 indicate that a fix was unable to be' + print *,'made for that paramater. Parameters 4 & 6 are not' + print *,'used. Vorticity data values are scaled by 1e5.' + print *,'Circulation data values are scaled by 1e-6.' + print *,'errdist is the distance that the position estimate is' + print *,'from the guess position for this time. MSLP value ' + print *,'here may differ from that in the atcfunix file since ' + print *,'the one here is that derived from the area-averaged ' + print *,'barnes analysis, while that in the atcfunix file is ' + print *,'from a specific gridpoint.' + write (6,21) geslon,360.-geslon,geslat + write (6,*) ' ' + write (6,23) + write (6,25) + endif + + if (geslat > 0.0) then + charmaxmin(1) = ' Max ' + charmaxmin(2) = ' Max ' + charmaxmin(3) = ' Max ' + charmaxmin(5) = ' Max ' + charmaxmin(10) = ' Max ' + charmaxmin(11) = ' Max ' + else + charmaxmin(1) = ' Min ' + charmaxmin(2) = ' Min ' + charmaxmin(3) = ' Min ' + charmaxmin(5) = ' Min ' + charmaxmin(10) = ' Min ' + charmaxmin(11) = ' Min ' + endif + + do ip=1,maxtp + if (ip == 1 .or. ip == 2 .or. ip == 11) then + ! This IF block allows vorticity values to be + ! written out and scaled up by 1e5 ... + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + elseif (ip == 3 .or. ip == 5 .or. ip == 10) then + ! This IF block allows circulation values to be + ! written out and scaled down by 1e-6 ... + + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + else + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + endif + enddo + + 21 format (' Guess location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 23 format (' parm# parm Max/Min Lon_fix(E) Lon_fix(W)' + & ,' Lat_fix Max/Min_value calcparm errdist(km)') + 25 format (' ----- ---- ------- ---------- ----------' + & ,' ------- ------------- -------- ----------') + 27 format (2x,i2,4x,a8,2x,a8,3x,f7.2,5x,f7.2,4x,f7.2,7x,f9.2 + & ,6x,L2,7x,f7.2) + 95 format (1x,a33,1x,i4,':',i2.2,a2,a4,a1,a9) + 97 format (' Gen ID (if available): ',i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3) + + +c If number of parameter centers close enough (iclose) > 0, then +c calculate the center by taking an average of all the parameter +c center positions that are within distance errmax from the guess +c position (geslon,geslat). Get a first-guess mean position, and +c then re-calculate the position estimate by giving more weight +c to those positions that are closer to the first-guess mean +c position. + + dist_from_mean = 0.0 + + if (iclose > 0) then + +c Get distances from first-guess mean position.... + + do ip=1,maxtp + if (calcparm(ip,ist)) then + call calcdist (clon_fguess,clat_fguess,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + dist_from_mean(ip) = dist + endif + enddo + +c Get the mean distance of each parameter estimate from +c the first-guess mean position + + call avgcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,iaret) + + if (iaret == 0) then + + call stdevcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,stderr_close,isret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After stdevcalc, xmn_dist_from_mean= ' + & ,xmn_dist_from_mean,' stderr_close= ' + & ,stderr_close,' isret= ',isret + endif + + endif + if (iaret /= 0 .or. isret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER -- Error occurred in either' + print *,'!!! avgcalc or stdevcalc. Storm number = ',ist + print *,'!!! RCC from avgcalc = ',iaret + print *,'!!! RCC from stdevcalc = ',isret + print *,'!!! Center fix will NOT be made, and processing' + print *,'!!! for this storm is ending. The probable cause' + print *,'!!! is that no calcparms were valid for this storm' + print *,'!!! at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + if (calcparm(1,ist) .or. calcparm(2,ist) .or. calcparm(7,ist) + & .or. calcparm(8,ist) .or. calcparm(9,ist) + & .or. calcparm(11,ist) .or. calcparm(3,ist) + & .or. calcparm(10,ist) .or. calcparm(5,ist) + & .or. calcparm(12,ist) .or. calcparm(13,ist) + & .or. calcparm(14,ist)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In fixcenter, STOPPING PROCESSING for this' + print *,'!!! storm. The reason is that none of the fix' + print *,'!!! locations for parms z850, z700, zeta 850,' + print *,'!!! zeta 700, MSLP, wcirc_850, wcirc_700, ' + print *,'!!! wcirc_sfc, sfc zeta or the various levels ' + print *,'!!! of thicknesses were within a ' + print *,'!!! reasonable distance of the guess location.' + print *,'!!! ist= ',ist,' ifh= ',ifh + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'!!! Forecast hour: ',i4,':',i2.2) + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now re-calculate the mean position by giving more weight +c to those position estimates that are closer to the first +c guess mean position. Note that if stderr_close < 5.0, we +c force it to be 5.0; we do this to avoid getting very +c large numbers for devia values, which could make the +c weights (wtpos) equal to 0. This occurred during testing +c when only 2 parameters were valid, and so, of course, the +c standard deviation from the mean of those 2 parameters +c was close to 0, which gave devia values around 6000, and +c then wtpos values of 0, leading to a divide by 0 crash +c later on in subroutine wtavrg. + + kprm=0 + + if (stderr_close > 0.0) then + if (stderr_close < 5.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Since stderr_close had a value less than' + print *,'5, stderr_close has been forced to be equal' + print *,'to 5 in order to avoid dividing by zero later' + print *,'on in subroutine wtavrg.' + endif + + stderr_close = 5.0 + endif + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + devia(kprm) = dist_from_mean(ip) / stderr_close + wtpos(kprm) = exp(-devia(kprm)/3.) + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + + if ( verb .ge. 3 ) then + write (6,113) ip,kprm,dist_from_mean(ip),devia(kprm) + & ,wtpos(kprm),temp_clon(kprm) + & ,360.-temp_clon(kprm),temp_clat(kprm) + endif + + endif + enddo + 113 format (1x,'ip= ',i2,' kprm= ',i2,' dist_from_mean= ',f7.3 + & ,' devia= ',f7.3,' wtpos= ',f8.5,2x,3(2x,f7.2)) + else +c +c This next if statement is for the case in which only 1 +c parameter is valid, for which the stderr_close will = 0 +c (obviously), but as long as we have 1 valid parameter, +c continue processing, and set the weight for that parm = 1. +c The else portion is for the case in which stderr_close +c = 0 with NO parms being close. +c + if (iclose == 1) then + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + wtpos(kprm) = 1 + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + endif + enddo + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, stderr_close not > 0' + print *,'!!! stderr_close = ',stderr_close + print *,'!!! The probable cause is that no calcparms were' + print *,'!!! valid for this storm at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + endif +c + if (kprm > 0) then + call wtavrg_lon (temp_clon,wtpos,kprm,fixlon(ist,ifh),iwtret1) + call wtavrg (temp_clat,wtpos,kprm,fixlat(ist,ifh),iwtret2) + if (iwtret1 == 0 .and. iwtret2 == 0) then + if (verb .ge. 3) then + print *,' ' + write (6,173) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + 173 format ('At end of fixcenter: ',a4,' fhr= ',i4,':',i2.2 + & ,' Fix position= ',f7.2,'E (',f6.2,'W)',2x,f7.2) + print *,' ' + endif + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER in call to wtavrg.' + print *,'!!! Return Codes from wtavrg calls follow: ' + print *,'!!! RCC from wtavrg for long fix: ',iwtret1 + print *,'!!! RCC from wtavrg for lat fix: ',iwtret2 + print *,'!!! This means a divide by zero would have ' + print *,'!!! been attempted, which means that the ' + print *,'!!! weights in wtpos are not > 0. Check in' + print *,'!!! subroutine fixcenter where devia values' + print *,'!!! are calculated to see if something is ' + print *,'!!! wrong there. Values of wtpos array follow:' + print *,'!!! ',wtpos + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + print *,' ' + endif + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, kprm NOT > 0' + print *,'!!! This means that, for whatever reason, the ' + print *,'!!! calcparm logical flag was set to .FALSE. for' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: IN FIXCENTER, No storms are within errmax ' + print *,'!!! OR the calcparm logical flag was set to .FALSE. ' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now calculate the average error of all the parms that are within +c a radius errpmax (defined in error_parms, ~600km), and the std +c dev of those errors. This standard deviation will be used in +c calculating the maximum allowable error for the next forecast +c time. + + if (itot4next > 0 .and. ifret /= 95) then + trkerr_avg = trkerr_avg / float(itot4next) + call stdevcalc (errdist,maxtp,use4next,trkerr_avg + & ,stderr(ist,ifh),isret) + if (isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in FIXCENTER calculating std deviation ' + print *,'!!! for use in next forecast hours errmax.' + print *,'!!! ist= ',ist,' ifh= ',ifh,' itot4next= ' + & ,itot4next + endif + + ifret = 95 + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine avgcalc (xdat,kmax,valid,xavg,iaret) +c +c ABSTRACT: This subroutine just calculates a straight average of +c the parameters in the input array (xdat). The logical array +c (valid) indicates whether or not to include a particular array +c member or not in the calculation. + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) +c + iaret = 0 +c + xsum = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + xsum = xsum + xdat(i) + ict = ict + 1 + endif + enddo +c + if (ict > 0) then + xavg = xsum / float(ict) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in avgcalc, ict NOT > 0' + endif + + xavg = xdat(1) + iaret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg (xdat,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xdat) using the input weights +c in the input array (wt). It is used to calculate the center lat +c and lon fix positions. +c + USE verbose_output + + real xdat(kmax),wt(kmax) +c + iwtret = 0 +c + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xdat(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg, wtot NOT > 0' + endif + + iwtret = 95 + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg_lon (xlon,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xlon) using the input weights +c in the input array (wt). This subroutine is specifically used +c to find the center lon fix positions. It contains code to +c account for wrapping around the Greenwich Meridian. +c + + USE verbose_output + + real xlon(kmax),wt(kmax) + integer gt345_ct,lt15_ct +c + iwtret = 0 + gt345_ct = 0 + lt15_ct = 0 + +c First check to see if we have lons that are both to the left +c and the right of the greenwich meridian + + do i = 1,kmax + if (xlon(i) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (xlon(i) < 15.) then + lt15_ct = lt15_ct + 1 + endif + enddo + + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some lons that are in the 300's (west of the GM), and + ! some that are in the 0's (east of the GM). We need to + ! standardize these if we want to get a meaningful average. + do i = 1,kmax + if (xlon(i) < 15.) then + xlon(i) = xlon(i) + 360.0 + endif + enddo + endif + + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xlon(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg_lon, wtot NOT > 0' + endif + + iwtret = 95 + endif + + if (xwtavg >= 360.0) then + xwtavg = xwtavg - 360.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine stdevcalc (xdat,kmax,valid,xavg,stdx,isret) + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) + + isret = 0 + + stdx = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + stdx = stdx + (xdat(i) - xavg)**2 + ict = ict + 1 + endif + enddo + + if (ict > 0) then + stdx = sqrt(stdx/float(ict)) + if (stdx == 0.0) then +c This can happen if you have just 2 points; The mean position +c will be exactly in the middle of the 2 points and so the +c standard deviation around that mean point will be 0. And +c since the calling routine will quit if the returned standard +c deviation is 0, we must force it to be 1 so the program +c continues running. Theoretically, it could also happen with +c 3 or more points, but the likelihood of the distances working +c out to exactly equidistant for 3 points is not that good. + stdx = 1.0 + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in stdevcalc, ict NOT > 0' + endif + + isret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,fxval,trkrinfo + & ,cmodel_type,maxmin,igwcret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the wind circulation near the storm center. This center fix is +c done differently than for the other parms. With this fix, +c we limit the area that is searched. This subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the +c original guess position for this lead time and the 5 other parm +c fixes that have already been made for this lead time. That +c modified guess position is passed into this subroutine as uvgeslon +c and uvgeslat, and that's where the searching for the wind +c circulation is centered. +c +c This subroutine works by converting the winds to Vt and Vr at +c each grid point evaluated, relative to each candidate center point +c that is being evaluated at the time in the loop. We then compute +c the circulation at each of 24 azimuths surrounding the storm +c center, where circulation = Vt * (length of a 1/24 arc, in meters) +c This process is repeated for 7 successive radii and the results +c are summed up over all radii, approximating a solid disk +c circulation. The point at which the circulation is maximized +c (NHEM) or minimized (SHEM) is the center of circulation. +c +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c + + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + character(*) cmodel_type,maxmin + integer, parameter :: numdist=7,numazim=24 + integer imax,jmax,ist,level,igwcret,icvpret,idist,iazim + real rdist(numdist),vr(numazim,numdist),vt(numazim,numdist) + real vt_mean(numdist),circul_band(numdist) + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + real rads,ri,uvgeslon,uvgeslat,dx,dy,ctlon,ctlat,fxval + real temp_grid_minlon,temp_guesslon,rlatt,rlont,bear + real targlon,targlat,xintrp_u,xintrp_v,vt_azim_sum,degrees + real circ_diff,circ_diff_sum,hemisphere,wind_mag_ctr,dist + real xmin_circ_diff_mean,xmax_circ_diff_mean,tlon,tlat + real dell,fmax,fmin,grid_buffer,circ_diff_mean + real circumference,arclength + real circul_disk,xmax_circul_disk,xmin_circul_disk + integer ibiret1,ibiret2,igvtret,azimuth_ct,igiret,npts + integer igibret + integer circ_diff_ct,ir,nhalf,bskip1,bskip2,iskip,nlev + integer ilonfix,jlatfix,ibeg,iend,jbeg,jend,i,j,k,iix,jix + logical(1) cflag, valid_pt(imax,jmax) + +c---------------- +c + + print *,' ' + print *,'top of get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' cmodel_type= ',cmodel_type + print *,' maxmin= ',maxmin + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' cflag= ',cflag + print *,' ctlon= ',ctlon,' ctlat= ',ctlat + print *,' fxval= ',fxval + print *,' igwcret= ',igwcret + + igwcret = 0 + + grid_maxlat = glatmax + grid_minlat = glatmin + grid_maxlon = glonmax + grid_minlon = glonmin + + rads = rads_wind_circ + ri = ri_wind_circ + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of get_wind_circulation, rads= ',rads + & ,' ri= ',ri,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+15; fmin = 1.0e+15 + ctlon = 0.0; ctlat = 0.0 + +c Distances checked and the radial intervals are a function of +c the grid resolution.... + + if (dell > 0.50) then + rdist(1) = 50. + rdist(2) = 85. + rdist(3) = 120. + rdist(4) = 155. + rdist(5) = 190. + rdist(6) = 225. + rdist(7) = 260. + else + rdist(1) = 35. + rdist(2) = 65. + rdist(3) = 95. + rdist(4) = 125. + rdist(5) = 155. + rdist(6) = 185. + rdist(7) = 215. + endif + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + print *,' in get_wind_circulation, nlev= ',nlev + + if (uvgeslat >= 0.0) then + hemisphere = 1.0 + else + hemisphere = -1.0 + endif + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend + & ,igibret) + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (uvgeslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = uvgeslon - 360. + else + temp_guesslon = uvgeslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = uvgeslon + endif + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + +c For the wind circulation analysis, we will want to speed things +c up for finer resolution grids. We can do this by skipping some +c of the points in the wind circulation analysis. + + if (dell > 0.20) then + bskip1 = 1 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 3 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 5 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 8 + bskip2 = 3 + else if (dell <= 0.03) then + bskip1 = 10 + bskip2 = 4 + endif + +c bskip1 = 1 +c bskip2 = 1 + + jix = 0 + +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to first loop, ' + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop1: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = uvgeslat + dell*float(j) + + iix = 0 + + iloop1: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop1 + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT from call in ' + print *,'!!! get_wind_circulation: icvpret= ',icvpret + endif + cycle iloop1 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist,degrees) + if (dist .gt. rads) cycle iloop1 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop1: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop1: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + ! These calls to bilin_int_uneven pass a variable "level" + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop1 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop1 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop1 +cc and radiusloop1). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,' ' +cc print *,'1st run, wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'1st run, ir= ',ir,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +c print *,'1st run, circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'1st run, circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'1st run, xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif + + enddo iloop1 + + enddo jloop1 + + if (uvgeslat >= 0.0) then + write (6,61) 360.-ctlon,ctlat,xmax_circul_disk + else + write (6,63) 360.-ctlon,ctlat,xmin_circul_disk + endif + + 61 format (' After first run, Wind Circulation (NHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmax_circul_disk = ',f15.1) + 63 format (' After first run, Wind Circulation (SHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmin_circul_disk = ',f15.1) + +c If nhalf is specified as 0, then don't go through any more +c iterations of this routine, just exit with the value that we +c already got the first time through the loop, above. + + if (dell > 0.50) then + nhalf = 4 + else if (dell > 0.20 .and. dell <= 0.50) then + nhalf = 3 + else if (dell > 0.10 .and. dell <= 0.20) then + nhalf = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + nhalf = 1 + else if (dell <= 0.05) then +c nhalf = 0 + nhalf = 1 +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'In get_wind_circulation, dell is < 0.05 deg, so ' +c print *,'nhalf is set to 0 and only the first iteration of' +c print *,'the search loop is done.' +c print *,' dell= ',dell,' nhalf= ',nhalf +c endif + endif + + if (nhalf < 1) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +c npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only do this once +c for this grid-refinement (even though the grid is redefined +c nhalf times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). Cut the value of +c rads in half (only do this once) so that any points beyond +c rads/2 are not considered as potential centers. + + rads = 0.5 * rads + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igibret) + + if (igibret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_wind_circulation from call to ' + print *,'!!! get_ij_bounds just before nhalf loop. ' + print *,'!!! Stopping processing for storm number ',ist + endif + igwcret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + kloop: do k = 1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: get_wind_circ kloop, k= ',i2,' ' + & ,i2.2,':',i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'get_wind_circ nhalf loop, k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip,' rads= ',rads + endif + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to loop k= ',k + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist + & ,degrees) + if (dist .gt. rads) cycle iloop2 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop2: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop2: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop2 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop2 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop2 +cc and radiusloop2). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,'kloop k= ',k,' wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'kloop k= ',k,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +cc print *,'kloop k= ',k,' circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'kloop k=',k,' circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0.0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'kloop k= ',k,' xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean + + enddo iloop2 + + enddo jloop2 + + if ( verb .ge. 3 ) then + if (uvgeslat >= 0.0) then + print *,'---> xmax_circul_disk= ',xmax_circul_disk + write (6,71) k,360.-ctlon,ctlat,xmax_circul_disk + else + print *,'---> xmin_circul_disk= ',xmin_circul_disk + write (6,73) k,360.-ctlon,ctlat,xmin_circul_disk + endif + endif + + enddo kloop + + 71 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (NHEM: Max) = ' + & ,f15.1) + 73 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (SHEM: Min) = ' + & ,f15.1) + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,xval,trkrinfo,igucret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the minimum in the wind speed near the storm center. This center +c fix is done differently than for the other parms. With this fix, +c we severely limit the area that is searched, because we do not +c want to confuse a wind minimum out on the periphery of a storm +c with the center wind minimum. Therefore, this subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the guess +c position for this time and the 5 other parm fixes. That modified +c guess position is passed into this subroutine as uvgeslon and +c uvgeslat, and that's where the searching for the wind minimum +c is done. To get the wind minimum, the u and v data are first +c interpolated down to a fine grid (see details below for exact +c figures), and then a single-pass barnes analysis is done on that +c fine grid. The reason that we first interpolate the data (which +c is different from how we do the other parms) is that if we just +c use the original grid resolution, we may not be able to +c accurately pick out a minimum in the wind field at the center. +c + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real, allocatable :: uold(:,:),vold(:,:),unew(:,:),vnew(:,:) + real, allocatable :: rlonold(:),rlatold(:),rlonnew(:),rlatnew(:) + real, allocatable :: vmag(:,:) + real :: dx,dy + real :: grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + character*1 :: gotlat + logical(1) cflag, valid_pt(imax,jmax) + logical(1), allocatable :: lbi(:,:) +c + gotlat = 'n' +c +c ----------------------------------------------------------------- +c INTERPOLATE INPUT GRID TO SMALLER GRID +c ----------------------------------------------------------------- +c +c Get beginning and ending j points (on the input grid) for a +c smaller array that surrounds the storm. It is this smaller array +c that we will interpolate to a finer grid. +c +c Calculate number of pts to either side of this j to search +c + npts = ceiling(rads_vmag/(dtk*((dx+dy)/2.))) +c + call get_ij_bounds (npts,0,ritrk_vmag,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij D, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij D, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center from call to ' + print *,'!!! get_ij_bounds, stopping processing for ' + print *,'!!! storm number ',ist + endif + + igucret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, and our gridtype is NOT' + print *,'!!! global, so we are going to redefine ibeg to 1.' + print *,' ' + endif + + ibeg = 1 + endif + endif + + if (jbeg < 1) jbeg = 1 + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center calculating ibeg, iend, jbeg' + print *,'or jend. ibeg= ',ibeg,' iend= ',iend,' jbeg= ',jbeg + print *,'jend= ',jend + print *,'uv center will not be calculated for this time.' + endif + + igrret = 99 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, and our gridtype is' + print *,'!!! NOT global, so we will redefine iend to imax.' + print *,' ' + endif + + iend = imax + endif + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif +c + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the grid sizes for +c some of the typical grids that will be used: +c +c Original grid size # of interps Final grid size +c -------------------- ------------ --------------------- +c 1.00 deg (111.19 km) 3 0.125 deg (13.9 km) +c 1.25 deg (138.99 km) 3 0.156 deg (17.4 km) +c 2.50 deg (277.99 km) 4 0.156 deg (17.4 km) + + if ((dx+dy)/2. > 1.2) then + numinterp = 4 + else if ((dx+dy)/2. > 0.50 .and. (dx+dy)/2. <= 1.2) then + numinterp = 3 + else if ((dx+dy)/2. > 0.25 .and. (dx+dy)/2. <= 0.50) then + numinterp = 2 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.25) then + numinterp = 1 + else if ((dx+dy)/2. <= 0.10) then + numinterp = 0 + endif + + dell = (dx+dy)/2. + imxold = iend - ibeg + 1 + jmxold = jend - jbeg + 1 + +c -------------------------------------------------------------- +c Before interpolating, make sure that all the original +c points have valid data. If they don't then exit the +c subroutine. NOTE: This is NOT checking to see if ALL the pts +c on the complete & full input grid have valid data; it only +c checks those points that are within the box returned from +c get_ij_bounds. + + do i=ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_uv_center, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! PROCESSING WILL STOP. ' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + stop 94 + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_uv_center' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + do j=jbeg,jend + if (.not. valid_pt(ip,j)) goto 975 + enddo + + enddo + +c ------------------------------------ +c Now begin the interpolation process + + allocate (uold(imxold,jmxold),stat=iuo) + allocate (vold(imxold,jmxold),stat=ivo) + allocate (rlonold(imxold),stat=iloo) + allocate (rlatold(jmxold),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. iloo /= 0 .or. ilao /= 0) goto 970 + + do intnum = 1,numinterp + + if (intnum == 1) then + + do i=ibeg,iend + + ik = i + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ik = i + imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i < 1' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i + endif + + igucret = 92 + return + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ik = i - imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i > imax' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i,' imax= ',imax + endif + + igucret = 92 + return + endif + endif + + rlonold(i-ibeg+1) = glon(ik) + do j=jbeg,jend + uold(i-ibeg+1,j-jbeg+1) = u(ik,j,nlev) + vold(i-ibeg+1,j-jbeg+1) = v(ik,j,nlev) + if (gotlat == 'n') then + rlatold(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatold once + enddo + + else + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate (rlatold) + allocate (uold(imxnew,jmxnew),stat=iuo) + allocate (vold(imxnew,jmxnew),stat=ivo) + allocate (rlonold(imxnew),stat=iloo) + allocate (rlatold(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 970 + + gotlat = 'n' + do i=1,imxnew + rlonold(i) = rlonnew(i) + do j=1,jmxnew + uold(i,j) = unew(i,j) + vold(i,j) = vnew(i,j) + if (gotlat == 'n') then + rlatold(j) = rlatnew(j) + endif + enddo + gotlat = 'y' + enddo + + imxold = imxnew + jmxold = jmxnew + deallocate (unew); deallocate (vnew) + deallocate (rlonnew); deallocate (rlatnew) + + endif + + dell = 0.5 * dell + imxnew = 2 * imxold - 1 + jmxnew = 2 * jmxold - 1 + + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + + call bilin_int_even (imxold,jmxold,uold + & ,imxnew,jmxnew,unew,ibiret) + call bilin_int_even (imxold,jmxold,vold + & ,imxnew,jmxnew,vnew,ibiret) +c call lin_int (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int_lon (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int (jmxold,jmxnew,rlatold,rlatnew,iliret) + + chk_lonspc_old = rlonold(imxold) - rlonold(imxold - 1) + chk_latspc_old = rlatold(jmxold) - rlatold(jmxold - 1) + chk_lonspc_new = rlonnew(imxnew) - rlonnew(imxnew - 1) + chk_latspc_new = rlatnew(jmxnew) - rlatnew(jmxnew - 1) + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, intnum= ',intnum + print *,'imxold= ',imxold,' imxnew= ',imxnew + print *,'jmxold= ',jmxold,' jmxnew= ',jmxnew + print *,'Grid boundaries of modified uv grid: ' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ' + & ,grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ' + & ,grid_minlon + endif + + enddo + +c ------------------ + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate(rlatold) + + if (numinterp == 0) then + + ! No interpolations were done for this fine mesh grid, but we + ! need to fill some of these arrays and define variables for + ! subsequent subroutine calls just below here that require + ! the variables imxnew, jmxnew, and the arrays unew and vnew. + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional ' + print *,'grid; iend should not > imax here !!!' + endif + + igucret = 99 + return + endif + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional' + print *,'grid; ibeg should not < 1 here !!!' + endif + + igucret = 99 + return + endif + endif + + imxnew = iend - ibeg + 1 + jmxnew = jend - jbeg + 1 + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + gotlat = 'n' + + do i=ibeg,iend + + ip = i + + if (i > imax) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i - imax ! Wrapping past GM + endif + + if (i < 1) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i + imax ! Wrapping past GM + endif + + rlonnew(i-ibeg+1) = glon(ip) + do j=jbeg,jend + unew(i-ibeg+1,j-jbeg+1) = u(i,j,nlev) + vnew(i-ibeg+1,j-jbeg+1) = v(i,j,nlev) + if (gotlat == 'n') then + rlatnew(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatnew once + enddo + + endif + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,'Grid boundaries of modified uv grid in get_uv_center:' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ',grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ',grid_minlon + endif + + allocate (vmag(imxnew,jmxnew),stat=ivm) + allocate (lbi(imxnew,jmxnew),stat=ilb) + if (ivm /= 0 .or. ilb /= 0) goto 972 + call calc_vmag (unew,vnew,imxnew,jmxnew,vmag,icvret) + deallocate (unew); deallocate (vnew) + + lbi = .TRUE. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to find_maxmin, imxnew= ',imxnew + & ,'jmxnew= ',jmxnew,' ist= ',ist + write (6,171) dell,uvgeslon,360.-uvgeslon,uvgeslat + 171 format (' dell= ',f7.3,' uvgeslon= ',f8.3,'E (',f8.3,'W)' + & ,' uvgeslat= ',f8.3) + endif + +c Note that in the next call, I pass the 'global' argument to +c find_maxmin. This defines what type of grid it is, so that the +c proper grid_buffer can be chosen. This grid_buffer is designed +c to avoid having a center be chosen too close to the grid +c boundary. However, in the case of vmag here, we are only using +c a small subgrid, and we want to make sure we use *all* points +c in that subgrid for searching, and that will occur if we set that +c calling argument to 'global' as opposed to 'regional'. + + call find_maxmin (imxnew,jmxnew,dell,dell,'vmag' + & ,vmag,'min',ist,uvgeslon,uvgeslat,rlonnew,rlatnew,lbi + & ,trkrinfo,cflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,'global',ifmret) + deallocate (vmag); deallocate (lbi) + deallocate (rlonnew); deallocate (rlatnew) +c + if (ifmret == 0) then + goto 995 + else + igucret = ifmret + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center in call to find_maxmin' + print *,'!!! storm num = ',ist,' igucret = ',igucret + endif + + goto 998 + endif +c + 970 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either uold, vold,' + print *,'!!! rlonold or rlatold in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 971 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either unew, vnew,' + print *,'!!! rlonnew or rlatnew in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 972 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either vmag or lbi in ' + print *,'!!! subroutine get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! ivm= ',ivm,' ilb= ',ilb + endif + + igucret = 97 + goto 998 + + 975 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Inside get_uv_center, at least one of the points' + print *,'!!! is not a valid data point. This point may be ' + print *,'!!! outside the valid data bounds of a regional grid' + print *,'!!! i= ',i,' j= ',j + print *,'!!! Storm number = ',ist + endif + + igucret = 98 + goto 998 +c + 995 continue + igucret = 0 +c + 998 continue + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_guess (guesslon,guesslat,clon,clat + & ,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) +c +c ABSTRACT: The purpose of this subroutine is to get a modified +c first guess lat/lon position before searching for the +c minimum in the wind field. The reason for doing this is +c to better refine the guess and avoid picking up a wind +c wind minimum far away from the center. So, use the +c first guess position (and give it strong weighting), and +c then also use the fix positions for the current time +c (give the vorticity centers stronger weighting as well), +c and then take the average of these positions. +c +c INPUT: +c guesslon guess longitude for this forecast time +c guesslat guess latitude for this forecast time +c clon array with center longitude fixes for the various parms +c clat array with center latitude fixes for the various parms +c calcparm logical; tells whether or not a parm has a valid fix +c at this forecast hour +c ist index for current storm +c ifh index for current forecast hour +c maxstorm max # of storms that can be handled +c +c OUTPUT: +c uvgeslon contains modified guess longitude position at which to +c look for the wind minimum +c uvgeslat contains modified guess latitude position at which to +c look for the wind minimum +c igugret return code for this subroutine (0=normal) +c---- +c + USE set_max_parms; USE level_parms; USE error_parms + USE verbose_output + + logical(1) calcparm(maxtp,maxstorm) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real uvgeslon, uvgeslat + real guesslon,guesslat,degrees + integer gt345_ct,lt15_ct + + sumlon = 0.0 + sumlat = 0.0 + ict = 0 + gt345_ct = 0 + lt15_ct = 0 + +c NOTE: We need to be careful in this routine when averaging +c the longitudes together, in case we cross the greenwich +c meridian, because then we may be averaging 345+ lons with +c lons that are less than 15, giving incorrect results. +c Therefore, check for this, and if it occurs, add 360 onto +c any of the <15 lons (add it twice for those lons being +c counted twice (guesslon and the vorticity centers)). + +c Weight the uv guess position by counting the storm's guess +c position twice. + + sumlon = sumlon + 2.*guesslon + sumlat = sumlat + 2.*guesslat + ict = ict + 2 + + if (guesslon > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (guesslon < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct.... + endif + + do ip = 1,maxtp + if ((ip > 2 .and. ip < 7) .or. ip == 10) then + cycle ! because 3-6 are for 850 & 700 u & v and 10 is + ! for surface wind magnitude. + else + if (calcparm(ip,ist)) then + call calcdist (guesslon,guesslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + + if (dist < uverrmax) then +c +c Give the vorticity centers 2x weighting as well +c + if (ip == 1 .or. ip == 2 .or. ip == 11) then + sumlon = sumlon + 2.*clon(ist,ifh,ip) + sumlat = sumlat + 2.*clat(ist,ifh,ip) + ict = ict + 2 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct... + endif + else + sumlon = sumlon + clon(ist,ifh,ip) + sumlat = sumlat + clat(ist,ifh,ip) + ict = ict + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 ! Only 1 for non-zeta parms + endif + endif + + endif + + endif + endif + enddo +c + if (ict > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (sumlon) + uvgeslon = (sumlon + (360.*float(lt15_ct)))/ ict + else + uvgeslon = sumlon / ict + endif + if (uvgeslon >= 360.0) then + uvgeslon = uvgeslon - 360. + endif + uvgeslat = sumlat / ict + igugret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_guess, ict not > 0, ict= ',ict + print *,'!!! vmag center will not be calculated for this' + print *,'!!! storm -- at least not at this level' + print *,'!!! Storm number = ',ist + endif + + igugret = 91 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calc_vmag (xu,xv,imx,jmx,wspeed,icvret) +c +c ABSTRACT: This subroutine calculates the magnitude of the wind +c speed for an array of points, given real u and real v arrays. +c + real xu(imx,jmx),xv(imx,jmx),wspeed(imx,jmx) +c + do i=1,imx + do j=1,jmx + wspeed(i,j) = sqrt( xu(i,j)*xu(i,j) + xv(i,j)*xv(i,j) ) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_even (imxold,jmxold,xold + & ,imxnew,jmxnew,xnew,ibiret) +c +c ABSTRACT: This subroutine does a bilinear interpolation on a +c grid of evenly spaced data. Do NOT attempt to use this subroutine +c with data that are not evenly spaced or you will get unpredictable +c results. +c + real xold(imxold,jmxold), xnew(imxnew,jmxnew) +c +c +c --------------------------------------------------------------------- +c Latitude ----> | +c | +c L O e O e O e O e O | O: original point from input array +c o | +c n e 1 2 1 2 1 2 1 e | 1: interpolated, primary inter. pt +c g | +c i O 2 O 2 O 2 O 2 O | e: interpolated edge point +c t | +c u e 1 2 1 2 1 2 1 e | 2: interpolated, secondary inter. pt +c d | +c e O 2 O 2 O 2 O 2 O | Interpolations are done in the order +c | as indicated above; First, the input +c | e 1 2 1 2 1 2 1 e | 'O' pts are placed onto the new, +c | | larger grid. From that, the '1' pts +c | O 2 O 2 O 2 O 2 O | can be interpolated. Next, the edge +c | | (e) pts are interpolated using an +c v e 1 2 1 2 1 2 1 e | interpolation of two 'O' pts and one +c | '1' pt. Finally, the '2' pts are +c O e O e O e O e O | done using the 2 surrounding '0' and +c | '1' pts. Bilinear interpolation is +c | made incredibly easier by the fact +c | that the grid is evenly spaced. +c --------------------------------------------------------------------- +c NOTE: Remember that the arrays that are read in are indexed as +c (lon,lat), so that in the diagram above, pt (1,1) is at the upper +c left and pt (imax,jmax) is at the lower right, and each column is +c a new latitude and each row is a new longitude. +c +c ----------------------------------------------------------------- +c Put original (O) values from input array into new, expanded array +c ----------------------------------------------------------------- +c + do i=1,imxold + do j=1,jmxold + xnew(2*i-1,2*j-1) = xold(i,j) + enddo + enddo +c +c ---------------------------------------------- +c Interpolate to get primary interior (1) points +c ---------------------------------------------- +c + do i=1,imxold-1 + do j=1,jmxold-1 + xnew(2*i,2*j) = 0.25 * (xnew(2*i-1,2*j-1) + xnew(2*i+1,2*j-1) + & + xnew(2*i+1,2*j+1) + xnew(2*i-1,2*j+1)) + enddo + enddo +c +c --------------------------- +c Interpolate edge (e) points +c --------------------------- +c +c ... Northernmost 'e' points ... +c + j=1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,2)) + enddo +c +c ... Southernmost 'e' points ... +c + j = 2*jmxold - 1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,j-1)) + enddo +c +c ... Westernmost 'e' points ... +c + i=1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(2,2*j)) + enddo +c +c ... Easternmost 'e' points ... +c + i = 2*imxold - 1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(i-1,2*j)) + enddo +c +c ------------------------------------------------ +c Interpolate to get secondary interior (2) points +c ------------------------------------------------ +c + do j=2,2*jmxold-2 + istep = mod(j+1,2) + do i=istep+2,2*imxold-2,2 + xnew(i,j) = 0.25 * (xnew(i-1,j) + xnew(i,j-1) + xnew(i+1,j) + & + xnew(i,j+1)) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points +c + do i=1,ioldmax-1 + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int_lon (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. This particular +c routine is specifically used for interpolating +c longitudes, and it factors in the possibility of +c interpolating across the greenwich meridian. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points, and make the +c necessary adjustment when interpolating a longitude between, +c for example, 359.5 and 0.0. +c + do i=1,ioldmax-1 + if (xnew(2*i-1) > 350. .and. xnew(2*i+1) < 10.) then + xnew(2*i) = 0.5 * (xnew(2*i-1) + (360. + xnew(2*i+1))) + else + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + endif + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) +c +c ABSTRACT: This subroutine finds the maximum and mean zeta values +c at 850 & 700 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms + USE trkrparms; USE level_parms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07 ------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanzeta,dx,dy,re,ri,parmlon,parmlat + integer igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) + integer n,ix1,ix2,ilev,npts,imax,jmax,igzvret,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_zeta_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_zeta_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max zeta values at 850 and 700 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_zeta_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_zeta_loop: do n=1,2 + + gridpoint_maxmin = -99.0 + xmeanzeta = -99.0 + compflag = .true. + + select case (n) + case (1); ilev=850 ! For 850 mb + case (2); ilev=700 ! For 700 mb + end select + + if (zeta(ilonfix,jlatfix,n) > -9990.0) then + + ! ------------------------------------------- + ! We have valid zeta data for this level, so + ! we first call barnes now to get the mean zeta + ! surrounding our found center position. + ! ------------------------------------------- + + if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,n),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanzeta + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds + imeanzeta(n) = int ((xmeanzeta * 1e6) + 0.5) + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_zeta_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for zeta values will not be done.') + exit report_zeta_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + exit report_zeta_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) n,ilev,xmeanzeta,imeanzeta(n) + 621 format (1x,'+++ RPT_MEAN_ZETA: n= ',i2,' lev= ',i4 + & ,' xmeanzeta= ',f9.6,' imeanzeta (*1e6)= ',i8) + write (6,*) ' --- mean zeta raw = ',xmeanzeta + endif + + ! ----------------------------------------------- + ! Call fix_latlon_to_ij to get the nearest actual + ! raw (grid) zeta data value, not the mean value. + ! ----------------------------------------------- + + call fix_latlon_to_ij (imax,jmax,dx,dy + & ,zeta(1,1,n),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanzeta,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + igridzeta(n) = int ((gridpoint_maxmin * 1e6) + 0.5) + else + igridzeta(n) = -99 + endif + + if ( verb .ge. 3 ) then + write (6,623) n,ilev,gridpoint_maxmin,igridzeta(n),ifilret + 623 format (1x,'+++ RPT_GRID_ZETA: n= ',i2,' lev= ',i4 + & ,' grid zeta= ',f9.6,' igrid zeta (*1e6)= ',i8 + & ,' ifilret= ',i3) + write (6,*) ' --- grid zeta raw= ',gridpoint_maxmin + endif + + enddo report_zeta_loop + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get 850 & 700 zeta for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine find_maxmin (imax,jmax,dx,dy,cparm,fxy,maxmin,ist + & ,guesslon,guesslat,rlonv,rlatv,valid_pt,trkrinfo + & ,compflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,cmodel_type,ifmret) +c +c This routine finds the location (clon,clat) of and value of the +c the max or min of fxy in the vicinity of slon,slat. The value of +c the input flag maxmin determines whether to look for a max or a +c min value. The max/min is determined by finding the point which +c gives the max/min value of a single point barnes analysis of fxy +c with e-folding radius re (km) and influence radius ri (km). The +c initial search is restricted to a radius rads around the point +c (slon,slat) on a grid with lon,lat spacing dx and dy. The location +c is refined by reducing the spacing of the search grid by a factor +c of two, nhalf times. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c maxmin Char string indicating whether to search for a max or min +c ist Number of the storm being processed +c guesslon Guess longitude of the storm +c guesslat Guess latitude of the storm +c rlonv Array containing longitude values of input grid points +c rlatv Array containing latitude values of input grid points +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c trkrinfo derived type detailing user-specified grid info +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c +c INPUT/OUTPUT: +c compflag Logical; continue processing this storm or not (would be +c set to FALSE if, for example, the guess position is +c outside the domain of a regional grid) +c +c OUTPUT: +c ctlon Center longitude of storm found for this parameter +c ctlat Center latitude of storm found for this parameter +c xval Max or Min value found at the (ctlon,ctlat) +c ifmret Return code from this subroutine +c +c UPDATE DEC 2009: For the HFIP HRH testing, it was found that +c due to the very limited domain size of some of the models, the +c barnes scheme was allowing points close to the grid boundaries +c to erroneously be selected as the center point. We add in a +c buffer (grid_buffer) here to prevent this from occurring. + + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + character(*) maxmin,cparm,cmodel_type + logical(1) compflag, valid_pt(imax,jmax) + real fxy(imax,jmax),rlonv(imax),rlatv(jmax) + real ctlon,ctlat,degrees,dx,dy,guesslon,guesslat,xval + real rads,re,ri,dell,fmax,fmin,rlatt,rlont,dist,ftemp,ritmp + real vmag_latmax,vmag_latmin,vmag_lonmax,vmag_lonmin,retmp + real tlon,tlat,grid_buffer,temp_grid_minlon,temp_guesslon + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + integer imax,jmax,ist,bskip1,bskip2,iskip,ifmret,npts,maxvgrid + integer ibeg,iend,jbeg,jend,ilonfix,jlatfix,igiret,icount,iret + integer ibct,ibarnes_loopct,i,j,k,iix,jix,jvlatfix,ivlonfix + integer nhalf,icvpret + integer date_time(8) + character (len=10) big_ben(3) +c + ifmret = 0 + nhalf = 5 +c +c ----------------------------------------------------------- +c Set initial parms for use in find_maxmin. +c Different radii used for V magnitude than for other parms, +c see discussion in module radii for more details. +c + if (cparm == 'vmag') then + +c NOTE: The maxvgrid variable determines what size grid to send +c to subroutine barnes. e.g., maxvgrid = 8 means send an +c 8x8 grid; maxvgrid = 12 means send a 12x12 grid. For +c ultra-fine mesh grids (finer than 0.04 deg, or 1/25 deg), +c we expand to 12 in order to sample a few more points +c around each grid point. + + if ((dx+dy)/2. > 0.04) then + maxvgrid = 8 + else + maxvgrid = 12 + endif + + rads = rads_vmag; re = retrk_vmag; ri = ritrk_vmag + re = (float(maxvgrid)/4) * ((dx+dy)/2. * dtk) ! Basically, this +c sets re equal to half the distance from the gridpoint +c in question to the farthest point that will be +c sampled when the (maxvgrid x maxvgrid) grid is passed +c on to subroutine barnes. Thus, just ignore the +c parameter retrk_vmag, and use this instead. + else if ((dx+dy)/2. < 1.26 .and. (dx+dy)/2. >= 0.40) then + rads = rads_most; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.40 .and. (dx+dy)/2. >= 0.10) then + rads = rads_fine; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.10) then + rads = rads_hres; re = retrk_hres; ri = ritrk_most + else + rads = rads_coarse; re = retrk_coarse; ri = ritrk_coarse + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of find_maxmin, rads= ',rads,' re= ',re + & ,' ri= ',ri,' cparm= ',cparm,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+12; fmin = 1.0e+12 + ctlon = 0.0; ctlat = 0.0 + + if (npts == 0) npts = 1 + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if (dell > 0.20) then + bskip1 = 2 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 4 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 6 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 10 + bskip2 = 5 + else if (dell <= 0.03) then + bskip1 = 15 + bskip2 = 5 + endif + + if (cparm == 'vmag') then + bskip1 = 1 + bskip2 = 1 + endif + +c If input parm is vmag, we've already done the minimizing by +c interpolating to the fine mesh grid, so we'll simply send the +c bounds that were input to this subroutine to barnes +c as boundaries for the array to search. For all other parms, +c however, no minimizing has been done yet, so we need to call +c get_ij_bounds to set the boundaries for a much smaller grid that +c surrounds the storm (as opposed to having subroutine barnes +c search the entire global grid). + + if (cparm == 'vmag') then + + if ( verb .ge. 3 ) then + print *,'In find_maxmin, jmax= ',jmax,' imax= ',imax + endif + + ibeg=1; jbeg=1; iend=imax; jend=jmax + vmag_latmax = rlatv(1) ! N-most lat of vmag subgrid + vmag_latmin = rlatv(jmax) ! S-most lat of vmag subgrid + vmag_lonmin = rlonv(1) ! W-most lon of vmag subgrid + vmag_lonmax = rlonv(imax) ! E-most lon of vmag subgrid + + if ( verb .ge. 3 ) then + write (6,44) vmag_latmax,vmag_lonmin,360.-vmag_lonmin + & ,imax,jmax + write (6,46) vmag_latmin,vmag_lonmax,360.-vmag_lonmax + endif + + 44 format (' vmag_latmax= ',f8.3,' vmag_lonmin= ',f8.3 + & ,'E (',f8.3,'W) imax= ',i4,' jmax= ',i4) + 46 format (' vmag_latmin= ',f8.3,' vmag_lonmax= ',f8.3 + & ,'E (',f8.3,'W)') + + if (vmag_lonmin > 330. .and. vmag_lonmax < 30.) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: For a case of find_maxmin, our vmag' + print *,'!!! subgrid is straddling the GM. The code should' + print *,'!!! be able to handle this, but if strange errors' + print *,'!!! are occurring, check into the code either here' + print *,'!!! in find_maxmin or get_uv_ctr.' + print *,' ' + endif + endif + + npts = ceiling(rads/(dtk*dell)) + + else + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,guesslon,guesslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to ' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + ifmret = 92 + return + endif + + endif + +c +c --------------------------------------------------------------- +c + if ( verb .ge. 3 ) then + print *,' ' + write (6,39) guesslon,360.-guesslon,guesslat + 39 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + if (cparm == 'vmag') then + print *,'ilonfix= (unused) jlatfix= (unused)' + & ,' npts= ',npts + print *,'ilonfix and jlatfix are meaningless for computing' + print *,'vmag, so ignore the large values you see for them.' + else + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + endif + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + + if ( verb .ge. 3 ) then + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: find_maxmin 1 ',i2.2,':',i2.2,':',i2.2) + endif + + ibct=0 + ibarnes_loopct = 0 + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (guesslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = guesslon - 360. + else + temp_guesslon = guesslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = guesslon + endif + + jix = 0 + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + jloop: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = guesslat + dell*float(j) + + iix = 0 + +c vlat(jix) = rlatt + + iloop: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c if (cparm == 'vmag') then +c print *,' ' +c print '(a16,i6,a4,i6,2(a8,f8.3),a12,f8.3)' +c & ,'in find_max, i= ',i +c & ,' j= ',j,' rlatt= ',rlatt,' rlont= ',rlont +c & ,' 360-rlont= ',360.-rlont +c endif + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT: icvpret= ',icvpret + endif + + cycle iloop + endif + + call calcdist(rlont,rlatt,temp_guesslon,guesslat,dist,degrees) + if (dist .gt. rads) cycle iloop + + if (cparm == 'vmag') then + +c This next bit of code gets the ij coordinates for an 8x8 +c box around the current point under consideration. These ij +c coordinates are sent to barnes so that barnes only loops +c 64 times, as opposed to nearly 10,000 if the whole 97x97 +c array were sent. So, fix rlatt to the grid point just +c northward of rlatt and fix rlont to the grid point just +c eastward of rlont. Note that this makes for a modified +c barnes analysis in that we're sort of specifying ahead of +c time exactly which grid points will be included and we'll +c be excluding some points that would be near the periphery +c of each (rlont,rlatt)'s range, but as long as we're consis- +c tent and do it this way for each point, it's well worth the +c trade-off in cpu time. Parameter maxvgrid determines what +c size array to send to barnes (maxvgrid=8 means 8x8) + + jvlatfix = int((vmag_latmax - rlatt)/dy + 1.) + ivlonfix = int((rlont - temp_grid_minlon)/dx + 2.) +c ivlonfix = int((rlont - vmag_lonmin)/dx + 2.) + + ibeg = ivlonfix - (maxvgrid/2) + iend = ivlonfix + (maxvgrid/2 - 1) + jbeg = jvlatfix - (maxvgrid/2 - 1) + jend = jvlatfix + (maxvgrid/2) + + if (ibeg < 1 .or. jbeg < 1 .or. + & iend > imax .or. jend > jmax) then + + ! DO NOT quit if we find a boundary outside the grid + ! bounds. Rather, just set the J violating bound(s) to + ! the min or max limit, and for I bounds, allow the + ! program to continue down to subsequent code below, + ! provided it's a global grid. + +c print *,'!!! ' +c print *,'!!! Before vmag adjustments, boundaries are: ' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt,' dx= ',dx +c print *,'!!! temp_grid_minlon= ',temp_grid_minlon +c print *,'!!! vmag_latmax= ',vmag_latmax +c print *,'!!! ivlonfix = ',ivlonfix,' jvlatfix = ',jvlatfix +c print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax +c print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Vmag will not be computed for' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Vmag will not be computed for ' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,'!!! ' + print *,'!!! *AFTER* vmag adjustments, boundaries are: ' + print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax + print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + endif + + endif + + endif + + if (cparm == 'vmag') then + ri = re * 3 +c print '(a36,f10.4,a6,f10.4)' +c & ,' + before call to vmag barnes, re= ',re,' ri= ',ri + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,bskip1,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes...' + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After 1st findmax loop, # calls to barnes = ',ibct + print *,'Total # of barnes loop iterations = ',ibarnes_loopct + endif + +c + 55 format ('i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ',f7.3 + & ,' barnval= ',f11.5) + 56 format ('k= ',i3,' i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ' + & ,f7.3,' barnval= ',f11.5) + + if (ctlon < 0.) then + ! We have grid-wrapped to find the ctlon, which was found to be + ! < 0, so for reporting purposes and for the start of the next + ! loop, set ctlon to positive degress east. + ctlon = ctlon + 360. + endif + + if (cparm == 'zeta') then + + if ( verb .ge. 3 ) then + print *,'!!! Zeta check, fmax= ',fmax,' fmin= ',fmin + write (6,61) 360.-ctlon,ctlat,fmax*100000.,fmin*100000. + endif + + else + + if ( verb .ge. 3 ) then + write (6,63) 360.-ctlon,ctlat,fmax,fmin + endif + + endif + 61 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 63 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax = ',e16.3,' fmin = ',e16.3) + 111 format (i2,' rlont= ',f7.2,'W rlatt= ',f7.2,' zeta= ',f13.8) + +c Through interpolation, the grid for vmag has already been +c minimized considerably, we don't need to go through the 2nd part +c of this subroutine, which halves the grid spacing. + + if (nhalf < 1 .or. cparm == 'vmag') then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c ------------------------------------------------------------- +c If the grid spacing is +c fine enough (I've chosen 0.2-deg as a min threshold), there is +c no need to halve the grid more than 3 times, as halving a +c 0.2-deg grid 3 times gives a resolution of 0.025-deg (2.7 km), +c or a max error in the position estimate of 2.7/2 = 1.35 km. + + if ((dx+dy)/2. <= 0.2) then + if ((dx+dy)/2. <= 0.05) then + nhalf = 1 + else + nhalf = 2 + endif + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +ctpm npts = 3 + npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only +c do this once for this grid-refinement (even though the grid is +c redefined 3 times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to get_ij_bounds' + print *,'!!! just before nhalf loop. Stopping processing' + print *,'!!! for storm number ',ist + endif + + ifmret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + + if ( verb .ge. 3 ) then + print *,' ' + endif + + if ((dx+dy)/2. <= 1.25 .and. ri >= 300 .and. re >= 150) then + retmp = re + ritmp = ri + re = re * 0.5 + ri = ri * 0.5 + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re has been reduced' + print *,'from ',retmp,' to ',re,', and ri has been reduced ' + print *,'from ',ritmp,' to ',ri + endif + + else + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re and ri have NOT ' + print *,'been changed. re = ',re,' ri = ',ri + endif + + endif + + ibct=0 + ibarnes_loopct = 0 + do k=1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: find_maxmin kloop, k= ',i2,' ',i2.2,':' + & ,i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat + fmax = -1.0e+15; fmin = 1.0e+15 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'find_maxmin nhalf loop, cparm= ',cparm,' k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,iskip,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes, k= ',k + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop2 + enddo jloop2 + + if ( verb .ge. 3 ) then + if (cparm == 'zeta') then + write (6,71) k,360.-ctlon,ctlat,fmax*100000.,fmin*100000. + else + write (6,73) k,360.-ctlon,ctlat,fmax,fmin + endif + endif + + enddo + + 71 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 73 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax = ',e16.3,' fmin = ',e16.3) + + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ppp after 2nd findmax loop, # calls to barnes = ' + & ,ibct + print *,'ppp Total # of barnes loop iterations = ' + & ,ibarnes_loopct + endif + + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine barnes(flon,flat,rlon,rlat,iimax,jjmax,iibeg,jjbeg + & ,iiend,jjend,fxy,defined_pt,bskip,re,ri,favg,icount,ctype + & ,trkrinfo,iret) +c +c ABSTRACT: This routine performs a single-pass barnes anaylsis +c of fxy at the point (flon,flat). The e-folding radius (km) +c and influence radius (km) are re and ri, respectively. +c +c NOTE: The input grid that is searched in this subroutine is most +c likely NOT the model's full, original grid. Instead, a smaller +c subgrid of the original grid is searched. The upper left and +c lower right grid point indices are passed into this subroutine +c (iibeg, jjbeg, iiend, jjend) for this subgrid. These indices are +c determined in the subroutine get_ij_bounds, and the purpose of +c doing it this way is to limit the number of points for which the +c subroutine has to calculate distances (for a global 1 deg grid, +c the number of loop iterations is reduced from 65160 to somewhere +c around 600). +c +c NOTE: This subroutine will immediately exit with a non-zero +c return code if it tries to access a grid point that does not have +c valid data. This would happen in the case of a regional grid, if +c you try to access a point near the edge of the grid (remember that +c because of the interpolation for the regional grids, there will be +c areas around the edges that have no valid data). +c +c INPUT: +c flon Lon value for center point about which barnes anl is done +c flat Lat value for center point about which barnes anl is done +c rlon Array of lon values for each grid point +c rlat Array of lat values for each grid point +c iimax Max number of pts in x-direction on input grid +c jjmax Max number of pts in y-direction on input grid +c iibeg i index for grid point to start barnes anlysis (upp left) +c jjbeg j index for grid point to start barnes anlysis (upp left) +c iiend i index for last grid point in barnes anlysis (low right) +c jjend j index for last grid point in barnes anlysis (low right) +c fxy Real array of data on which to perform barnes analysis +c defined_pt Logical; bitmap array used for regional grids +c bskip integer to indicate number of grid points to skip during +c a barnes loop, in order to speed processing +c re input e-folding radius for barnes analysis +c ri input influence radius for searching for min/max +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, in +c this barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c favg Average value about the point (flon,flat) +c iret Return code from this subroutine +c + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real fxy(iimax,jjmax), rlon(iimax), rlat(jjmax) + real degrees + integer bskip + logical(1) defined_pt(iimax,jjmax) + character(*) ctype + +c -------------------------- + + res = re*re + wts = 0.0 + favg = 0.0 + + icount = 0 + + do jix=jjbeg,jjend,bskip + do iix=iibeg,iiend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > iimax) then + if (trkrinfo%gridtype == 'global') then + i = iix - iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i,' imax= ',iimax + print *,' ' + endif + + stop 97 + endif + endif + + icount = icount + 1 + + call calcdist(flon,flat,rlon(i),rlat(j),dist,degrees) + + if (dist .gt. ri) cycle + + if (defined_pt(i,j)) then + if (fxy(i,j) >-999.01 .and. fxy(i,j) <-998.99) then + ! This is a patch. Even though this (i,j) is a valid + ! point, its zeta value has been set to -999 because a + ! neighboring point in subroutine rvcal was found + ! to be out of the grid boundaries. + cycle + endif + wt = exp(-1.0*dist*dist/res) + wts = wts + wt + favg = favg + wt*fxy(i,j) + else + if (ctype == 'vitals') then + continue + else +carw print *,' ' +carw print *,'!!! UNDEFINED PT OUTSIDE OF GRID IN BARNES....' +carw print *,'!!! i= ',i,' j= ',j +carw print *,'!!! flon= ',flon,' flat= ',flat +carw print *,'!!! rlon= ',rlon(i),' rlat= ',rlat(j) +carw print *,'!!! re= ',re,' ri= ',ri +carw print *,'!!! EXITING BARNES....' +carw print *,' ' +carw iret = 95 +carw return + endif + endif + + enddo + enddo + + if (wts > 1.0E-5) then + favg = favg/wts + else + favg = 0.0 + endif + iret = 0 +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,rglatmax,rglatmin,rglonmax,rglonmin,geslon,geslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) +c +c ----------------------------------------------------------- +c ABSTRACT: This subroutine figures out, based on ri, dx and dy and +c the guess latitude and longitude positions, the farthest reaching +c grid points that are searchable by an analysis subroutine. The +c purpose is to return indices for a subgrid that is much smaller +c than the original, full grid. This smaller subgrid can then be +c passed to a subsequent analysis or interpolation subroutine, and +c work can be done on this smaller array. This can help save time, +c especially in the barnes analysis subroutine, as work will only +c be done on, say, a 20 x 20 array (400 pts) instead of on a +c 360 x 181 array (65160 pts). It's crucial, however, to make sure +c that the ibeg, jbeg, iend and jend are extended far enough out to +c fully encompass any search that would be done. Below is a +c diagram showing the different grids.... +c +c Full Global or Regional Model Grid (Grid F) -----------> +c ---------------------------------------------------------------- +c | | (ibeg,jbeg) | +c | | x = ij position that the | (Grid R) | +c | | geslat/geslon is fixed to. ._______________. | +c | | | | | +c | | Even though only the points | (Grid B) | | +c | | within Grid B will be checked | . . . . k | | +c v | later on for a max/min (in the | . . . . . | | +c | case of a subsequent call to | . . x . e | | +c | find_maxmin), the barnes anal- | . . . . . | | +c | ysis will include all pts sur- | . . . . . | | +c | rounding these Grid B points | | | +c | that are within a radius of ri. ._______________. | +c | So in the case of pt. k, that ri | +c | radius may extend all the way to the Grid R | | +c | boundary, thus we need to include those (iend,jend) | +c | points within our ibeg-jbeg-iend-jend bounds. | +c | | +c ---------------------------------------------------------------- +c +c Remember that the grids we deal with start north and increase +c south, so the northernmost latitude on the input grid will have +c a j index of 1. +c +c INPUT: +c npts Num pts from x to edge of max/min search grid (Grid B) +c (i.e., You define the size of Grid B by the value of +c npts that you pass into this subroutine). +c nhalf Number of times the grid spacing will be halved +c ri Radius of influence (for use in barnes analysis) +c imax Number of points in x-direction on original grid +c jmax Number of points in y-direction on original grid +c dx Input grid spacing in i-direction on original grid +c dy Input grid spacing in j-direction on original grid +c rglatmax Value of northern-most latitude on original grid +c rglatmin Value of southern-most latitude on original grid +c rglonmax Value of eastern-most longitude on original grid +c rglonmin Value of western-most longitude on original grid +c geslat Value of latitude of guess position of storm +c geslon Value of longitude of guess position of storm +c +c OUTPUT: +c ilonfix i index on full, input grid that storm is fixed to +c jlatfix j index on full, input grid that storm is fixed to +c ibeg i index for top left of sub-array (Grid R) of input grid +c iend i index for bot right of sub-array (Grid R) of input grid +c jbeg j index for top left of sub-array (Grid R) of input grid +c jend j index for bot right of sub-array (Grid R) of input grid +c igiret Return code from this subroutine +c + USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + real tmpangle +c + igiret = 0 +c +c -------------------------------------- +c GET BEGINNING AND ENDING J POINTS.... +c +c (1) Calculate number of searchable, max/min pts, that is, the pts +c from x to the edge of Grid B. +c (2) Calculate number of pts beyond the last search point in Grid +c B, but are within the bounds of Grid R and thus can be +c included in the barnes analysis. +c (3) Add (1) and (2) to get the max number of pts to subtract/add +c to x to get jbeg and jend. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Beginning of get_ij_bounds...' + print *,' geslat= ',geslat,' geslon= ',geslon + print *,' ' + endif + + +c If nhalf > 0: This occurs in the case of a call from fmax, when +c the grid spacing is halved nhalf times. In this case, we have to +c do extra work to figure out the maximum possible grid point. For +c this case: +c jhlatpts = # of grid pts to last possible search pt (from x to +c edge of Grid B in above diagram), plus a cushion. +c jripts = # of searchable grid points within radius ri of last +c possible search pt (num pts between edge of Grid B +c and edge of Grid R in above diagram), plus a cushion +c jbmaxlatpts = # of pts (in j direction) from x to the edge of +c Grid R to include in this subgrid. +c +c If nhalf = 0: In this case, the grid spacing will not be reduced, +c so the number of pts in j direction from x to the edge of Grid +c B will be the input parameter npts, and just multiply it by 2, +c and add 2 for a cushion to get jmaxlatpts. Typically, this sub +c is called from find_maxmin, and in that sub, the first time that +c this sub is called, nhalf will = 0. Then, after a first-shot +c center is found, the grid spacing will be cut in order to rerun +c barnes on a smaller grid, and that's when nhalf will be sent +c here as 3. +c + if (nhalf > 0) then + rdeg = 0.0 + do i = 1,nhalf + rdeg = rdeg + float(npts) * (1./(float(i)*2)) * (dx+dy)/2 + enddo + jhlatpts = ceiling(rdeg/dy) + 1 + jripts = ceiling((ri + 1.)/(dtk*dx)) + 1 + jbmaxlatpts = jhlatpts + jripts + else + jbmaxlatpts = npts * 2 + 2 + endif +c +c +c Roughly fix geslat to the grid point just poleward of geslat. +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' +++ Near top of get_ij_bounds, ' + print *,' +++ geslat= ',geslat,' geslon= ',geslon + print *,' +++ rglatmax= ',rglatmax,' rglatmin= ',rglatmin + print *,' +++ rglonmax= ',rglonmax,' rglonmin= ',rglonmin + print *,' +++ imax= ',imax,' jmax= ',jmax + print *,' +++ dx= ',dx,' dy= ',dy,' nhalf= ',nhalf + print *,' +++ npts= ',npts + if(nhalf>0) then + print *,' +++ jhlatpts= ',jhlatpts,' jripts= ',jripts + else + print *,' +++ nhalf<=0 so jhlatpts and jripts unused' + endif + print *,' +++ jbmaxlatpts= ',jbmaxlatpts + endif + + if (geslat >= 0.0) then + jlatfix = int((rglatmax - geslat)/dy + 1.) + else + jlatfix = ceiling((rglatmax - geslat)/dy + 1.) + endif + + if ( verb .ge. 3 ) then + print *,' +++ jlatfix= ',jlatfix + endif + + jbeg = jlatfix - jbmaxlatpts + jend = jlatfix + jbmaxlatpts + if (jbeg > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jbeg > jmax' + print *,'!!! jbeg = ',jbeg,' jmax= ',jmax + endif + + igiret = igiret + 1 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jend < 1, jend = ',jend + endif + + igiret = igiret + 1 + return + endif + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' +++ jbeg= ',jbeg,' jend= ',jend + endif + + ! If using a global grid, avoid using the pole points, or else + ! you'll get a cosfac = 0 and then a divide by zero!!! + + if (jend == jmax .and. rglatmin == -90.0) then + jend = jmax - 2 + endif + if (jbeg == 1 .and. rglatmax == 90.0) then + jbeg = 3 + endif + +c ----------------------------------------- +c NOW GET BEGINNING AND ENDING I POINTS.... +c +c Using the map factor (cos lat), figure out, based on ri, the +c max distance beyond the last search point in x-direction (in +c degrees) that could be searched at this guess latitude (geslat) +c (i.e., in the diagram above, the max num pts from pt. e eastward +c to the edge of Grid R). Calculate how many grid points that is, +c add 2 to it for a cushion, & add the number of points (npts) +c within the defined search grid (Grid B) to get ibmaxlonpts. +c +c April, 2007: A min statement was put on the calculation to +c derive dlon, since with that cosine in there, the values of +c of dlon could get pretty ridiculous as you approach the poles. +c Also, the cosine factor (cosfac) used to be computed at the +c most poleward latitude possible given the jend here. For +c similar concerns with cosines near the poles, I've scrapped +c this to instead compute the cosine factor at the input +c guess latitude. - tpm + + cosfac = cos (geslat * dtr) + tmpangle = cosfac * dtk + dlon = min((ri /tmpangle ),20.0) +c dlon = min((ri / (cosfac * dtk)),20.0) +c + if (nhalf > 0) then + ihlonpts = ceiling(rdeg/dx) + 1 + ibmaxlonpts = ihlonpts + ceiling(dlon/dx) + 2 + else + ibmaxlonpts = npts + ceiling(dlon/dx) + 2 + endif + + if ( verb .ge. 3 ) then + if(nhalf>0) then + print *,' +++ rdeg= ',rdeg,' ri= ',ri,' cosfac= ',cosfac + print *,' +++ dtr= ',dtr,' dtk= ',dtk,' dlon= ',dlon + else + print*,' +++ nhalf<=0 so rdeg,ri,cosfac,dtr,dtk,dlon unused' + endif + print *,' +++ ibmaxlonpts= ',ibmaxlonpts,' dx= ',dx,' dy= ',dy + endif + +c Roughly fix geslon to the grid point just EASTward of geslon. + + ilonfix = int((geslon - rglonmin)/dx + 2.) + + ibeg = ilonfix - ibmaxlonpts + iend = ilonfix + ibmaxlonpts + + if ( verb .ge. 3 ) then + print *,' +++ (orig) ilonfix= ',ilonfix + print *,' +++ (orig) ibeg= ',ibeg,' iend= ',iend + print *,' +++ ' + endif + + if (ibeg > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 1 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, ibeg > imax' + print *,'!!! for a non-global grid' + print *,'!!! ibeg = ',ibeg,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + ! For a regional grid, just set iend to be imax + iend = imax + endif + endif + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + ! For a regional grid, just set ibeg to be 1 + ibeg = 1 + endif + endif + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + + if ( verb .ge. 3 ) then + print *,'!!! ERROR in get_ij_bounds, iend < 1' + print *,'!!! for a non-global grid' + print *,'!!! iend = ',iend,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine check_bounds (guesslon,guesslat,ist,ifh,trkrinfo + & ,icbret) +c +c ABSTRACT: This subroutine checks to make sure that the requested +c storm is in fact within the model's grid boundaries; +c this is only a concern for the regional models. +c + USE def_vitals; USE grid_bounds; USE set_max_parms + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + if (trkrinfo%gridtype == 'regional') then + if (guesslon > glonmax .or. guesslon < glonmin .or. + & guesslat > glatmax .or. guesslat < glatmin) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is outside of grid' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + goto 125 + else + icbret = 0 + endif + endif + + ! We have encountered problems with global grids where we + ! continue tracking almost the whole way to the pole. While + ! that's nice to do that, it creates problems for array + ! indices, especially in subroutine getradii. So we will cut + ! our losses and eliminate tracking of storms within + ! 5 degrees of the pole for global grids. + + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'global')then + if (guesslat > 85.0 .or. guesslat < -85.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is too close to the' + print *,'!!! N or S Pole for global tracking.' + print *,'!!! STOPPING TRACKING FOR THIS STORM DUE TO POLE' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + else + icbret = 0 + endif + endif + + 125 continue +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calcdist(rlonb,rlatb,rlonc,rlatc,xdist,degrees) +c +c ABSTRACT: This subroutine computes the distance between two +c lat/lon points by using spherical coordinates to +c calculate the great circle distance between the points. +c Figure out the angle (a) between pt.B and pt.C, +c N. Pole then figure out how much of a % of a great +c x circle distance that angle represents. +c / \ +c b/ \ cos(a) = (cos b)(cos c) + (sin b)(sin c)(cos A) +c / \ +c pt./<--A-->\c NOTE: The latitude arguments passed to the +c B / \ subr are the actual lat vals, but in +c \ the calculation we use 90-lat. +c a \ +c \pt. NOTE: You may get strange results if you: +c C (1) use positive values for SH lats AND +c you try computing distances across the +c equator, or (2) use lon values of 0 to +c -180 for WH lons AND you try computing +c distances across the 180E meridian. +c +c NOTE: In the diagram above, (a) is the angle between pt. B and +c pt. C (with pt. x as the vertex), and (A) is the difference in +c longitude (in degrees, absolute value) between pt. B and pt. C. +c +c !!! NOTE !!! -- THE PARAMETER ecircum IS DEFINED (AS OF THE +c ORIGINAL WRITING OF THIS SYSTEM) IN KM, NOT M, SO BE AWARE THAT +c THE DISTANCE RETURNED FROM THIS SUBROUTINE IS ALSO IN KM. +c + USE trig_vals + + real degrees +c + if (rlatb < 0.0 .or. rlatc < 0.0) then + pole = -90. + else + pole = 90. + endif +c + distlatb = (pole - rlatb) * dtr + distlatc = (pole - rlatc) * dtr + difflon = abs( (rlonb - rlonc)*dtr ) +c + cosanga = ( cos(distlatb) * cos(distlatc) + + & sin(distlatb) * sin(distlatc) * cos(difflon)) + +c This next check of cosanga is needed since I have had ACOS crash +c when calculating the distance between 2 identical points (should +c = 0), but the input for ACOS was just slightly over 1 +c (e.g., 1.00000000007), due to (I'm guessing) rounding errors. + + if (cosanga > 1.0) then + cosanga = 1.0 + endif +cPENG added bug fixed on 04/28/2016-------------------------- + if (cosanga < -1.0) then + cosanga = -1.0 + endif +cPENG added on 04/28/2016-------------------------- + + degrees = acos(cosanga) / dtr + circ_fract = degrees / 360. + xdist = circ_fract * ecircum +c +c NOTE: whether this subroutine returns the value of the distance +c in km or m depends on the scale of the parameter ecircum. +c At the original writing of this subroutine (7/97), ecircum +c was given in km. +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine subtract_cor (imax,jmax,dy,level) +c +c ABSTRACT: This subroutine subtracts out the coriolis parameter +c from the vorticity values. It is needed because at the original +c writing of this system, all of the forecast centers who included +c vorticity included only absolute vorticity. +c + USE tracked_parms; USE trig_vals; USE grid_bounds + + implicit none + + integer :: i,j,imax,jmax,level + real :: dy,coriolis,rlat +c + do j=1,jmax + rlat = glatmax - ((j-1) * dy) + coriolis = 2. * omega * sin(rlat*dtr) + do i=1,imax + zeta(i,j,level) = zeta(i,j,level) - coriolis + enddo + enddo +c + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_grib_file_name (ifh,gfilename,ifilename) + +c ABSTRACT: This subroutine uses various input regarding the model +c and forecast hour and generates the name of the input grib file +c for this particular forecast hour. Remember that the lead time +c is in minutes and that 5 spaces must be reserved for the lead +c time (e.g., f00360). File name should be something that looks +c like either, e.g., "gfdl.6thdeg.katrina12l.2005082818.f00720", +c or "gfdl.6thdeg.2005082818.f00720" (the part in there with the +c storm name & ID is optional). The grib index file name should +c be exactly the same as the grib data file itself, but with the +c character string ".ix" added onto the end of the name. +c +c NOTE: Array iftotalmins is brought in via module tracked_parms. +c +C INPUT: +c ifh integer array index for current lead time +c +c OUTPUT: +c gfilename GRIB file name +c ifilename GRIB index file name + + USE gfilename_info; USE tracked_parms; USE atcf + USE verbose_output + + implicit none + + character(*) gfilename,ifilename + character cfmin*5,cymdh*10 + integer ifh,nlen1,nlen2,nlen3,nlen4,nlen5 + +c Convert integer minutes to 5-position character, with +c leading zeroes, and convert 10-digit integer date into +c 10-position character. Then trim the various input variables +c and combine all into the file name. + + write (cfmin,'(i5.5)') iftotalmins(ifh) + write (cymdh,'(i10.10)') atcfymdh + + nlen1 = len_trim(gmodname) + gfilename = trim(gmodname(1:nlen1)) + + nlen2 = len_trim(rundescr) + + gfilename = trim(gfilename(1:nlen1))//'.'//trim(rundescr(1:nlen2)) + + nlen3 = len_trim(atcfdescr) + nlen4 = len_trim(gfilename) + +c If an extension to the name with the ATCF or storm name descriptor +c was included, then add it to the name now. Otherwise, just add +c the starting date and the lead time in minutes. + + if (nlen3 > 0) then + gfilename = trim(gfilename(1:nlen4))//'.' + & //trim(atcfdescr(1:nlen3))//'.'//cymdh//'.f'//cfmin + else + gfilename = trim(gfilename(1:nlen4))//'.'//cymdh//'.f'//cfmin + endif + +c Create the name for the grib index file, which is just the name of +c the grib file, with "ix" added to the end of it. + + nlen5 = len_trim(gfilename) + ifilename = trim(gfilename(1:nlen5))//'.ix' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,72) 'gfilename',gfilename + write (6,72) 'ifilename',ifilename + endif + + 72 format (1x,'In get_grib_file_name, file name for ',a9 + & ,' is ',a120) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) +c +c ABSTRACT: This subroutine reads the input GRIB file for the +c tracked parameters. It then calls subroutines to convert the +c data from a 1-d array into a 2-d array if the read was successful. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature (I jerry-rigged this by storing +c the data as being at the 401 mb level.) +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +cPENG----2018-06-07 ------------------------ +c 18. Ushear 200-850hPa (501hPa level) +c 19. 500hPa relative humidity +c +c INPUT: +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c inp of a derived type, contains user-input info +c lugb integer unit number of input grib file +c lugi integer unit number of input grib index file +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE verbose_output; USE params; USE grib_mod; USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + type (datecard) inp + type (gribfield) :: gfld,prevfld,holdgfld +c + integer, parameter :: jf=40000000 +c integer, parameter :: nreadparms=17 +cPENG----2018-06-07 ------------------------ + integer, parameter :: nreadparms=19 + + real, allocatable :: f(:) + real :: dmin,dmax,firstval,lastval + logical(1), allocatable :: lb(:) + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1) file_open + logical :: unpack=.true. + logical :: open_grb=.false. + character*1 :: lbrdflag + character*8 :: chparm(nreadparms) + CHARACTER(len=8) :: pabbrev + character (len=10) big_ben(3) + integer date_time(8) + integer,dimension(200) :: jids,jpdt,jgdt + integer :: listsec1(13), enable_timing + integer, intent(in) :: imax,jmax + integer igparm(nreadparms),iglev(nreadparms) + integer iglevtyp(nreadparms) + integer ig2_parm_cat(nreadparms),ig2_parm_num(nreadparms) +cPENG----2018-06-07 ------------------------ + integer ig2_parm_cat_cmcd(nreadparms) + & ,ig2_parm_num_cmcd(nreadparms) + +c integer ig2_lev_val(nreadparms),ig2_lev_typ(nreadparms) +cPENG----2018-06-07 ------------------------ + integer ig2_lev_10(nreadparms) + integer ig2_lev_11(nreadparms),ig2_lev_12(nreadparms) + +cPENG 04/18/2018 for CMC Det. and CMC ensemble data +cPENG----2018-06-07 ------------------------ +c integer ig2_lev_11_cmc(nreadparms),ig2_lev_val_cmc(nreadparms) +c integer ig2_lev_11_cmcd(nreadparms),ig2_lev_val_cmcd(nreadparms) + integer ig2_lev_11_cmc(nreadparms),ig2_lev_12_cmc(nreadparms) + integer ig2_lev_11_cmcd(nreadparms),ig2_lev_12_cmcd(nreadparms) + + integer cpsig2_parm_cat(nlevs_cps),cpsig2_parm_num(nlevs_cps) +c integer cpsig2_lev_typ(nlevs_cps),cpsig2_lev_val(nlevs_cps) +cPENG-04/18/2018 for CMC Det. and CMC ensemble data + integer cpsig2_lev_10(nlevs_cps) + integer cpsig2_lev_11(nlevs_cps),cpsig2_lev_12(nlevs_cps) + + integer ec_igparm(nreadparms),ec_iglev(nreadparms) + integer ec_iglevtyp(nreadparms) + integer cpsgparm(nlevs_cps),cpsglev(nlevs_cps) + integer cpsglevtyp(nlevs_cps) + integer ec_cpsgparm(nlevs_cps) + integer jpds(200),jgds(200),kpds(200),kgds(200) + integer igvret,ifa,ila,ip,ifh,i,j,k,kj,iret,kf,lugb,lugi + integer jskp,jdisc,np + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer pdt_4p0_vert_level,pdt_4p0_vtime + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 +c + lbrdflag = 'n' + enable_timing=trkrinfo%enable_timing +c The following data statements contain the parameters that will be +c used to search the grib files. The first 9 parameters will all be +c used to locate the storm position. The last 4 parameters (500 mb +c u- and v-components and 10 m u- and v- components) will not be +c used for tracking, but only for helping to estimate the next first +c guess position (500 mb winds) and for estimating the max near- +c surface wind speeds in the vicinity of the storm (10 m winds). +c +c ** NOTE: iglevtyp(12 & 13) and iglev(12 & 13) are initialized to +c 0 just to satisfy the IBM xlf compiler, which barks about +c there being too few initial values in the list when I +c only had 11 values there -- even though the real +c initialization for these variables is done just about +c 10 lines below. +c +c ** NOTE: The new ECMWF hi-res data uses the ECMWF GRIB parameter +c ID table, which has different values than the NCEP +c table. Therefore, we needed to add the variables and +c data values for ec_igparm, ec_iglevtyp and ec_iglev. +c +c July 2007: Read statements added for GP height for cyclone +c phase space (CPS) algorithm. + +c data igparm /41,41,33,34,33,34,7,7,1,33,34,33,34,11,7,7,81/ +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7,81/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 +c & ,100,100,100,1/ +c data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500,401 +c & ,500,200,0/ +cPENG----2018-06-07 ------------------------ + data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7,81 + & ,33,52/ + data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 + & ,100,100,100,1,100,100/ + data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500,401 + & ,500,200,0,501,500/ + + data cpsgparm /13*7/ + data ec_cpsgparm /13*156/ + data cpsglevtyp /13*100/ + data cpsglev /900,850,800,750,700,650,600,550,500,450,400 + & ,350,300/ + +c data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 +c & ,131,132,130,156,156,999/ +c data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 +c & ,100,100,100,999/ +c data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 +c & ,401,500,200,999/ +c +c data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' +c & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' +c & ,'vgrid','temp','gphgt','gphgt','lmask'/ +cPENG----2018-06-07 ------------------------ + data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 + & ,131,132,130,156,156,999,131,157/ + data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 + & ,100,100,100,999,100,100/ + data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 + & ,401,500,200,999,501,500/ + + data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' + & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' + & ,'vgrid','temp','gphgt','gphgt','lmask' + & ,'ushe','rhum'/ + +c data ig2_parm_cat /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2/ +c data ig2_parm_num /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8/ +c data ig2_lev_typ /100,100,100,100,100,100,100,100,101,103,103 +c & ,100,100,100,100,100,-9999/ +c data ig2_lev_val /850,700,850,850,700,700,850,700,0,10,10,500,500 +c & ,401,500,200,-9999/ +cPENG----2018-06-07 ------------------------ + data ig2_parm_cat /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2,2,1/ + data ig2_parm_num /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8,2,1/ + data ig2_parm_cat_cmcd /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2,2,1/ + data ig2_parm_num_cmcd /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8,2,1/ + + data ig2_lev_10 /100,100,100,100,100,100,100,100,101,103,103 + & ,100,100,100,100,100,-9999,100,100/ + +cPENG 04/18/2018 for CMC Det. and CMC ensemble data +c data ig2_lev_11_cmc /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 +c & ,-4,-4,0/ +c data ig2_lev_val_cmc/85,7,85,85,7,7,85,7,0,10,10,5,5,40100 +c & ,5,2,-9999/ +c data ig2_lev_11_cmcd /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 +c & ,-4,-4,0/ +c data ig2_lev_val_cmcd /85,7,85,85,7,7,85,7,0,10,10,5,5,40100 +c & ,5,2,-9999/ +cPENG----2018-06-07 ------------------------ +cPENG---for gfs/gefs NEVGEM/FENS ensemble---------- + data ig2_lev_11 /0,0,0,0,0,0,0,0,0,0,0 + & ,0,0,0,0,0,0,0,0/ + data ig2_lev_12 /85000,70000,85000,85000,70000,70000,85000,70000 + & ,0,10,10,50000,50000 + & ,40100,50000,20000,-9999,50100,50000/ +cPENG---for CMC ensemble--------------------- + data ig2_lev_11_cmc /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0,0,-4/ + data ig2_lev_12_cmc/85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999,50100,5/ +cPENG---for CMC deterministic--------- + data ig2_lev_11_cmcd /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0,0,0/ + data ig2_lev_12_cmcd /85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999,50100,50000/ + + + data cpsig2_parm_cat /13*3/ + data cpsig2_parm_num /13*5/ +c data cpsig2_lev_typ /13*100/ +c data cpsig2_lev_val /900,850,800,750,700,650,600,550,500,450,400 +c & ,350,300/ +cPENG---------------------------------- + data cpsig2_lev_10 /13*100/ + data cpsig2_lev_11 /13*0/ + data cpsig2_lev_12 /90000,85000,80000,75000,70000,65000 + & ,60000,55000,50000,45000,40000 + & ,35000,30000/ + +c Model numbers used: (1) AVN, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) Early Eta, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble, +c (13) SREF Ensemble, +c (14) NCEP Ensemble (from ensstat mean fields), +c (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) NCEP Ensemble RELOCATION +c (21) UKMET hi-res (from NHC) +c (23) FNMOC Ensemble +c (24) HWRF Basin-scale + + if (trkrinfo%gribver == 2) then + +c For GRIB2, we will check to see if the MSLP being searched for +c is the standard MSLP (MSLP parm ID = 1) or if it is the +c so-called "Eta" or "Membrane" MSLP reduction that is included +c in the output for some models (like GFS and GDAS). Note that +c for 10m winds, with GRIB2, so far with all of the GRIB2 model +c data we've seen to this point, they all have the same IDs for +c 10m winds for all models, so no need to break out by model +c like we do for GRIB v1 in the else portion of this if statement. + + ig2_parm_num(9) = trkrinfo%g2_mslp_parm_id ! 1 = standard MSLP + ! reduction, 192 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB2 read, MSLP ID = ig2_parm_num(9) = ' + & ,ig2_parm_num(9) + + endif + + else + +c For GRIB1, do the same check as done just above in the IF part +c of this IF statement, but note that we need to also check to +c see what the GRIB1 parm IDs are for the sfc wind level type +c and value. Most models list the level type as 105 (which means +c height above the ground) and then a level value of 10. But +c ECMWF and UKMET use a level type of 1 (which means ground or +c water surface) and a level value of 0. + + igparm(9) = trkrinfo%g1_mslp_parm_id ! 102 = standard MSLP + ! reduction, 130 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglev(10) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + iglev(11) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + + ec_iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglev(10) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + ec_iglev(11) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB1 read, MSLP ID = igparm(9) = ' + & ,igparm(9) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev type = ' + & ,iglevtyp(10) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev value = ' + & ,iglev(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev type = ' + & ,ec_iglevtyp(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev value = ' + & ,ec_iglev(10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata. A return' + print *,'code (iret) not equal to zero indicates that ' + print *,'subroutine getgb was unable to find the requested ' + print *,'parameter. This could be simply because the parm is ' + print *,'not included in the grib file (this is likely for ' + print *,'ECMWF data, as they limit what they send us), or it ' + print *,'could indicate a problem with the grib index file.' + endif + + + if (allocated(f)) deallocate(f) + if (allocated(lb)) deallocate(lb) + allocate (f(imax*jmax),stat=ifa) + allocate (lb(imax*jmax),stat=ila) + if (ifa /= 0 .or. ila /= 0) then + print *,' ' + print *,'!!! ERROR in getdata allocating f or lb array.' + print *,'!!! ifa = ',ifa,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + if (trkrinfo%gribver == 2) then + + ! Reading from a GRIB v2 file.... + + grib2_standard_parm_read_loop: do ip = 1,nreadparms + + if (ip == 17) then + if (trkrinfo%use_land_mask == 'y' .or. + & trkrinfo%use_land_mask == 'Y') then + continue + else + if (verb .ge. 3) then + print *,' ' + print *,'The use_land_mask flag has not been set to ' + print *,'y or Y, so we will not try to read it in... ' + print *,' ' + cycle grib2_standard_parm_read_loop + endif + endif + endif + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + if (ip == 17) then + jdisc=1 ! hydrological products. At this point, used only + ! for the land-sea mask. + else + jdisc=0 ! meteorological products + endif + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for input parameter by production template 4.0. This +c tave program is used primarily for temperature, but still we +c will leave that as a variable and not-hard wire it in case we +c choose to average something else in the future. + + ! We are looking for Temperature or GP Height here. This + ! block of code, or even the smaller subset block of code that + ! contains the JPDT(1) and JPDT(2) assignments, can of course + ! be modified if this program is to be used for interpolating + ! other variables.... + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = ig2_parm_cat(ip) + JPDT(2) = ig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + +c JPDT(10) = ig2_lev_typ(ip) +cPENG----2018-06-07 ------------------------ + JPDT(10) = ig2_lev_10(ip) + +cPENG 04/18/2018 CMC Det. and CMC ensemble data +c if (inp%model == 16 ) then +c JPDT(11) = ig2_lev_11_cmc(ip) +c JPDT(12) = ig2_lev_val_cmc(ip) +c elseif (inp%model == 15 ) then +c JPDT(11) = ig2_lev_11_cmcd(ip) +c JPDT(12) = ig2_lev_val_cmcd(ip) +c else +c JPDT(11) = 0 +c if (JPDT(10) == 100) then ! isobaric surface +c JPDT(12) = ig2_lev_val(ip) * 100 ! GRIB2 levels are in Pa +c else +c JPDT(12) = ig2_lev_val(ip) ! This is going to be either mslp +c & ! or 10m winds. +c endif +c endif +cPENG----2018-06-07 ------------------------ + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 8 ) then + JPDT(11) = ig2_lev_11(ip) + JPDT(12) = ig2_lev_12(ip) + else if(inp%model == 16) then + JPDT(11) = ig2_lev_11_cmc(ip) + JPDT(12) = ig2_lev_12_cmc(ip) + else if(inp%model == 15) then + JPDT(11) = ig2_lev_11_cmcd(ip) + JPDT(12) = ig2_lev_12_cmcd(ip) + endif + + if ( verb_g2 .ge. 1 ) then + print *,'before getgb2 call, value of unpack = ',unpack + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is CLOSED' + endif + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is CLOSED' + endif + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,531) date_time(5),date_time(6),date_time(7) + 531 format (1x,'TIMING: before getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,532) date_time(5),date_time(6),date_time(7) + 532 format (1x,'TIMING: after getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call, value of unpacked = ' + & ,gfld%unpacked + print *,'after getgb2 call, gfld%ngrdpts = ',gfld%ngrdpts + print *,'after getgb2 call, gfld%ibmap = ',gfld%ibmap + endif + + if ( iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb2 found parm: ',chparm(ip) +c print *,'+++ at level = ',ig2_lev_val(ip) +cPENG---2018-06-07------------------------------------------- + print *,'+++ at level = ',ig2_lev_12(ip) + + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + select case (chparm(ip)) + case ('absv') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpdt(12) == 20000 .or. jpdt(12) == 2) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) +cPENG----2018-06-07 ------------------------ + case ('ushe') + call conv1d2d_real (imax,jmax,f,ushear + & ,need_to_flip_lats) + case ('rhum') + call conv1d2d_real (imax,jmax,f,rhumid + & ,need_to_flip_lats) + + + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb2 could not find parm: ' + & ,chparm(ip) +c print *,'!!! at level = ',ig2_lev_val(ip) +cPENG---2018-06-07------------------------------------------- + print *,'+++ at level = ',ig2_lev_12(ip) + + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib2_standard_parm_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c This is the GRIB2 reading section. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + grib2_cps_parm_lev_loop: do ip = 1,nlevs_cps + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + + jpds = -1 + jgds = -1 + j=0 + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = cpsig2_parm_cat(ip) + JPDT(2) = cpsig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + +c JPDT(10) = cpsig2_lev_typ(ip) + JPDT(10) = cpsig2_lev_10(ip) +cPENG-------------------------------------------- + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + JPDT(11) = cpsig2_lev_11(ip) + JPDT(12) = cpsig2_lev_12(ip) + endif + +c if (JPDT(10) == 100) then ! isobaric surface +c JPDT(12) = cpsig2_lev_val(ip) * 100 ! GRIB2 levels +c ! are in Pa +c else +c if (verb .ge. 3) then +c print *,' ' +c print *,'ERROR in getdata: JPDT(10) array value' +c print *,'should only be 100 in this CPS section' +c print *,'for GRIB2 data.' +c endif +c endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,731) date_time(5),date_time(6),date_time(7) + 731 format (1x,'TIMING: before getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn + & ,jgdt,unpack,krec,gfld,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,732) date_time(5),date_time(6),date_time(7) + 732 format (1x,'TIMING: after getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 (PHASE) in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call(PHASE),' + & ,' value of unpacked = ',gfld%unpacked + print *,'after getgb2 (PHASE) call, gfld%ngrdpts = ' + & ,gfld%ngrdpts + print *,'after getgb2 (PHASE) call, gfld%ibmap = ' + & ,gfld%ibmap + endif + + if (verb .ge. 3) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + +c Determine packing information from GRIB2 file. +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) + & then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ' + & ,gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ' + & ,gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned + ! from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do +c this once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) + & then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.' + & ,gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ' + & ,gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ' + & ,gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.' + & ,gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline + & ,gfld%ipdtmpl(1),gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,231) + 231 format (' rec# param level byy bmm bdd ' + & ,'bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin + & ,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo grib2_cps_parm_lev_loop + + endif + + endif + + else + + !---------------------------------- + ! Reading from a GRIB v1 file.... + !---------------------------------- + + grib1_read_loop: do ip = 1,nreadparms + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then ! ECMWF hi-res data uses ECMWF table + print *,' ' + print *,'WARNING: From the namelist, inp%model is set to a' + print *,' value of 4, which is for ECMWF, so in routine' + print *,' getdata_grib, the input jpds(5,6,7) parms are' + print *,' going to have values that are specific for' + print *,' ECMWF GRIB1 data.' + print *,' ' + jpds(5) = ec_igparm(ip) + jpds(6) = ec_iglevtyp(ip) + jpds(7) = ec_iglev(ip) + else ! All other models use NCEP-standard GRIB table + jpds(5) = igparm(ip) + jpds(6) = iglevtyp(ip) + jpds(7) = iglev(ip) + endif + + print *,' ' + print *,' --- Before getgb, jpds(5)= ',jpds(5) + print *,' --- , jpds(6)= ',jpds(6) + print *,' --- , jpds(7)= ',jpds(7) + + if (jpds(5) == 999) then + cycle + endif + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,831) date_time(5),date_time(6),date_time(7) + 831 format (1x,'TIMING: before getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + + if (enable_timing /= 0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,832) date_time(5),date_time(6),date_time(7) + 832 format (1x,'TIMING: after getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb call, j= ',j,' k= ',k + & ,' iftotalmins= ' + & ,iftotalmins(ifh),' parm # (ip) = ',ip,' iret= ',iret + else + print *,'After getgb call, j= ',j,' k= ',k,' ifhours= ' + & ,ifhours(ifh),' parm # (ip) = ',ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb found parm: ',chparm(ip) + print *,'+++ at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,29) + else + write (6,31) + endif + 29 format (' rec# parm# levt lev byy bmm bdd bhh fmin' + & ,' npts minval maxval') + 31 format (' rec# parm# levt lev byy bmm bdd bhh fhr ' + & ,' npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + + select case (chparm(ip)) + case ('absv') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpds(7) == 200) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb could not find parm: ',chparm(ip) + print *,'!!! at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib1_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + cps_grib1_lev_loop: do ip = 1,nlevs_cps + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then + ! Use different grib parm id for ECMWF GP height + jpds(5) = ec_cpsgparm(ip) + else + jpds(5) = cpsgparm(ip) + endif + jpds(6) = cpsglevtyp(ip) + jpds(7) = cpsglev(ip) + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,841) date_time(5),date_time(6),date_time(7) + 841 format (1x,'TIMING: before getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,842) date_time(5),date_time(6),date_time(7) + 842 format (1x,'TIMING: after getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,39) + else + write (6,41) + endif + 39 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fmin npts minval maxval') + 41 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fhr npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo cps_grib1_lev_loop + + endif + + endif + + endif +c + deallocate (f) + deallocate (lb) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) +c +c ABSTRACT: This subroutine reads the input NetCDF file for the +c tracked parameters for one lead time. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +c +c If the user has requested to check the cyclone phase space for +c this run (phaseflag set to 'y' and phasescheme set to 'cps'), +c then we need to have gp height data for 900-300 mb at every 50 +c mb. Some of those levels for gp height data were already read +c in with the read of the initial 17 parameters, but we will be +c sure to read in the others, if requested. +c +c INPUT: +c ncfile_id integer ID associated with the NetCDF file +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file +c itself in subroutine read_netcdf_fhours. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE netcdf_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo +c integer, parameter :: nreadparms=17,nreadparms_cps=13 +cPENG---2018-06-07-------------------------------- + integer, parameter :: nreadparms=19,nreadparms_cps=13 + + real, allocatable :: f(:) + real :: dmin,dmax,xmissing_value,xfill_value + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + character*1 :: lbrdflag,match_check + character*30 :: chparm(nreadparms) + character*30 :: chparm_cps(nreadparms_cps) + integer, intent(in) :: ncfile_id,imax,jmax + integer :: igvret,ifa,ip,ifh,i,j,k,m,n,ncfile_tmax,nf_get_att_real + integer :: nf_get_att_double,nf_inq_attlen,imvlen,ifvlen + integer :: usertime,ncix,missing_val_length,nf_status + integer :: nf_inq_varid,varid +c + lbrdflag = 'n' + +cnc data cpsgparm /13*7/ +cnc data cpsglevtyp /13*100/ +cnc data cpsglev /900,850,800,750,700,650,600,550,500,450,400 +cnc & ,350,300/ + +c data chparm /'vort850','vort700' +c & ,'u850','v850','u700','v700' +c & ,'h850','h700','slp','u_ref','v_ref' +c & ,'u500','v500','tm'/ + +c Load the names of the NetCDF variables for the standard +c variables into the chparm array... + + chparm(1) = netcdfinfo%rv850name + chparm(2) = netcdfinfo%rv700name + chparm(3) = netcdfinfo%u850name + chparm(4) = netcdfinfo%v850name + chparm(5) = netcdfinfo%u700name + chparm(6) = netcdfinfo%v700name + chparm(7) = netcdfinfo%z850name + chparm(8) = netcdfinfo%z700name + chparm(9) = netcdfinfo%mslpname + chparm(10) = netcdfinfo%usfcname + chparm(11) = netcdfinfo%vsfcname + chparm(12) = netcdfinfo%u500name + chparm(13) = netcdfinfo%v500name + chparm(14) = netcdfinfo%tmean_300_500_name + chparm(15) = netcdfinfo%z500name + chparm(16) = netcdfinfo%z200name + chparm(17) = netcdfinfo%lmaskname + + + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata_netcdf.' + endif + + if (allocated(f)) deallocate(f) + allocate (f(imax*jmax),stat=ifa) + if (ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getdata_netcdf allocating f data array.' + print *,'!!! ifa = ',ifa + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + !--------------------------------------------------------------- + ! First go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match up + ! the lead times that were read in with the lead times that + ! we read in directly from the NetCDF file. Get the index from + ! the NetCDF file for that lead time and use that in the call to + ! the read routine (get_var3_tlev_double). + !--------------------------------------------------------------- + + usertime = iftotalmins(ifh) + + match_check = 'n' + + find_index_loop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + ncix = m + if (verb .ge. 1) then + print *,'+++ Time match in getdata_netcdf for usertime= ' + & ,usertime,' netcdf file index= ncix= ',ncix + endif + match_check = 'y' + exit find_index_loop + endif + + enddo find_index_loop + + if (match_check == 'n') then + print *,' ' + print *,'!!! ERROR in getdata_netcdf: ' + print *,' For a NetCDF file, the user has ' + print *,' requested to process a lead time, and that lead' + print *,' time does not exist in the NetCDF list of time' + print *,' values. ' + print *,' ifh= ',ifh + print *,' usertime= iftotalmins(ifh)= ',iftotalmins(ifh) + print *,' STOPPING....' + stop 99 + endif + + !--------------------------------------------------------------- + ! Now go through the read loop for the list of parameters + !--------------------------------------------------------------- + + netcdf_standard_parm_read_loop: do ip = 1,nreadparms + + if (chparm(ip) == 'X' .or. chparm(ip) == 'x') then + if (verb .ge. 3) then + print *,' ' + print *,'!!! NetCDF read NOT requested for parm # ',ip + endif + cycle netcdf_standard_parm_read_loop + else + if (verb .ge. 3) then + print *,' ' + print *,'+++ NetCDF read requested for parm # ',ip + & ,' ... parm= ',chparm(ip) + endif + endif + + ! Note that I am sending a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which we + ! want), depending on the model & grid, we may need to flip the + ! grid in the north-south direction. I already have a routine + ! for converting data from a 1-d to a 2-d array, and it has + ! the functionality for flipping a grid, so I programmed it as + ! getting a 1-d array from the netcdf read routine and send that + ! 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm(ip),imax,jmax,ncix + & ,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + + ! Need to get the value of the "missing_value" attribute for + ! this variable from the list of attributes in the NetCDF + ! file. Only do this for the first lead time, since the + ! value of the "missing_value" obviously will not change + ! with lead time. + +c nf_status = nf_inq_attlen (ncfile_id,varid,"missing_value" +c & ,imvlen) +c nf_status = nf_inq_attlen (ncfile_id,varid,"_FillValue" +c & ,ifvlen) + + ! These next two nf function calls retrieve the value of the + ! "missing_value" attribute from the list of attributes for + ! the given variable being read in. This is needed in order + ! to know if a non-valid point is being accessed, as for a + ! regional grid, like the nested fvGFS. In GRIB1/GRIB2 files, + ! such regions would be bitmapped out, but in a NetCDF file, + ! no such bitmap exists, so we have to check for missing + ! values. In case it's a moving grid, we need to do this + ! for every lead time, since the "map of missing values" + ! will shift with lead time. Once we have those missing + ! values, we can loop through them and fill the valid_pt + ! logical array so that, in the end, we will have the same + ! logical bitmap for masking out missing data that we have + ! with GRIB1/GRIB2 data. + + nf_status = nf_inq_varid (ncfile_id,chparm(ip),varid) + + print *,'nf_status from nf_inq_varid call = ',nf_status + + nf_status = nf_get_att_real (ncfile_id,varid,"missing_value" + & ,xmissing_value) + + print *,'nf_status from nf_get_att_real call = ',nf_status + +c nf_status = nf_get_att_real (ncfile_id,varid,"_FillValue" +c & ,xfill_value) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"missing_value",len=imvlen) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"_FillValue",len=ifvlen) +c +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + if (verb .ge. 3) then + write (6,31) + 31 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,33) ifhours(ifh),ifclockmins(ifh),ip,chparm(ip) + & ,dmin,dmax + 33 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,35) chparm(ip),xmissing_value + 35 format (' --- ',a30,' missing value = ',g12.4) + endif + + ! This call to conv1d2d_logic_netcdf creates + ! a logical bitmap, so that in case we have + ! regional (non-global) data and an irregular grid (e.g., + ! the FV3 nested grid), we can mask out grid points that + ! have missing values as their data values. There is not + ! actually a native logical bitmap in NetCDF, so we will + ! create one by examining the real data values and masking + ! out grid points that have missing values. + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic_netcdf (imax,jmax,f,valid_pt + & ,xmissing_value,need_to_flip_lats) + lbrdflag = 'y' + endif + + if (ip == 1) then ! 850 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else if (ip == 2) then ! 700 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + else if (ip == 3) then ! 850 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (ip == 4) then ! 850 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (ip == 5) then ! 700 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (ip == 6) then ! 700 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (ip == 7) then ! 850 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (ip == 8) then ! 700 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (ip == 9) then ! MSLP + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + else if (ip == 10) then ! Near-sfc (10m) u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + else if (ip == 11) then ! Near-sfc (10m) v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + else if (ip == 12) then ! 500 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else if (ip == 13) then ! 500 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else if (ip == 14) then ! 300-500 mb mean Temp + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + else if (ip == 15) then ! 500 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (ip == 16) then ! 200 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + else if (ip == 17) then ! Land-sea mask + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + else + + print *,'!!! NOTE: Parm not recognized. ' + print *,'!!! ip is > 17.... ip= ',ip + print *,'!!! Forecast time level = ',ifh + + endif + + endif + + enddo netcdf_standard_parm_read_loop + +c *--------------------------------------------------------------* +c If we are attempting to determine the cyclone structure using +c Hart's cyclone phase space, then read in data now that will +c allow us to do that. If we are instead just using the +c mid-level (300-500 mb) mean temperature to do that with a +c simple warm-core check, then that mean temperature field was +c already read in above in the read loop for the standard +c variables. The variables needed here for CPS are pretty +c straightforward: gp height every 50 mb from 300 to 900 mb. +c keep in mind that we have already read in a few of these +c gp height records for selected levels above. +c *--------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + chparm_cps(1) = netcdfinfo%z900name + chparm_cps(2) = netcdfinfo%z850name + chparm_cps(3) = netcdfinfo%z800name + chparm_cps(4) = netcdfinfo%z750name + chparm_cps(5) = netcdfinfo%z700name + chparm_cps(6) = netcdfinfo%z650name + chparm_cps(7) = netcdfinfo%z600name + chparm_cps(8) = netcdfinfo%z550name + chparm_cps(9) = netcdfinfo%z500name + chparm_cps(10) = netcdfinfo%z450name + chparm_cps(11) = netcdfinfo%z400name + chparm_cps(12) = netcdfinfo%z350name + chparm_cps(13) = netcdfinfo%z300name + + ! Read in GP Height levels for cyclone phase space... + + if (verb .ge. 3) then + print *,' ' + print *,'--- Reads for CPS parms follow...' + print *,' ' + endif + + netcdf_cps_parm_read_loop: do ip = 1,nreadparms_cps + + if (chparm_cps(ip) == 'X' .or. chparm_cps(ip) == 'x') then + if (verb .ge. 3) then + print *,'!!! ERROR: NetCDF read NOT requested for' + print *,'!!! CPS parm # ',ip + print *,'!!! You must have an error in your namelist.' + print *,'!!! You have requested to do cyclone phase' + print *,'!!! checking, so you need to include the ' + print *,'!!! NetCDF names for ALL requested gp height' + print *,'!!! variables from 900 to 300 mb, every 50 ' + print *,'!!! mb,in the namelist.' + print *,'!!! phaseflag is being set to NO (n), and ' + print *,'!!! phase-checking will NOT take place.' + print *,'!!! If you want to run again and just do ' + print *,'!!! phase-checking with a simple warm-core' + print *,'!!! check, then in the namelist set phaseflag' + print *,'!!! to y and set phasescheme to vtt.' + phaseflag = 'n' + endif + exit netcdf_cps_parm_read_loop + else + if (verb .ge. 3) then + print *,'+++ NetCDF read requested for cps parm # ',ip + & ,' ... parm= ',chparm_cps(ip) + endif + endif + + ! As above, we send a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which + ! we want), depending on the model & grid, we may need to + ! flip the grid in the north-south direction. I already + ! have a routine for converting data from a 1-d to a 2-d + ! array, and it has the functionality for flipping a grid, + ! so I programmed it as getting a 1-d array from the netcdf + ! read routine and send that 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm_cps(ip),imax + & ,jmax,ncix,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm_cps(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + nf_status = nf_inq_varid (ncfile_id,chparm_cps(ip),varid) + nf_status = nf_get_att_real (ncfile_id,varid + & ,"missing_value",xmissing_value) + + if (verb .ge. 3) then + write (6,231) + 231 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,233) ifhours(ifh),ifclockmins(ifh),ip + & ,chparm_cps(ip),dmin,dmax + 233 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,235) chparm_cps(ip),xmissing_value + 235 format (' --- ',a30,' missing value = ',g12.4) + endif + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo netcdf_cps_parm_read_loop + + endif + + endif + + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_ncdim1 (ncid,var1_name,nmax) +c +c ABSTRACT: This routine queries a netcdf file to get the +c value of a requested file dimension (e.g., imax, jmax) +c + implicit none + + include "netcdf.inc" + + integer, intent(in) :: ncid + character*(*), intent(in) :: var1_name + integer, intent(out) :: nmax + integer :: status, var1id + + status = nf_inq_dimid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + status = nf_inq_dimlen (ncid,var1id,nmax) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_ncdim1 +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_var1_double (ncid,var1_name,nmax,var1) +c +c ABSTRACT: This routine reads a netcdf file in order to return +c a 1-dimensional array of data. + + implicit none + + include "netcdf.inc" + + integer, intent(in):: ncid + character*(*), intent(in):: var1_name + integer, intent(in):: nmax + real, intent(out):: var1(nmax) + + integer :: status, var1id + + status = nf_inq_varid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) +! write(*,*) 'Got var1id', var1id + + status = nf_get_var_real (ncid,var1id,var1) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var1_double +c +c--------------------------------------------------------- +c +c--------------------------------------------------------- + subroutine get_var3_tlev_double (ncid,var3_name,imax,jmax,ncix + & ,var3,igvret) +c +c ABSTRACT: This routine reads a netcdf file and returns a +c 2-dimensional synoptic variable at a particular lead time. +c The lead time is specified by the ltix array, which is +c included in module tracked_parms and defined in subroutine +c read_fhours. +c +c PARAMETERS +c +c INPUT: +c ncid integer that contains the NetCDF file ID +c var3_name character name of NetCDF input file +c imax integer x-dimension of input data +c jmax integer y-dimension of input data +c ncix integer index of time level for where this time level +c actually is inside the NetCDF data. Do NOT confuse this +c with the index of where this forecast hour is in the +c user's list of input forecast hours, as they may be +c different. For example, the user may request times that +c are every 6 hours, but the NetCDF file might have times +c that are every hour, so the indices for those two arrays +c will be different. Be sure to use the one (ncix) that +c indicates where the data actually starts in the +c NetCDF file. +c +c OUTPUT: +c var3 real array with real values returned from NetCDF read +c igvret integer return code from this routine + + USE tracked_parms; USE verbose_output; USE netcdf_parms + + implicit none + + include "netcdf.inc" +c + integer, intent(in) :: ncid,ncix + character*(*), intent(in) :: var3_name + integer, intent(in) :: imax,jmax + real, intent(out) :: var3(imax,jmax) + integer :: istart(3),ilength(3) + integer :: status,var3id,igvret + + if (verb .ge. 3) then + print *,' ' + print *,'In get_var3_tlev_double, ncix= ',ncix + print *,' nctotalmins(ncix)= ',nctotalmins(ncix) + endif + + istart(1) = 1 + istart(2) = 1 + istart(3) = ncix + + ilength(1) = imax + ilength(2) = jmax + ilength(3) = 1 + + igvret = 0 + + status = nf_inq_varid (ncid,var3_name,var3id) + + if (status /= NF_NOERR) then + print *,' ' + print *,'NOTE: Could not find variable ',var3_name,' at time' + & ,' index ncix= ',ncix + & ,' nctotalmins(ncix)= ',nctotalmins(ncix) + + igvret = 92 + return + endif + + status = nf_get_vara_real (ncid,var3id,istart,ilength,var3) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var3_tlev_double +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine handle_netcdf_err (status) +c +c ABSTRACT: This subroutine is an error handling routine for NetCDF- +c related functions. + + implicit none + + include "netcdf.inc" + + integer status +c + if (status /= nf_noerr) then + print *,' ' + print *,'Tracker NetCDF error: ' + print *, nf_strerror(status) + stop 'Stopped' + endif + + end subroutine handle_netcdf_err +c +c------------------------------------------------------------------- +c +c------------------------------------------------------------------- + subroutine bitmapchk (n,ld,d,dmin,dmax) +c +c This subroutine checks the bitmap for non-existent data values. +c Since the data from the regional models have been interpolated +c from either a polar stereographic or lambert conformal grid +c onto a lat/lon grid, there will be some gridpoints around the +c edges of this lat/lon grid that have no data; these grid +c points have been bitmapped out by Mark Iredell's interpolater. +c To provide another means of checking for invalid data points +c later in the program, set these bitmapped data values to a +c value of -999.0. The min and max of this array are also +c returned if a user wants to check for reasonable values. +c + logical(1) ld + dimension ld(n),d(n) +c + dmin=1.E15 + dmax=-1.E15 +c + do i=1,n + if (ld(i)) then + dmin=min(dmin,d(i)) + dmax=max(dmax,d(i)) + else + d(i) = -999.0 + endif + enddo +c + return + end +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic (imax,jmax,lb1d,lb2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of logical data (lb1d) into a 2-dimensional output +c array (dimension imax,jmax) of logical data (lb2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c lb1d 1-d array containing logical bitmap values +c iscanflag This is kgds(11), an integer value in the GDS, +c which holds the scanning mode for the data values +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + logical(1) lb1d(imax*jmax),lb2d(imax,jmax) + logical(1) :: need_to_flip_lats + integer :: ilat,ilatix,ilon,imax,jmax +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + lb2d(ilon,ilatix) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + lb2d(ilon,ilat) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic_netcdf (imax,jmax,dat1d,lb2d + & ,xmissing_val,need_to_flip_lats) +c +c ABSTRACT: The purpose of this routine is to create a 2-d logical +c bitmap to be used for masking out regions with missing data, +c such as for a regional grid with irregular boundaries (such as +c we've seen for the regional / nested FV3). This bitmap will +c have the same functionality as a GRIB1/GRIB2 bitmap. The trick +c is that NetCDF does not have a logical bitmap within its +c definition, so we need to make one. We do this by reading in +c the "missing_value" attribute for any variable, then here we +c scan through all the data values retrieved from the NetCDF read, +c and then for all grid points with missing values we set the +c valid_pt flag to .false. +c +c Note the use of the need_to_flip_lats flag. This is in order to +c handle grids that are flipped. Most grids -- NCEP, UKMET, ECMWF +c -- have point (1,1) as the uppermost left point on the grid, and +c the data goes from north to south. Some grids -- GFDL and the +c new NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the need_to_flip_lats flag was set to TRUE in getgridinfo, meaning +c that we have northward scanning data, we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d array containing floating point data values +c xmissing_val real value of missing value for the given variable +c that was read in for the calling routine +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + USE verbose_output + + implicit none + + logical(1) lb2d(imax,jmax) + logical(1) need_to_flip_lats + integer ilat,ilatix,ilon,imax,jmax,tct,fct,mct + real dat1d(imax*jmax) + real xmissing_val +c + tct = 0 + fct = 0 + mct = 0 + + if (verb >= 3) then + print *,' ' + print *,'TOP of conv1d2d_logic_netcdf, xmissing_val= ' + & ,xmissing_val + print *,' ' + endif +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then + lb2d(ilon,ilatix) = .false. +c print *,'LBSF FLIP: ilon= ',ilon,' ilatix= ',ilatix +c fct = fct + 1 + else + lb2d(ilon,ilatix) = .true. +c tct = tct + 1 + endif + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then +c print *,'LBSF no-flip: ilon= ',ilon,' ilat= ',ilat + lb2d(ilon,ilat) = .false. +c fct = fct + 1 + else + lb2d(ilon,ilat) = .true. +c tct = tct + 1 + endif + enddo + enddo + + endif + +c print *,' ' +c print *,' LB STATS: tct= ',tct,' fct= ',fct,' mct= ',mct +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine conv1d2d_real (imax,jmax,dat1d,dat2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of real data (dat1d) into a 2-dimensional output +c array (dimension imax,jmax) of real data (dat2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d real array of data +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c dat2d 2-d real array of data +c + logical(1) :: need_to_flip_lats + real dat1d(imax*jmax),dat2d(imax,jmax) +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + dat2d(ilon,ilatix) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + dat2d(ilon,ilat) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_nlists (inp,trkrinfo,netcdfinfo) +c +c ABSTRACT: This subroutine simply reads in the namelists that are +c created in the shell script. Namelist datein contains the +c starting date information, plus the model identifier. Namelist +c stswitch contains the flags for processing for each storm. +c + USE inparms; USE set_max_parms; USE atcf; USE trkrparms; USE phase + USE structure; USE gfilename_info + USE verbose_output; USE waitfor_parms; USE netcdf_parms + USE tracking_parm_prefs + + implicit none + + integer ifh + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo +c + namelist/datein/inp + namelist/atcfinfo/atcfnum,atcfname,atcfymdh,atcffreq + namelist/trackerinfo/trkrinfo + namelist/phaseinfo/phaseflag,phasescheme,wcore_depth + namelist/structinfo/structflag,ikeflag + namelist/fnameinfo/gmodname,rundescr,atcfdescr + namelist/verbose/verb,verb_g2 + namelist/waitinfo/use_waitfor,wait_min_age,wait_min_size + & ,wait_max_wait,wait_sleeptime + & ,use_per_fcst_command,per_fcst_command + namelist/netcdflist/netcdfinfo + namelist/parmpreflist/user_wants_to_track_zeta850 + & ,user_wants_to_track_zeta700,user_wants_to_track_wcirc850 + & ,user_wants_to_track_wcirc700,user_wants_to_track_gph850 + & ,user_wants_to_track_gph700,user_wants_to_track_mslp + & ,user_wants_to_track_wcircsfc,user_wants_to_track_zetasfc + & ,user_wants_to_track_thick500850 + & ,user_wants_to_track_thick200500 + & ,user_wants_to_track_thick200850 + +c Set namelist default values: + use_per_fcst_command='t' + per_fcst_command=' ' + atcffreq=600 + trkrinfo%enable_timing=1 + trkrinfo%want_oci=.false. + trkrinfo%gribver=1 ! Set to GRIB1 as default, can be set to + ! something else in the namelist input. + + read (5,NML=datein,END=801) + 801 continue + read (5,NML=atcfinfo,END=807) + 807 continue + print *,'just before trackerinfo read namelist' + read (5,NML=trackerinfo,END=809) + 809 continue + print *,'just after trackerinfo read namelist' + read (5,NML=phaseinfo,END=811) + 811 continue + read (5,NML=structinfo,END=815) + 815 continue + read (5,NML=fnameinfo,END=817) + 817 continue + read (5,NML=waitinfo,END=821) + 821 continue + read (5,NML=netcdflist,END=823) + 823 continue + read (5,NML=parmpreflist,END=825) + 825 continue + read (5,NML=verbose,END=819,ERR=833) + 819 continue + goto 837 + 833 continue + verb = 1 + 837 continue + + print *,'in read_nlists, verb= ',verb + + if ( verb .ge. 0 ) then + print *,' ' + print *,'After datein namelist in trak.f, namelist ' + & ,'parms follow:' + print *,'Forecast initial year = byy = ',inp%byy + print *,'Forecast initial month = bmm = ',inp%bmm + print *,'Forecast initial day = bdd = ',inp%bdd + print *,'Forecast initial hour = bhh = ',inp%bhh + print *,'Forecast model identifier = model= ',inp%model + print *,'Forecast model type = modtyp= ',inp%modtyp + print *,'Forecast model data lead time units= lt_units= ' + & ,inp%lt_units + print *,'Forecast model data sequencing setup= file_seq= ' + & ,inp%file_seq + print *,'Forecast model nest type = ',inp%nesttyp +c + print *,' ' + print *,'Values read in from atcfinfo namelist: ' + write (6,89) atcfnum,atcfname + write (6,90) atcfymdh + write (6,92) atcffreq + 89 format ('ATCF ID = ',i2,' ATCF Name = ',a4) + 90 format ('ATCF date (initial date on output atcf records) = ' + & ,i10) + 92 format ('ATCF output frequency (in hours*100) = atcffreq = ',i6) +c + print *,' ' + print *,'Values read in from trackerinfo namelist follow: ' + write (6,101) ' western boundary = westbd = ',trkrinfo%westbd + write (6,101) ' eastern boundary = eastbd = ',trkrinfo%eastbd + write (6,101) ' northern boundary = northbd = ',trkrinfo%northbd + write (6,101) ' southern boundary = southbd = ',trkrinfo%southbd + write (6,102) ' tracker type = ',trkrinfo%type + write (6,103) ' mslp threshold = mslpthresh = ' + & ,trkrinfo%mslpthresh + write (6,120) ' Flag for using backup mslp gradient check= ' + & ,'use_backup_mslp_grad_check = ' + & ,trkrinfo%use_backup_mslp_grad_check + write (6,103) ' v850 threshold = v850thresh = ' + & ,trkrinfo%v850thresh + write (6,122) ' Flag for using backup 850 mb Vt check= ' + & ,'use_backup_850_vt_check = ' + & ,trkrinfo%use_backup_850_vt_check + write (6,123) ' Max allowable distance between the ' + & ,'tracker-found fixes for mslp and 850 zeta = ' + & ,trkrinfo%max_mslp_850 + write (6,104) ' model grid type = ',trkrinfo%gridtype + write (6,101) ' Contour interval to be used = ',trkrinfo%contint + write (6,106) ' Flag for whether or not roci will be computed' + & ,' and written out for tracker-type case = ' + & ,trkrinfo%want_oci + write (6,105) ' Flag for whether or not vitals will be written ' + & ,'out = ',trkrinfo%out_vit + write (6,109) ' Flag for whether or not a land mask will be ' + & ,'used for tcgen candidate low filtering = ' + & ,trkrinfo%use_land_mask + write (6,110) ' Flag for input data type (grib or netcdf) = ' + & ,trkrinfo%inp_data_type + write (6,107) ' Flag for which GRIB version (1 or 2) the input' + & ,' data will be in = ',trkrinfo%gribver + write (6,108) ' Flag for input GRIB2 JPDTN (0 or 1) = ' + & ,trkrinfo%g2_jpdtn + write (6,112) ' Flag for input GRIB2 MSLP ID (1 or 192) = ' + & ,trkrinfo%g2_mslp_parm_id + write (6,114) ' Flag for input GRIB1 MSLP ID (102 or 130) = ' + & ,trkrinfo%g1_mslp_parm_id + write (6,116) ' Flag for input GRIB1 sfcwind level type ' + & ,'(PDS Octet 10... should be 1 or 105) = ' + & ,trkrinfo%g1_sfcwind_lev_typ + write (6,118) ' Flag for input GRIB1 sfcwind level value ' + & ,'(PDS Octets 11 & 12... usually 0 or 10) = ' + & ,trkrinfo%g1_sfcwind_lev_val + + 101 format (a31,f7.2) + 102 format (a16,a7) + 103 format (a31,f7.4) + 104 format (a19,a8) + 106 format (a46,a41,L1) + 105 format (a48,a6,a1) + 109 format (a45,a41,a1) + 110 format (a45,a6) + 107 format (a47,a19,i1) + 108 format (a39,i2) + 112 format (a43,i4) + 114 format (a45,i4) + 116 format (a41,a39,i4) + 118 format (a42,a43,i4) + 120 format (a44,a29,a1) + 122 format (a40,a26,a1) + 123 format (a36,a44,f7.1) + + print *,' ' + print *,' ' + print *,'Values read in from netcdflist namelist: ' + print *,' ' + write (6,300) netcdfinfo%num_netcdf_vars ! Total *possible* + ! number of input NetCDF variables, + ! including those that are included + ! in the input file and those that + ! are not. + write (6,370) netcdfinfo%netcdf_filename ! full path filename + write (6,301) + write (6,302) netcdfinfo%rv850name ! 850 mb rel vort + write (6,304) netcdfinfo%rv700name ! 700 mb rel vort + write (6,306) netcdfinfo%u850name ! 850 mb u-comp + write (6,308) netcdfinfo%v850name ! 850 mb v-comp + write (6,310) netcdfinfo%u700name ! 700 mb u-comp + write (6,312) netcdfinfo%v700name ! 700 mb v-comp + write (6,314) netcdfinfo%z850name ! 850 mb gp height + write (6,316) netcdfinfo%z700name ! 700 mb gp height + write (6,318) netcdfinfo%mslpname ! mslp + write (6,320) netcdfinfo%usfcname ! near-sfc u-comp + write (6,322) netcdfinfo%vsfcname ! near-sfc v-comp + write (6,324) netcdfinfo%u500name ! 500 mb u-comp + write (6,326) netcdfinfo%v500name ! 500 mb v-comp + write (6,328) netcdfinfo%tmean_300_500_name !Mean T @ 300-500 mb + write (6,330) netcdfinfo%z500name ! 500 mb gp height + write (6,332) netcdfinfo%z200name ! 200 mb gp height + write (6,334) netcdfinfo%lmaskname ! Land mask + write (6,336) netcdfinfo%z900name ! 900 mb gp height + write (6,338) netcdfinfo%z800name ! 800 mb gp height + write (6,340) netcdfinfo%z750name ! 750 mb gp height + write (6,342) netcdfinfo%z650name ! 650 mb gp height + write (6,344) netcdfinfo%z600name ! 600 mb gp height + write (6,346) netcdfinfo%z550name ! 550 mb gp height + write (6,348) netcdfinfo%z450name ! 450 mb gp height + write (6,350) netcdfinfo%z400name ! 400 mb gp height + write (6,352) netcdfinfo%z350name ! 350 mb gp height + write (6,354) netcdfinfo%z300name ! 300 mb gp height + write (6,355) netcdfinfo%time_name ! Name of time variable + ! (usually it is "time") + write (6,356) netcdfinfo%lon_name ! longitudes + write (6,358) netcdfinfo%lat_name ! latitudes + write (6,359) netcdfinfo%time_units ! This will be either "days" + ! or "hours". If it's "hours", + ! then all the time data values + ! are for hours since the initial + ! time. Same thing for "days", + ! however if it is "days", then + ! know that a value of 0.25 will + ! be the same as a 6-hour lead + ! time. + + 300 format ('Total *possible* number of input NetCDF variables,' + & ,/,' including those that are included in the input' + & ,/,' NetCDF file and those that are not = ',i4) + 370 format ('Input NetCDF filename = ',a180) + 301 format (' ',/ + & ,'List of NetCDF variables follows. A value of X ',/ + & ,'indicates the variable is not included in the ',/ + & ,'input file and no attempt will be made to read in ',/ + & ,'that variable: ',/,' ') + 302 format ('NetCDF variable name for 850 mb vort = ',a30) + 304 format ('NetCDF variable name for 700 mb vort = ',a30) + 306 format ('NetCDF variable name for 850 mb u-comp = ',a30) + 308 format ('NetCDF variable name for 850 mb v-comp = ',a30) + 310 format ('NetCDF variable name for 700 mb u-comp = ',a30) + 312 format ('NetCDF variable name for 700 mb v-comp = ',a30) + 314 format ('NetCDF variable name for 850 mb gp height = ',a30) + 316 format ('NetCDF variable name for 700 mb gp height = ',a30) + 318 format ('NetCDF variable name for MSLP = ',a30) + 320 format ('NetCDF variable name for near-sfc u-comp = ',a30) + 322 format ('NetCDF variable name for near-sfc v-comp = ',a30) + 324 format ('NetCDF variable name for 500 mb u-comp = ',a30) + 326 format ('NetCDF variable name for 500 mb v-comp = ',a30) + 328 format ('NetCDF variable name for 300-500 mb Mean T = ',a30) + 330 format ('NetCDF variable name for 500 mb gp height = ',a30) + 332 format ('NetCDF variable name for 200 mb gp height = ',a30) + 334 format ('NetCDF variable name for land-sea mask = ',a30) + 336 format ('NetCDF variable name for 900 mb gp height = ',a30) + 338 format ('NetCDF variable name for 800 mb gp height = ',a30) + 340 format ('NetCDF variable name for 750 mb gp height = ',a30) + 342 format ('NetCDF variable name for 650 mb gp height = ',a30) + 344 format ('NetCDF variable name for 600 mb gp height = ',a30) + 346 format ('NetCDF variable name for 550 mb gp height = ',a30) + 348 format ('NetCDF variable name for 450 mb gp height = ',a30) + 350 format ('NetCDF variable name for 400 mb gp height = ',a30) + 352 format ('NetCDF variable name for 350 mb gp height = ',a30) + 354 format ('NetCDF variable name for 300 mb gp height = ',a30) + 355 format ('NetCDF variable name for time = ',a30) + 356 format ('NetCDF variable name for longitudes = ',a30) + 358 format ('NetCDF variable name for latitudes = ',a30) + 359 format ('NetCDF time value (hours|days) = ',a30) + + print *,' ' + print *,' ' + print *,'Values read in from parmpreflist namelist: ' + print *,' ' + write (6,402) user_wants_to_track_zeta850 + write (6,404) user_wants_to_track_zeta700 + write (6,406) user_wants_to_track_wcirc850 + write (6,408) user_wants_to_track_wcirc700 + write (6,410) user_wants_to_track_gph850 + write (6,412) user_wants_to_track_gph700 + write (6,414) user_wants_to_track_mslp + write (6,416) user_wants_to_track_wcircsfc + write (6,418) user_wants_to_track_zetasfc + write (6,420) user_wants_to_track_thick500850 + write (6,422) user_wants_to_track_thick200500 + write (6,424) user_wants_to_track_thick200850 + + 402 format ('user_wants_to_track_zeta850= ',a2) + 404 format ('user_wants_to_track_zeta700= ',a2) + 406 format ('user_wants_to_track_wcirc850= ',a2) + 408 format ('user_wants_to_track_wcirc700= ',a2) + 410 format ('user_wants_to_track_gph850= ',a2) + 412 format ('user_wants_to_track_gph700= ',a2) + 414 format ('user_wants_to_track_mslp= ',a2) + 416 format ('user_wants_to_track_wcircsfc= ',a2) + 418 format ('user_wants_to_track_zetasfc= ',a2) + 420 format ('user_wants_to_track_thick500850= ',a2) + 422 format ('user_wants_to_track_thick200500= ',a2) + 424 format ('user_wants_to_track_thick200850= ',a2) + + print *,' ' + print *,'Values read in from phaseinfo namelist: ' + write (6,211) phaseflag,phasescheme + write (6,212) wcore_depth + 211 format ('Storm phase flag = ',a1,' Phase scheme = ',a4) + 212 format ('Storm phase, warm core depth (wcore_depth) = ',f7.2) + + print *,' ' + print *,'Values read in from structinfo namelist: ' + write (6,93) structflag + write (6,95) ikeflag + 93 format ('Structure flag = ',a1) + 95 format ('IKE flag = ',a1) + + print *,' ' + print *,'Values read in for grib file name from fnameinfo' + & ,' namelist: ' + write (6,131) gmodname + write (6,133) rundescr + write (6,135) atcfdescr + 131 format ('Model name description = gmodname = ',a4) + 133 format ('Forecast run description = rundescr = ',a40) + 135 format ('Optional ATCF / Storm name description = atcfdescr = ' + & ,a40) + + print *,' ' + print *,'Value read in for verbose output for most output:' + write (6,141) verb + 141 format ('Value read in for verbose flag = verb = ',i2) + + print *,' ' + print *,'Value read in for verbose output for grib2 output:' + write (6,142) verb_g2 + 142 format ('Value read in for GRIB2 verbose flag = verb_g2 = ',i2) + + print *,' ' + print *,'Values read in from waitinfo namelist:' + write (6,151) use_waitfor + write (6,152) wait_min_age + write (6,153) wait_min_size + write (6,154) wait_max_wait + write (6,155) wait_sleeptime + if(len_trim(per_fcst_command)>0) then + write (6,156) trim(per_fcst_command) + else +c No command specified, so disable the feature + use_per_fcst_command='n' + endif + 151 format ('Flag for input file waiting = use_waitfor = ',a1) + 152 format ('min age (time in seconds since last mod) = ' + & ,'wait_min_age = ',i8) + 153 format ('min file size in bytes = wait_min_size = ',i12) + 154 format ('max number of seconds to wait for each file = ' + & ,'wait_max_wait = ',i6) + 155 format ('number of seconds to sleep between checks = ' + & ,'wait_sleeptime = ',i6) + 156 format ('command to run after every forecast time = "',A,'"') +c + if (use_waitfor == 'y') then + if (inp%file_seq == 'multi') then + continue + else + print *,' ' + print *,'!!! ERROR: The use_waitfor flag is set to "y".' + print *,' This requires that the inp%file_seq flag be' + print *,' set to "multi", but you have specified ' + print *,' something else. ' + print *,' inp%file_seq = ',inp%file_seq + print *,' STOPPING....' + print *,' ' + STOP 95 + endif + endif +c + endif + return + end +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_fhours (ifhmax) +c +c ABSTRACT: This subroutine reads in a text file that contains the +c forecast times that will be read in. The format of the file is +c in "MMMMM", i.e., minutes, for example, for a forecast going out +c to 120h, the file would look like this: +c +c For reference, here +c are the times that +c match up with the +c minutes on the left: +c +c 1 0 0:00 +c 2 240 4:00 +c 3 270 4:30 +c 4 300 5:00 +c 5 330 5:30 +c 6 360 6:00 +c 7 600 10:00 +c 8 630 10:30 +c 9 660 11:00 +c 10 690 11:30 +c 11 720 12:00 +c 12 960 16:00 +c 13 990 16:30 +c . . . +c . . . +c . . . +c 87 7200 120:00 +c +c Note that we are now allowing for sub-hourly time intervals. +c + USE tracked_parms + USE verbose_output + + implicit none +c + integer, parameter :: iunit_fh=15 + integer itmphrs(750),itmpmins(750),input_mins(750),itmpltix(750) + integer ifhmax,inphr,inpmin,ict,i,ifa,ifma,icma,ira,inpltix,ila + real xminfract + + itmphrs = -99 + itmpmins = -99 + + if (allocated(ifhours)) deallocate (ifhours) + if (allocated(iftotalmins)) deallocate (iftotalmins) + if (allocated(ifclockmins)) deallocate (ifclockmins) + if (allocated(fhreal)) deallocate (fhreal) + if (allocated(ltix)) deallocate (ltix) + + ict = 0 + do while (.true.) + + if ( verb .ge. 3 ) then + print *,'Top of while loop in read_fhours' + endif + + read (iunit_fh,85,end=130) inpltix,inpmin + write (6,85) inpltix,inpmin + + if (inpmin >= 0 .and. inpmin < 150000) then + ict = ict + 1 + itmpltix(ict) = inpltix + itmphrs(ict) = inpmin / 60 + itmpmins(ict) = mod(inpmin,60) + input_mins(ict) = inpmin + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Input minutes not between 0 and 150000' + print *,'!!! inpmin= ',inpmin + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + if ( verb .ge. 3 ) then + print *,'readloop, ict= ',ict,' inpmin= ',inpmin + endif + + enddo + + 130 continue + + ifhmax = ict + + 85 format (i4,1x,i5) + + if ( verb .ge. 3 ) then + print *,' ' + endif + + allocate (ifhours(ifhmax),stat=ifa) + allocate (iftotalmins(ifhmax),stat=ifma) + allocate (ifclockmins(ifhmax),stat=icma) + allocate (fhreal(ifhmax),stat=ira) + allocate (ltix(ifhmax),stat=ila) + if (ifa /= 0 .or. ifma /= 0 .or. icma /= 0 .or. ira /= 0 .or. + & ila /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_fhours allocating either ifhours,' + print *,'!!! iftotalmins, ifclockmins or fhreal.' + print *,'!!! ifa = ',ifa,' ifma= ',ifma,' ira= ',ira + print *,'!!! icma= ',icma,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + do i = 1,ifhmax + + ltix(i) = itmpltix(i) + xminfract = float(itmpmins(i)) / 60. + fhreal(i) = float(itmphrs(i)) + xminfract + ifhours(i) = itmphrs(i) + ifclockmins(i) = itmpmins(i) + iftotalmins(i) = input_mins(i) + + if (i > 1) then + if (fhreal(i) > fhreal(i-1)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: In read_fhours, the time read in ' + print *,'!!! is not greater than the previous time.' + print *,'!!! i= ',i + print *,'!!! fhreal(i)= ',fhreal(i) + print *,'!!! fhreal(i-1)= ',fhreal(i-1) + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + endif + + if ( verb .ge. 3 ) then + write (6,87) i,ltix(i),iftotalmins(i),fhreal(i),ifhours(i) + & ,ifclockmins(i) + endif + + enddo + + 87 format (1x,'i= ',i3,' input lead time index= ',i4,' minutes= ' + & ,i5,' real_lead_time= ',f6.2,' clock_lead_time= ',i3,':' + & ,i2) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_tcv_card1 (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + logical(1) :: vit_file_exists + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + + ! Check to see if the TC Vitals file exists. If so, then open it + ! using the unit specified in lucard. + + inquire (file="tcvit_rsmc_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for existing, RSMC-numbered' + & ,' storms exists and will be opened with ' + & ,' unit= lucard= ',lucard + endif + + open (unit=lucard,file="tcvit_rsmc_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_rsmc_storms.txt has ' + print *,' been opened with unit= lucard= ',lucard + endif + + else + + if (trkrinfo%type == 'tracker') then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card. The fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. This TC Vitals file is needed for' + print *,'!!! a tracker case. Check to see that the ' + print *,'!!! TC Vitals file exists in this directory and' + print *,'!!! is named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING....' + print *,'!!! ' + iret=99 + return + endif + + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! NOTE: In read_tcv_card, the fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. While this TC Vitals file is ' + print *,'!!! needed for tracker cases, you are running' + print *,'!!! either a midlat or tcgen case here, and so ' + print *,'!!! that file is not needed... although you can ' + print *,'!!! run with using tc vitals for those genesis' + print *,'!!! cases if you want to. You may want to check' + print *,'!!! and make sure this is what you intend. If ' + print *,'!!! you do want to use it, the TC Vitals file ' + print *,'!!! should be in this directory and it should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! ' + endif + + endif + + endif + + ii=1 + + if (vit_file_exists) then + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + 801 continue + endif + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + + maxstorm = numtcv + + if (maxstorm > 0) then + continue + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING...' + print *,'!!! ' + iret=99 + return + endif + endif + + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! Stopping....' + print *,' ' + endif + + iret = 99 + return + + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card opening rsmc TC vitals' + print *,'!!! file named tcvit_rsmc_storms.txt. A file with' + print *,'!!! that name needs to be in your working directory.' + print *,'!!! It should contain the input TC vitals for ' + print *,'!!! already-existing storms that have rsmc-issued' + print *,'!!! storm IDs.' + endif + + iret = 97 + return + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine read_gen_vitals1(lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + logical(1) :: vit_file_exists + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + + ! Check to see if the genesis TC Vitals file exists. If so, then + ! open it using the unit specified in lgvcard. + + inquire (file="tcvit_genesis_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for genesis' + & ,' storms exists and will be opened with ' + & ,' unit= lgvcard= ',lgvcard + endif + + open (unit=lgvcard,file="tcvit_genesis_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_genesis_storms.txt has ' + print *,' been opened with unit= lgvcard= ',lgvcard + endif + + endif + + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + + ii = numtcv + 1 + + if (vit_file_exists) then + + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1 + & ,1x,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + endif + + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals opening genesis vitals' + print *,'!!! file named tcvit_genesis_storms.txt' + endif + + iret = 97 + return + + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just read the +c grib file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE tracked_parms; USE inparms + USE verbose_output; USE params; USE grib_mod + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + + type(gribfield) :: gfld,prevfld,holdgfld + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1), allocatable :: lb(:) + logical :: unpack=.true. + logical :: open_grb=.false. + CHARACTER(len=8) :: pabbrev + integer,dimension(200) :: jids,jpdt,jgdt + integer, parameter :: jf=40000000 + integer :: listsec1(13) + integer pdt_4p0_vert_level,pdt_4p0_vtime + real xhold,xlondiff,xlatdiff,temp,firstval,lastval + real, allocatable :: f(:) + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer jpds(200),jgds(200),igetpds(200),igetgds(200) + integer, intent(in) :: ifh + integer, intent(out) :: imax,jmax + integer iia,ija,ila,midi,midj,i,j,iix,jix,ifa,iret + integer iscanflag,iggret,kf,k,lugb,lugi,jskp,jdisc + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 + + iggret = 0 + + allocate (lb(jf),stat=ila); allocate (f(jf),stat=ifa) + if (ila /= 0 .or. ifa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating either lb or f' + print *,'!!! ila = ',ila,' ifa= ',ifa + endif + iggret = 97 + return + endif + + if (trkrinfo%gribver == 2) then + + ! Search for a record from a GRIB2 file + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 ! meteorological products + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for Temperature or GP Height by production template.... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + ! Request a record on a lat/lon grid. + + jgdtn = 0 + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpdt(8) = 0 + jpdt(9) = iftotalmins(ifh) + else + jpdt(8) = 1 + jpdt(9) = ifhours(ifh) + endif + + if (verb >= 3) then + print *,'before getgb2 call, lugb= ',lugb,' lugi= ',lugi + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + if ( iret.ne.0) then + print *,' ' + print *,' ERROR: getgb2 error in getgridinfo = ',iret + print *,' FATAL ERROR: cannot proceed without info ' + print *,' from getgridinfo. STOPPING....' + stop 95 + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if ( verb_g2 .ge. 1 ) then + print *,' ' + print *,' -- BEGIN getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'PDT num= gfld%ipdtnum= ',gfld%ipdtnum + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + imax = gfld%igdtmpl(8) + jmax = gfld%igdtmpl(9) + dx = float(gfld%igdtmpl(17))/1.e6 + dy = float(gfld%igdtmpl(17))/1.e6 + kf = gfld%ngrdpts + + holdgfld = gfld + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + + endif + + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' -- END getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' ' + print *,' ' + + endif + + need_to_flip_lons = .false. + + iscanflag = gfld%igdtmpl(19) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(gfld%igdtmpl(12))/1.e6 + glatmax = float(gfld%igdtmpl(15))/1.e6 + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(gfld%igdtmpl(15))/1.e6 + glatmax = float(gfld%igdtmpl(12))/1.e6 + need_to_flip_lats = .false. + endif + + glonmin = float(gfld%igdtmpl(13))/1.e6 + glonmax = float(gfld%igdtmpl(16))/1.e6 + + if (verb .ge. 3) then + print *,'In getgridinfo: glatmin= ',glatmin + print *,' glatmax= ',glatmax + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + + else + + !------------------------------------------ + ! Search for a record from a GRIB1 file + !------------------------------------------ + + jpds = -1 + jgds = -1 + + jgds(1) = 0 ! Request a record that's on a lat/lon grid + + if ( verb .ge. 3 ) then + print *,'before getgb in getgridinfo, ifh= ',ifh + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* Forecast hour: ',i4,':',i2.2) + print *,' ifhours(ifh)= ',ifhours(ifh) + print *,' iftotalmins(ifh)= ',iftotalmins(ifh) + endif + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + j=0 + +c jpds(14) = 0 ! test +c + write(*,980) jpds(1),jpds(2) + write(*,981) jpds(3),jpds(4) + write(*,982) jpds(5),jpds(6) + write(*,983) jpds(7),jpds(8) + write(*,984) jpds(9),jpds(10) + write(*,985) jpds(11),jpds(12) + write(*,986) jpds(13),jpds(14) + write(*,987) jpds(15),jpds(16) + write(*,988) jpds(17),jpds(18) + write(*,989) jpds(19),jpds(20) + write(*,990) jpds(21),jpds(22) + write(*,991) jpds(23),jpds(24) + write(*,992) jpds(25) + write(*,880) jgds(1),jgds(2) + write(*,881) jgds(3),jgds(4) + write(*,882) jgds(5),jgds(6) + write(*,883) jgds(7),jgds(8) + write(*,884) jgds(9),jgds(10) + write(*,885) jgds(11),jgds(12) + write(*,886) jgds(13),jgds(14) + write(*,887) jgds(15),jgds(16) + write(*,888) jgds(17),jgds(18) + write(*,889) jgds(19),jgds(20) + write(*,890) jgds(21),jgds(22) + + 980 format(' jpds(1) = ',i7,' jpds(2) = ',i7) + 981 format(' jpds(3) = ',i7,' jpds(4) = ',i7) + 982 format(' jpds(5) = ',i7,' jpds(6) = ',i7) + 983 format(' jpds(7) = ',i7,' jpds(8) = ',i7) + 984 format(' jpds(9) = ',i7,' jpds(10) = ',i7) + 985 format(' jpds(11) = ',i7,' jpds(12) = ',i7) + 986 format(' jpds(13) = ',i7,' jpds(14) = ',i7) + 987 format(' jpds(15) = ',i7,' jpds(16) = ',i7) + 988 format(' jpds(17) = ',i7,' jpds(18) = ',i7) + 989 format(' jpds(19) = ',i7,' jpds(20) = ',i7) + 990 format(' jpds(21) = ',i7,' jpds(22) = ',i7) + 991 format(' jpds(23) = ',i7,' jpds(24) = ',i7) + 992 format(' jpds(25) = ',i7) + 880 format(' jgds(1) = ',i7,' jgds(2) = ',i7) + 881 format(' jgds(3) = ',i7,' jgds(4) = ',i7) + 882 format(' jgds(5) = ',i7,' jgds(6) = ',i7) + 883 format(' jgds(7) = ',i7,' jgds(8) = ',i7) + 884 format(' jgds(9) = ',i7,' jgds(10) = ',i7) + 885 format(' jgds(11) = ',i7,' jgds(12) = ',i7) + 886 format(' jgds(13) = ',i7,' jgds(14) = ',i7) + 887 format(' jgds(15) = ',i7,' jgds(16) = ',i7) + 888 format(' jgds(17) = ',i7,' jgds(18) = ',i7) + 889 format(' jgds(19) = ',i7,' jgds(20) = ',i7) + 890 format(' jgds(20) = ',i7,' jgds(22) = ',i7) + + print *,'lugb= ',lugb,' lugi= ',lugi + print *,'before ggi getgb jpds(14) = ',jpds(14) + print *,'before ggi getgb jgds(1) = ',jgds(1) + + call getgb(lugb,lugi,jf,j,jpds,jgds, + & kf,k,igetpds,igetgds,lb,f,iret) + + if (iret.ne.0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo calling getgb' + print *,'!!! Return code from getgb = iret = ',iret + endif + + iggret = iret + else + iggret=0 + imax = igetgds(2) + jmax = igetgds(3) + dx = float(igetgds(9))/1000. + dy = float(igetgds(10))/1000. + endif + +c write(*,780) igetpds(1),igetpds(2) +c write(*,781) igetpds(3),igetpds(4) +c write(*,782) igetpds(5),igetpds(6) +c write(*,783) igetpds(7),igetpds(8) +c write(*,784) igetpds(9),igetpds(10) +c write(*,785) igetpds(11),igetpds(12) +c write(*,786) igetpds(13),igetpds(14) +c write(*,787) igetpds(15),igetpds(16) +c write(*,788) igetpds(17),igetpds(18) +c write(*,789) igetpds(19),igetpds(20) +c write(*,790) igetpds(21),igetpds(22) +c write(*,791) igetpds(23),igetpds(24) +c write(*,792) igetpds(25) +c write(*,680) igetgds(1),igetgds(2) +c write(*,681) igetgds(3),igetgds(4) +c write(*,682) igetgds(5),igetgds(6) +c write(*,683) igetgds(7),igetgds(8) +c write(*,684) igetgds(9),igetgds(10) +c write(*,685) igetgds(11),igetgds(12) +c write(*,686) igetgds(13),igetgds(14) +c write(*,687) igetgds(15),igetgds(16) +c write(*,688) igetgds(17),igetgds(18) +c write(*,689) igetgds(19),igetgds(20) +c write(*,690) igetgds(21),igetgds(22) +c +c 780 format(' kpds(1) = ',i7,' kpds(2) = ',i7) +c 781 format(' kpds(3) = ',i7,' kpds(4) = ',i7) +c 782 format(' kpds(5) = ',i7,' kpds(6) = ',i7) +c 783 format(' kpds(7) = ',i7,' kpds(8) = ',i7) +c 784 format(' kpds(9) = ',i7,' kpds(10) = ',i7) +c 785 format(' kpds(11) = ',i7,' kpds(12) = ',i7) +c 786 format(' kpds(13) = ',i7,' kpds(14) = ',i7) +c 787 format(' kpds(15) = ',i7,' kpds(16) = ',i7) +c 788 format(' kpds(17) = ',i7,' kpds(18) = ',i7) +c 789 format(' kpds(19) = ',i7,' kpds(20) = ',i7) +c 790 format(' kpds(21) = ',i7,' kpds(22) = ',i7) +c 791 format(' kpds(23) = ',i7,' kpds(24) = ',i7) +c 792 format(' kpds(25) = ',i7) +c 680 format(' kgds(1) = ',i7,' kgds(2) = ',i7) +c 681 format(' kgds(3) = ',i7,' kgds(4) = ',i7) +c 682 format(' kgds(5) = ',i7,' kgds(6) = ',i7) +c 683 format(' kgds(7) = ',i7,' kgds(8) = ',i7) +c 684 format(' kgds(9) = ',i7,' kgds(10) = ',i7) +c 685 format(' kgds(11) = ',i7,' kgds(12) = ',i7) +c 686 format(' kgds(13) = ',i7,' kgds(14) = ',i7) +c 687 format(' kgds(15) = ',i7,' kgds(16) = ',i7) +c 688 format(' kgds(17) = ',i7,' kgds(18) = ',i7) +c 689 format(' kgds(19) = ',i7,' kgds(20) = ',i7) +c 690 format(' kgds(20) = ',i7,' kgds(22) = ',i7) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,' dx= ',dx,' dy= ',dy + endif + + +c ---------------------------------------------------------------- +c Get boundaries of the data grid. NOTE: gds(4) is referred to in +c GRIB documenatation as the "Latitude of origin", which might +c imply "minimum Latitude". However, for the grids that we'll be +c using in this program, the "Latitude of origin" will be listed +c under gds(4) as the northernmost point (eg., in MRF, +c gds(4) = 90), so for this program, use gds(4) as your max lat, +c and gds(7) as your min lat. However, in case NCEP, UKMET or +c ECMWF change their convention and begin flipping their grids, a +c check is made to make sure that the max lat is not less than the +c min lat. +c +c BUGFIX (August, 2001): It is possible to have an input grid +c which goes from south to north (such as NAVGEM). In this case, +c we flip the data in subroutine conv1d2d_real. However, the max +c and min latitudes listed in the GRIB GDS will be confused, so we +c need to check the value of the GRIB scanning mode flag here. + + need_to_flip_lons = .false. + + iscanflag = igetgds(11) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(igetgds(4))/1000. + glatmax = float(igetgds(7))/1000. + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(igetgds(7))/1000. + glatmax = float(igetgds(4))/1000. + need_to_flip_lats = .false. + endif + + glonmin = float(igetgds(5))/1000. + glonmax = float(igetgds(8))/1000. + + endif + +c After this point in this subroutine, nothing is GRIB1 / GRIB2 +c specific, so it does not need to be within the if/then +c statement above that differentiated between GRIB / GRIB2. + +c17Jul2014 if (glonmin < 0.0) glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax < 0.0) glonmax = 360. - abs(glonmax) + + if (glonmin >= 0.0 .and. glonmax >= 0.0) then + if (glonmin > glonmax) then + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Badly notated longitude boundaries in ' + print *,' GRIB PDS, because the min longitude ' + print *,' (glonmin) is greater than the max ' + print *,' longitude (glonmax) where both longitudes' + print *,' are greater than 0.' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + print *,' !!! STOPPING....' + stop 98 + endif + endif + elseif (glonmin < 0.0 .and. glonmax >= 0.0) then + ! An example of this is the MPAS data, which starts and ends + ! at the dateline and is specified as glonmin=-179.875, + ! glonmax=179.875. Convert to be positive and go from + ! 180.125 to 539.875. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0, glonmax > 0, so glonmin' + print *,' will be converted to be > 0 and 360 will' + print *,' be added to glonmax.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. + abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin < 0.0 .and. glonmax < 0.0) then + ! Examples of this are GFDL and HWRF. In this case, make + ! both glonmin and glonmax positive. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0 and glonmax < 0, so both' + print *,' will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin >= 0.0 .and. glonmax < 0.0) then + ! An example of this is the GFS data, which goes from + ! glonmin=0.0 to glonmax=-0.5. Convert it here to go + ! from glonmin=0.0 to glonmax=359.5 + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is >= 0 and glonmax < 0, so' + print *,' glonmax will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + endif + +c17Jul2014 if (glonmin < 0.0) then +c17Jul2014 glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax <= 0.0) then +c17Jul2014 glonmax = 360. - abs(glonmax) +c17Jul2014 else +c17Jul2014 glonmax = 360 + abs(glonmax) +c17Jul2014 endif +c17Jul2014 endif + + if (glatmax < glatmin) then + temp = glatmax + glatmax = glatmin + glatmin = temp + endif + + if (glonmin > 200.0 .and. glonmin <= 360.) then + if (glonmax < 50.) then + ! Likely GM-wrapping in current record + glonmax = glonmax + 360. + endif + endif +c + if ( verb .ge. 3 ) then + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + print *,' ' + print *,'NOTE: For regional grids, valid data points might' + print *,'NOT extend all the way to the gds-defined grid ' + print *,'boundary, due to the fact that data have been ' + print *,'interpolated from a NPS or Lamb-Conf grid onto a ' + print *,'lat/lon grid. This program checks the logical ' + print *,'bitmap for valid data points, but just keep this in' + print *,'mind if trying to debug errors that occur near the' + print *,'grid boundaries for regional models.' + endif + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glat)) deallocate(glat) + if (allocated(glon)) deallocate(glon) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + endif + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + + iggret = 96 + return + endif + + do j=1,jmax + glat(j) = glatmax - (j-1)*dy + enddo + do i=1,imax + glon(i) = glonmin + (i-1)*dx + enddo + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + +c -------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have +c forgotten to change the input grid bounds from a global grid +c run). Modify the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just query the +c netcdf file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE inparms + USE verbose_output; USE netcdf_parms + + implicit none +c + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (datecard) inp + + logical(1) :: need_to_flip_lats,need_to_flip_lons + real xhold,xlondiff,xlatdiff + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer iscanflag,iggret + integer, intent(in) :: ncfile_id + integer, intent(out) :: imax,jmax + integer :: iia,ija,midi,midj,i,j,iix,jix +c + + iggret = 0 + + call get_ncdim1(ncfile_id,netcdfinfo%lon_name,imax) + call get_ncdim1(ncfile_id,netcdfinfo%lat_name,jmax) + + if (allocated(tmplon)) deallocate (tmplon) + if (allocated(tmplat)) deallocate (tmplat) + allocate (tmplon(imax),stat=iia) + allocate (tmplat(jmax),stat=ija) + if (iia /= 0 .or. ija /= 0) then + print *,' ' + print *,'!!! ERROR in sub getgridinfo_netcdf allocating arrays.' + print *,'!!! iia = ',iia,' ija= ',ija + iggret = 94 + return + endif + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + endif + + call get_var1_double (ncfile_id,netcdfinfo%lon_name,imax,tmplon) + call get_var1_double (ncfile_id,netcdfinfo%lat_name,jmax,tmplat) + +c Compute the dx and dy by picking values out of the middle of +c the lat and lon arrays.... + + midi = imax/2 + midj = jmax/2 + + dx = abs(tmplon(midi) - tmplon(midi-1)) + dy = abs(tmplat(midj) - tmplat(midj-1)) + + if (verb .ge. 1) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,'dx= ',dx,' dy= ',dy + print *,' ' + write (6,112) midi,dx + write (6,113) midj,dy + + 112 format(1x,' DX: midi= ',i4,' dx= ',f8.4) + 113 format(1x,' DY: midj= ',i4,' dy= ',f8.4) + endif + + +c ------------------------------------------------------------------ +c Get boundaries of the data grid. Note that it is possible to have +c an input grid which goes from south to north (in fact, it appears +c that many NetCDF files are constructed this way). Keep in mind, +c however, that the tracker has been written such that point (1,1) +c should be the upper-leftmost point on the grid, while point +c (imax,jmax) should be the lower-rightmost point. If we check and +c find that we're dealing with data that instead starts from the +c south and increases northward, we flip the data in subroutine +c conv1d2d_real. Similarly here, we make sure to test so that when +c we are done in this routine, glatmax refers to the northernmost +c latitude and glatmin the southernmost latitude. + + if (tmplon(imax) > tmplon(1)) then + glonmin = tmplon(1) + glonmax = tmplon(imax) + else + glonmin = tmplon(imax) + glonmax = tmplon(1) + endif + + if (tmplat(1) > tmplon(jmax)) then + glatmax = tmplat(1) + glatmin = tmplat(jmax) + else + glatmax = tmplat(jmax) + glatmin = tmplat(1) + endif + + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glon)) deallocate (glon) + if (allocated(glat)) deallocate (glat) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + iggret = 96 + return + endif + + ! If the lat or lon grids are flipped (i.e., the lats increase + ! from south to north, or the lons increase westward), then we + ! will need to flip both the data arrays as well as the arrays + ! that are holding the values of the lats and lons.... + + need_to_flip_lats = .false. + need_to_flip_lons = .false. + + if (tmplat(1) > tmplon(jmax)) then + do j=1,jmax + glat(j) = tmplat(j) + enddo + else + do j=1,jmax + jix = jmax - j + 1 + glat(jix) = tmplat(j) + enddo + need_to_flip_lats = .true. + endif + + if (tmplon(imax) > tmplon(1)) then + do i=1,imax + glon(i) = tmplon(i) + enddo + else + do i=1,imax + iix = imax - i + 1 + glon(iix) = tmplon(i) + enddo + need_to_flip_lons = .true. + endif + +c do i = 1,imax +c print *,'i= ',i,' glon(i)= ',glon(i) +c enddo +c do j = 1,jmax +c print *,'j= ',j,' glat(j)= ',glat(j) +c enddo + +c --------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have forgotten +c to change the input grid bounds from a global grid run). Modify +c the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) +c +c ABSTRACT: The purpose of this subroutine is to read the "time" +c dimension and "time data" from the NetCDF file so that we know +c how many time levels there are and what those time levels are. +c One reason for doing this is that some models, like the GFDL +c FV3, do not output hour 0 data, so we need to check this first +c before running through the tracking processing for the various +c hours. We will take the list of hours read in here directly from +c the NetCDF file and compare that against the *requested* list of +c forecast hours that the user has entered. The user might not be +c aware that there is no hour 0 data for a given model. We compare +c these two lists of forecast hours and then write a message if +c there is a lead time that is not in the NetCDF file. +c +c INPUT: +c ncfile character name of NetCDF file +c ncfile_id integer id associated with NetCDF file after open +c ifhmax integer max number of lead times that the user has +c requested on the input lead times data file. This +c value was set in subroutine read_fhours. +c netcdfinfo variable of user-defined type netcdfstuff (from +c module netcdf_parms). +c +c OUTPUT: +c ncfile_tmax integer max number of lead times that are in the +c NetCDF file, as read in from this subroutine +c ncfile_has_hour0 character flag (y|n) that tells whether or not +c the input NetCDF data file actually has an hour0 +c record in it or not. +c + USE netcdf_parms; USE tracked_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo + + character :: ncfile*180,ncfile_has_hour0*1,match_check*1 + integer, intent(in) :: ncfile_id + integer, intent(out) :: ncfile_tmax + integer :: infta,k,m,n,ifhmax,irnhret,usertime +c + + irnhret = 0 + ncfile_has_hour0 = 'n' + + !----------------------------------------------------------- + ! First read the NetCDF file to get the number of time levels, + ! which will be returned in "ncfile_tmax".... + !----------------------------------------------------------- + + print *,' ' + print *,'in read_netcdf_hours...' + print *,'ncfile_id= ',ncfile_id + print *,'netcdfinfo%time_name= ',netcdfinfo%time_name + print *,'ncfile_tmax= ',ncfile_tmax + + call get_ncdim1(ncfile_id,netcdfinfo%time_name,ncfile_tmax) + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + print *,'Num netcdf time levs= ncfile_tmax= ',ncfile_tmax + endif + + if (allocated(netcdf_file_time_values)) then + deallocate (netcdf_file_time_values) + endif + + allocate (netcdf_file_time_values(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating' + print *,'!!! netcdf_file_time_values array. infta = ',infta + irnhret = 94 + return + endif + + + !----------------------------------------------------------- + ! Now read in the actual time values that are stored in the + ! NetCDF file.... + !----------------------------------------------------------- + + call get_var1_double (ncfile_id,netcdfinfo%time_name,ncfile_tmax + & ,netcdf_file_time_values) + + if (verb .ge. 1) then + do k = 1,ncfile_tmax + print *,'k= ',k,' netcdf_file_time_values(k)= ' + & ,netcdf_file_time_values(k) + enddo + endif + + !------------------------------------------------------------ + ! Now convert the NetCDF time values into minutes in order to + ! be able to compare with the user-requested list of lead + ! times. Remember that the NetCDF lead times will be listed + ! either as hours or as fractions of days. + !------------------------------------------------------------ + + if (allocated(nctotalmins)) then + deallocate (nctotalmins) + endif + + allocate (nctotalmins(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating ' + print *,'!!! nctotalmins array. infta = ',infta + irnhret = 94 + return + endif + + do k = 1,ncfile_tmax + + if (netcdfinfo%time_units == 'hours') then + nctotalmins(k) = int(netcdf_file_time_values(k)) * 60 + elseif (netcdfinfo%time_units == 'days') then + nctotalmins(k) = int(netcdf_file_time_values(k) * 60. * 24.) + else + print *,' ' + print *,'!!! ERROR: In read_netcdf_hours, the value of' + print *,' netcdfinfo%time_units is neither hours nor days.' + print *,' netcdfinfo%time_units= ',netcdfinfo%time_units + print *,' STOPPING....' + print *,' ' + stop 99 + endif + + if (verb .ge. 1) then + write (6,71) k,netcdf_file_time_values(k),nctotalmins(k) + endif + + enddo + + 71 format (1x,i5,' netcdf_file_time_values(k)= ',f8.4 + & ,' nctotalmins(k)= ',i10) + + !------------------------------------------------------------ + ! Now go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match + ! the two lists up. The big one to watch out for is whether + ! or not the NetCDF file actually has an hour 0 lead time. + !------------------------------------------------------------ + + userloop: do n = 1,ifhmax + + usertime = iftotalmins(n) + + match_check = 'n' + + netcdfloop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + if (verb .ge. 1) then + print *,'+++ Time match for usertime= ',usertime + endif + match_check = 'y' + endif + + enddo netcdfloop + + if (match_check == 'n') then + + if (usertime == 0) then + print *,' ' + print *,'Warning: For a NetCDF file, the user has requested' + print *,'to read in an hour 0 file, however a scan of the' + print *,'time data values in the NetCDF file indicates' + print *,'that there is no hour 0 data in this file. ' + print *,'We will substitute either missing values or ' + print *,'the values from the TC Vitals data in the ' + print *,'hour 0 record and then start searching at the ' + print *,'next lead time.' + ncfile_has_hour0 = 'n' + else + print *,' ' + print *,'!!! ERROR: For a NetCDF file, the user has' + print *,' requested to process a particular lead time that' + print *,' does not exist in the NetCDF list of time ' + print *,' values.' + print *,' n= ',n + print *,' usertime= iftotalmins(n)= ',iftotalmins(n) + print *,' STOPPING....' + stop 99 + endif + + elseif (match_check == 'y') then + + if (usertime == 0) then + if (verb .ge. 1) then + print *,' ' + print *,'+++ For the input NetCDF file, an hour0 data ' + print *,' record exists in the data file.' + endif + ncfile_has_hour0 = 'y' + endif + + endif + + enddo userloop +c + return + end +c +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_valid_point (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) +c +c ABSTRACT: This subroutine checks to see if the input lat/lon +c point is associated with four surrounding (i,j) locations that +c have valid data. The writing of this routine was prompted by the +c HFIP project in February, 2009. Some of their high resolution +c data for their inner nests contained grids that had been rotated +c from native map projections to regular lat/lon grids, but that +c rotation left "empty" spots on the lat/lon grid where there is +c no data. Then when searching in find_maxmin, we were running +c barnes iterations from these lat/lon locations where there was +c no data, which would give artificially low values at those +c lat/lon locations (because the barnes scheme would only include +c points that were relatively far away where there was valid data). +c So in this routine, we call subroutine fix_latlon_to_ij in order +c to get the nearest (i,j) coordinates, and then we check all of +c these points to make sure that valid data exist. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing in i-direction +c dy grid spacing in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c rlatt,rlont input lat/lon about which we will check the +c surrounding (i,j) locations for valid data. +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c icvpret return code from this routine. A value of 0 means that +c all is okay and the input point is surrounded by valid +c data. + + USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,ifix,jfix + integer ifilret,icvpret + character(*) cmaxmin + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real rlont,rlatt,xdum,gridpoint_maxmin + real dx,dy,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +c + call fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt + & ,xdum,ifix,jfix,gridpoint_maxmin,'checker' + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) + + if (ifilret /= 0) then + icvpret = 99 + return + endif + + if (valid_pt(ifix,jfix)) then + icvpret = 0 + else + icvpret = 99 + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.025) then + grfact = 20 + else if (grdspc > 0.025 .and. grdspc <= 0.05) then + grfact = 12 + else if (grdspc > 0.05 .and. grdspc <= 0.10) then + grfact = 6 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif + + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (1*grfact) + iend = ipfix + (2*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (2*grfact) + iend = ipfix + (1*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (1*grfact) + iend = ipfix + (1*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (2*grfact) + jend = jpfix + (1*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (1*grfact) + jend = jpfix + (2*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (1*grfact) + jend = jpfix + (1*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +c print *,' End of fix_latlon_to_ij, gridpoint_maxmin = ' +c & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine rvcal (imax,jmax,dlon,dlat,z,vp) +c +c ABSTRACT: This routine calculates the relative vorticity (zeta) +c from u,v on an evenly-spaced lat/lon grid. Centered finite +c differences are used on the interior points and one-sided +c differences are used on the boundaries. +c +c NOTE: There are 3 critical arrays in this subroutine, the first +c being zeta and the 2nd and 3rd being u and v. There is a +c critical difference in the array indexing for the levels. For +c zeta, the array is dimensioned with levels from 1 to 3, with +c 1 = 850, 2 = 700, 3 = sfc. However, there is an extra level +c for the winds, such that the level dimension goes 1 = 850, +c 2 = 700, 3 = 500, 4 = sfc. So we need to adjust for that in +c this routine. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE trig_vals; USE grid_bounds + USE verbose_output + + implicit none + + dimension cosfac(jmax),tanfac(jmax) + real tmpzeta(imax,jmax) + real xlondiff,xlatdiff,dlon,dlat,dfix + real dlat_edge,dlat_inter,dlon_edge,dlon_inter + real rlat(jmax),cosfac,tanfac + integer z,iscanflag,nlat,nlon,i,j,imax,jmax,w + integer ii,jj + logical(1) vp(imax,jmax) + +c -------------------------- + +c Figure out what level of data we have and what the array +c indices should be. + + if (z == 1) then + ! z = 1 for 850 mb zeta, w = 1 for 850 mb winds + w = 1 + else if (z == 2) then + ! z = 2 for 700 mb zeta, w = 2 for 700 mb winds + w = 2 + else if (z == 3) then + ! z = 3 for sfc zeta, w = 4 for sfc (10m) winds + w = 4 + endif + +c Calculate grid increments for interior and edge points. + +c IMPORTANT: If dtk is defined in module trig_vals in km, then +c we need to multiply by 1000 here to get meters. If it's defined +c as meters, just let it be. Since the wind values are given in +c meters, that's why we need the dlon values to be in meters. + + if (dtk < 750.) then ! chances are, dtk was defined as km + dfix = 1000.0 + else ! dtk was already defined as meters + dfix = 1.0 + endif + + dlon_edge = dtk * dfix * dlon ! Di dist over 1 grid pt + dlat_edge = dtk * dfix * dlat ! Dj dist over 1 grid pt + dlon_inter = dtk * dfix * 2.0 * dlon ! Di dist over 2 grid pts + dlat_inter = dtk * dfix * 2.0 * dlat ! Dj dist over 2 grid pts + + +c Calculate required trig functions. These are functions of +c latitude. Remember that the grid must go from north to south. +c This north-to-south requirement has +c already been checked in subroutine getgridinfo. If necessary, +c any flipping of the latitudes was done there, and flipping of +c the data, again if necessary, was done in subroutine getdata. + + do j=2,jmax-1 + rlat(j) = glatmax - ((j-1) * dlat) + cosfac(j) = cos(dtr*rlat(j)) + tanfac(j) = (tan(dtr*rlat(j)))/erad + enddo + +c Set trig factors at end points to closest interior point +c to avoid a singularity if the domain includes the poles, +c which it will for the global grids (MRF, GDAS, GFS, UKMET,NCE) + + cosfac(1) = cosfac(2) + tanfac(1) = tanfac(2) + cosfac(jmax) = cosfac(jmax-1) + tanfac(jmax) = tanfac(jmax-1) + +c NOTE: These next bits of vorticity calculation code assume that +c the input grid is oriented so that point (1,1) is the upper +c left-most (NW) and point (imax,jmax) is the lower right- +c most point. Any other grids will probably crash the +c program due to array out of bounds errors. +c NOTE: Before each calculation is done, the logical array is +c checked to make sure that all the data points in this +c calculation have valid data (ie., that the points are not +c outside a regional model's boundaries). +c +c !!! IMPORTANT NOTE: While testing this, I uncovered a bug, which was +c that I had the "j+1" and "j-1" reversed. Just from a physical +c understanding, the du/dy term at a point is calculated by taking +c the u value north of the point minus the u value south of the +c point. Intuitively, this is u(j+1) - u(j-1). However, we have +c designed this program to have the northernmost point as +c the beginning of the grid (i.e., for the global grids, j=1 at 90N, +c and j increases southward). Thus, if you would do u(j+1) - +c u(j-1), you would actually be taking the u value south of the +c point minus the u value north of the point, EXACTLY THE OPPOSITE +c OF WHAT YOU WANT. Therefore, the vorticity calculations have +c been changed so that we now have u(j-1) - u(j+1). +c +c UPDATE FEB 2009: With limited domain grids that have missing +c data on them (such as you would have for a grid that has been +c converted from a non-lat/lon grid to a lat/lon grid), we were +c running into problems below with the setting of zeta values to +c a missing value of -999. In place of this, the easiest thing to +c do is to simply assign a value of the background coriolis value +c to that point. No, this is not correct, but it is the easiest +c workaround for this right now. Setting it to zero would be too +c far off. Setting it to the coriolis component has a net effect +c of not having much impact on the barnes scheme result. +c +c --------------- +c Interior points +c --------------- + + if ( verb .ge. 3 ) then + print *,'Just before inter rvcalc, dlon_inter = ',dlon_inter + & ,' dlat_inter = ',dlat_inter + endif + + do j=2,jmax-1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1) .and. vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo + enddo +c +c ----------------------------- +c Bottom (Southernmost) points +c ----------------------------- +c + j=jmax + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------- +c Top (Northernmost) points +c -------------------------- +c + j=1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c ------------------------------- +c Left edge (Westernmost) points +c ------------------------------- +c + i=1 + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------------- +c Right edge (Easternmost) points +c -------------------------------- +c + i=imax + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c --------- +c SW corner +c --------- + i=1 + j=jmax + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i+1,j,w)-v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NW corner +c --------- + i=1 + j=1 + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NE corner +c --------- + i=imax + j=1 + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c SE corner +c --------- + i=imax + j=jmax + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i,j,w)-v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + do ii=1,imax + do jj=1,jmax + tmpzeta(ii,jj) = zeta(ii,jj,z) * 1.e5 + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine thickness_calc (imax,jmax,vp) +c +c ABSTRACT: This routine calculates the thicknesses for three +c different layers: 200-500, 500-850 and 200-850 mb. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE verbose_output + + implicit none + + integer i,j,layer,upper,lower,imax,jmax + logical(1) vp(imax,jmax) + +c -------------------------- + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 +c +c The array indices for the levels for the 4 different GP height +c arrays (as assigned in subroutine getdata) are as follows: +c 1: 850 mb +c 2: 700 mb +c 3: 500 mb +c 4: 200 mb + + + do layer = 1,3 + + select case (layer) + case (1); upper=3; lower=1; + case (2); upper=4; lower=3; + case (3); upper=4; lower=1; + end select + + do j = 1,jmax + do i = 1,imax + + if (vp(i,j)) then + thick(i,j,layer) = hgt(i,j,upper) - hgt(i,j,lower) + else + thick(i,j,layer) = -999.0 + endif + + enddo + enddo + + enddo +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine first_ges_center (imax,jmax,dx,dy,cparm,fxy + & ,cmaxmin,trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) +c +c ABSTRACT: This subroutine scans an array and picks out areas of +c max or min, then loads those center positions into the first- +c guess lat & lon arrays to be used by subroutine tracker for +c locating the very specific low center positions. +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c dx Grid spacing in i-direction for the input grid +c dy Grid spacing in j-direction for the input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c finf Logical. Field of influence. Dimension same as fxy +c cmaxmin Char string to indicate if search is for a max or a min +c trkrinfo Derived type that holds/describes various tracker parms, +c including the contour interval to be used +c ifh Index for the forecast hour +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c contour_info Type cint_stuff from module contours. Contains +c contour information +c +c OUTPUT: +c maxmini Integer array containing i-indeces of max/min locations +c maxminj Integer array containing j-indeces of max/min locations +c ifgcret return code from this subroutine +c +c OTHER: +c storm Contains the tcvitals for the storms (module def_vitals) + + USE trkrparms; USE grid_bounds; USE set_max_parms; USE def_vitals + USE contours; USE tracked_parms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,n,isstart,ifamret,ibeg,jbeg,iend,jend + integer ifh,maxstorm,imax,jmax,itemp,ifgcret + integer stormct,oldstormct,mm + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + character(*) cparm,cmaxmin + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real dmax,dmin,dx,dy,dbuffer,tmp + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of first_ges_center *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for new lows at hour ',i4,':',i2.2) + print *,'*-------------------------------------------------*' + endif + + +c First check the user-supplied grid boundaries to see if we will +c scan the entire array or just a portion of it. + + if (trkrinfo%northbd < -998.0 .or. trkrinfo%southbd < -998.0 .or. + & trkrinfo%westbd < -998.0 .or. trkrinfo%eastbd < -998.0) then + ! User did not specify a subgrid, so scan the whole domain + ibeg = 1 + iend = imax + jbeg = 1 + jend = jmax + else + +c if (trkrinfo%westbd > 360.0 .or. trkrinfo%eastbd < 0.0 .or. +c & trkrinfo%westbd < 0.0 .or. + + if (trkrinfo%westbd > 360.0 .or. + & trkrinfo%northbd > 90.0 .or. trkrinfo%northbd <-90.0 .or. + & trkrinfo%southbd > 90.0 .or. trkrinfo%southbd <-90.0 .or. + & trkrinfo%westbd >= trkrinfo%eastbd .or. + & trkrinfo%southbd >= trkrinfo%northbd) then + + if (trkrinfo%westbd > trkrinfo%eastbd) then + + if (trkrinfo%westbd < 360.0 .and. + & trkrinfo%eastbd >= 0.0)then + + ! In this special case, the user has specified that the + ! western boundary be to the west of the Greenwich + ! meridian and the eastern boundary be to the east of it. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE: The user supplied grid lon boundaries' + print *,'++ span across the Greenwich meridian.' + print *,'++ ' + print *,'++ Western boundary: ',trkrinfo%westbd + print *,'++ Eastern boundary: ',trkrinfo%eastbd + print *,'++ Northern boundary: ',trkrinfo%northbd + print *,'++ Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ! Calculate the beginning and ending i and j points for + ! this case of spanning the Greenwich meridian. The + ! beginning and ending j points are, obviously, the same + ! as for the regular case below in the else. The + ! i-beginning point will also be the same as for the + ! regular case. However, the i-ending point will be + ! modified for the meridian wrap; it will be > imax. + + jbeg = int(((glatmax + dy - trkrinfo%northbd) + & / dy) + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) + & / dy) + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) + & / dx) + 0.5) +c iend = int(((trkrinfo%eastbd - glonmin + dx) +c & / dx) + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) + & / dx) + 0.5) + imax + + goto 377 + + endif + endif + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. There is a' + print *,'!!! problem with the user-supplied grid ' + print *,'!!! boundaries. Please check them and ' + print *,'!!! resubmit the program.' + print *,'!!!' + print *,'!!! Western boundary: ',trkrinfo%westbd + print *,'!!! Eastern boundary: ',trkrinfo%eastbd + print *,'!!! Northern boundary: ',trkrinfo%northbd + print *,'!!! Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ifgcret = 91 + return + + 377 continue + + else + ! Calculate the beginning and ending i and j points.... + jbeg = int(((glatmax + dy - trkrinfo%northbd) / dy) + & + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) / dy) + & + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) / dx) + & + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) / dx) + & + 0.5) + endif + endif + +c Scan the requested portion of the grid and pick out the max and +c min data values, figure out what the max and min contour levels +c will be, and fill an array with the values of the various +c intermediate, incremental contour levels. + + if (trkrinfo%contint <= 0) then + + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. For a midlat' + print *,'!!! or tcgen run of the tracker, the contour' + print *,'!!! interval supplied by the user is not ' + print *,'!!! greater than 0.' + print *,'!!! ' + print *,'!!! User-supplied contint = ',trkrinfo%contint + print *,' ' + endif + + ifgcret = 91 + return + endif + + dmin = 9.99e20 + dmax = -9.99e20 + + do j = jbeg,jend + do i = ibeg,iend + if (i > imax) then + itemp = i - imax ! If wrapping past GM + else + itemp = i + endif + if (valid_pt(itemp,j)) then + if (fxy(itemp,j) < dmin) dmin = fxy(itemp,j) + if (fxy(itemp,j) > dmax) dmax = fxy(itemp,j) + endif + enddo + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*--------------------------------------------*' + print *,'In first_ges_center, dmin= ',dmin,' dmax= ',dmax + endif + + +c We want to allow for storms moving out of the sub-region, +c in which case we might hit slightly lower or higher +c contours than were found in the sub-region, so allow for +c an extra buffer and modify dmin and dmax.... + + dbuffer = (dmax - dmin) / 2.0 + dmax = dmax + dbuffer + dmin = dmin - dbuffer + + if ( verb .ge. 3 ) then + print *,'after adjustment, dmin= ',dmin,' dmax= ',dmax + endif + +c Next 2 lines changed for compiler compatibility on +c other platforms.... +c contour_info%xmaxcont = dmax - amod(dmax,trkrinfo%contint) +c contour_info%xmincont = dmin - amod(dmin,trkrinfo%contint) + + tmp = trkrinfo%contint + contour_info%xmaxcont = dmax - mod(dmax,tmp) + contour_info%xmincont = dmin - mod(dmin,tmp) + + if ( verb .ge. 3 ) then + print *,'A1 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A1 contour_info%xmincont= ',contour_info%xmincont + endif + + if (contour_info%xmincont > contour_info%xmaxcont) then + contour_info%xmincont = contour_info%xmaxcont + endif + +c if (dmin > contour_info%xmincont) then +c contour_info%xmincont=contour_info%xmincont + trkrinfo%contint +c endif +c if (dmax < contour_info%xmaxcont) then +c contour_info%xmaxcont=contour_info%xmaxcont - trkrinfo%contint +c endif + + if ( verb .ge. 3 ) then + print *,'A2 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A2 contour_info%xmincont= ',contour_info%xmincont + print *,'maxconts= ',maxconts + endif + +c NOTE: In the loop below, the contour_info%contvals array is now +c (5/2003) no longer used in subsequent subroutines. But we still +c need to figure out the value of the contvals as we iterate the +c loop so we can know when we've surpassed dmax and can stop +c incrementing contour_info%numcont, which we do need in subsequent +c subroutines. + + contour_info%numcont = 0 + do n = 1,maxconts + contour_info%numcont = contour_info%numcont + 1 + contour_info%contvals(n) = contour_info%xmincont + + & float(n-1)*trkrinfo%contint +c print *,'n= ',n,' contour_info%contvals(n)= ' +c & ,contour_info%contvals(n) + if (contour_info%contvals(n) >= dmax) exit + enddo + + oldstormct = stormct + call find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) + + if (stormct > 0) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' ' + print *,'!!! ************************************************' + print *,'!!! ' + print *,'!!! NOTE: In first_ges_center, the value of stormct' + print *,'!!! returned from find_all_maxmins is not greater' + print *,'!!! than 0. This means there are no new centers' + print *,'!!! to track, which is not likely. Perhaps you are' + print *,'!!! searching over too small of an area??' + print *,'!!! ' + print *,'!!! ************************************************' + print *,' ' + endif + + endif + + print *,'ifh= ',ifh,' oldstormct= ',oldstormct + print *, ' stormct= ',stormct + + do mm = 1,300 + print *,'mm= ',mm,' maxmini(mm)= ',maxmini(mm) + & ,' maxminj(mm)= ',maxminj(mm) + enddo + + if (stormct > oldstormct .and. stormct > 0) then + isstart = oldstormct + 1 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,*) 'New search: ' + write (6,*) 'Possible new max/min locations at ifh= ',ifh + write (6,*) '--------------------------------------------' + endif + + do n = isstart,stormct + if (trkrinfo%type == 'midlat') then + storm(n)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(n)%tcv_center = 'TCG ' + endif + slonfg(n,ifh) = glonmin + (maxmini(n)-1)*dx + slatfg(n,ifh) = glatmax - (maxminj(n)-1)*dy + storm(n)%tcv_stspd = -99 + storm(n)%tcv_stdir = -99 + write (storm(n)%tcv_storm_id,'(i4.4)') n + write (storm(n)%tcv_storm_name,'(i4.4)') n + stormswitch(n) = 1 + if (cparm == 'mslp') then + + if ( verb .ge. 3 ) then + write (6,71) maxmini(n),maxminj(n),slonfg(n,ifh) + & ,360.-slonfg(n,ifh),slatfg(n,ifh) + & ,slp(maxmini(n),maxminj(n))/100.0 + endif + + endif + enddo + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' New search: ' + print *,'!!! NOTE: No new storms found in find_all_maxmins' + print *,'!!! at ifh = ',ifh,' stormct= ',stormct + print *,'!!! oldstormct= ',oldstormct + print *,' ' + endif + + endif + + 71 format (1x,'i= ',i4,' j= ',i4,' lon: ',f7.2,'E (',f6.2,'W)' + & ,2x,' lat: ',f6.2,' mslp: ',f6.1,' mb') +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) +c +c ABSTRACT: This subroutine will search an area delineated by +c input i and j indeces in order to find all local maxes or mins +c in that area. The (i,j) locations of the maxes/mins are returned +c in the maxmini and maxminj arrays. The input 3-character string +c cmaxmin will tell the subroutine to look for a "max" or a "min". +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c ibeg i-index for upper left location of grid to search +c iend i-index for lower right location of grid to search +c jbeg j-index for upper left location of grid to search +c jend j-index for lower right location of grid to search +c fxy Real array of data values +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c contour_info Type cint_stuff from module contours containing the +c the following 4 variables: +c 1. xmincont Real value for min contour level in the fxy data array +c 2. xmaxcont Real value for max contour level in the fxy data array +c 3. contvals Real array holding values of cont levels at this time +c 4. numcont Number of contour intervals found at this time +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c trkrinfo derived type containing various user-input tracker parms +c cmaxmin String that declares if "min" or "max" is being searched +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c +c OUTPUT: +c maxmini integer array containing i-indeces of the max/min points +c maxminj integer array containing j-indeces of the max/min points +c ifamret return code from this subroutine + + USE trkrparms; USE set_max_parms; USE contours + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + integer stormct,i,j,ibeg,iend,jbeg,jend,ix,jx,ixp1,ixm1 + integer ip,jp,maxstorm,jxp1,jxm1,ifamret,isret,iaret,iclmret + integer isoiret,icccret,igicwret,imax,jmax + character ccflag*1,get_last_isobar_flag*1,point_is_over_water*1 + character(*) cmaxmin + logical(1) still_finding_valid_maxmins,rough_gradient_check_okay + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real xavg,stdv,search_cutoff,dmin,dmax,sphere_cutoff + real plastbar,rlastbar,fract_land,dx,dy + +c----- + still_finding_valid_maxmins = .true. + + +c print *,'ctm beg of find_all_maxmins, maxstorm= ',maxstorm + + +c First, we want to get the mean and standard deviation of the input +c field to be searched. We can use the standard deviation info as +c part of our guideline for when to stop searching for maxes & mins. +c We will set the search cut-off threshold at 1/2 standard deviation +c above the mean for min searches. So, for the example of mslp, if +c the mean pressure over the whole domain is 1010 mb and the +c standard deviation is 12 mb, then when we are searching, if the +c lowest available (i.e., hasn't been found in a previous iteration +c of this loop) pressure is 1016, then it's time to stop searching. + + call avgcalc (fxy,imax*jmax,valid_pt,xavg,iaret) + call stdevcalc (fxy,imax*jmax,valid_pt,xavg,stdv,isret) + if (iaret /= 0 .or. isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_all_maxmins, the calls to avgcalc' + print *,'!!! and/or stdevcalc returned an error.' + print *,'!!! iaret= ',iaret,' isret= ',iaret + print *,' ' + endif + + ifamret = 98 + return + endif + + if (cmaxmin == 'min') then + search_cutoff = xavg + stdv*0.5 + else + search_cutoff = xavg - stdv*0.5 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In find_all_maxmins, search_cutoff= ',search_cutoff + print *,' ' + endif + +c Now begin to search the domain. We do a simple gridpoint scan, +c and once we find the max/min value, we pass the (i,j) coordinates +c at that point to a routine to check for a closed contour. Then +c we mask out those points in the contour (or, if there is not a +c closed contour, just the 8 points immediately surrounding the low +c center) and we do another iteration of search_loop to look for +c more lows. We mask out points we've found so that on subsequent +c iterations of search_loop, we don't find the same old center +c again and again and again..... + + search_loop: do while (still_finding_valid_maxmins) + + dmin = 9.99e20 + dmax = -9.99e20 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + ip = i + jp = j + + if (ip > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: In find_all_maxmins, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. The search' + print *,'!!! will not extend to the user-requested' + print *,'!!! grid boundary.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',ip + print *,' ' + endif + + exit iloop + + endif + endif + + if (valid_pt(ip,jp) .and..not. masked_out(ip,jp)) then + if (cmaxmin == 'min') then + if (fxy(ip,jp) < dmin) then + dmin = fxy(ip,jp) + ix = ip + jx = jp + endif + else + if (fxy(ip,jp) > dmax) then + dmax = fxy(ip,jp) + ix = ip + jx = jp + endif + endif + endif + + enddo iloop + enddo jloop + + if (cmaxmin == 'min') then + if (dmin < search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + else + if (dmax > search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + endif + +c As a rough first check, see if the neighboring points on all +c 4 sides have a gradient sloping down into the found min point, +c or at least that there is a flat field not having a gradient +c sloping away from the center point. + + call get_ijplus1_check_wrap (imax,jmax,ix,jx,ixp1,jxp1,ixm1 + & ,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In find_all_maxmins, the center we found' + print *,'!!! is too close to the grid boundary and will' + print *,'!!! NOT be checked for a closed contour.' + print *,'!!! ix= ',ix,' jx= ',jx,' fxy= ',fxy(ix,jx) + print *,'!!! ' + print *,' ' + endif + + masked_out(ix,jx) = .true. + cycle search_loop + endif + + if (cmaxmin == 'min') then + if (fxy(ix,jx) <= fxy(ixp1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxm1) .and. + & fxy(ix,jx) <= fxy(ixm1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + else + if (fxy(ix,jx) >= fxy(ixp1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxm1) .and. + & fxy(ix,jx) >= fxy(ixm1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + endif + + if (rough_gradient_check_okay) then + + if ( verb .ge. 3 ) then + print *,'Found a possible max/min at ix= ',ix,' jx= ',jx + endif + + +c From this rough check, we appear to have a gradient sloping +c in towards the center point. Now call the subroutine to +c check whether or not there is in fact a closed contour +c surrounding this local maximum or minimum. + + get_last_isobar_flag = 'n' + ccflag = 'n' + call check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,ccflag,cmaxmin,trkrinfo + & ,1,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if (ccflag == 'y') then + if (stormct < maxstorm) then + stormct = stormct + 1 + + if ( verb .ge. 3 ) then + print *,'AAA stormct= ',stormct,' ix= ',ix,' jx= ',jx + endif + + ! For a tcgen case, we will add in one additional check, + ! and that is to ensure the point is (mostly) over water. + ! Only do this check if the user has requested it (some + ! of the global models do not have a land-sea mask + ! included in the grib data files). + + point_is_over_water = 'u' + + if (trkrinfo%use_land_mask == 'y') then + call check_land_mask (imax,jmax,ix,jx,fract_land + & ,valid_pt,dx,dy,point_is_over_water,iclmret) + if (iclmret /= 0) then + print *,' ' + print *,'!!! ERROR from check_land_mask for ix= ',ix + & ,' jx= ',jx + print *,'!!! STOPPING PROGRAM' + stop 95 + endif + endif + + if (point_is_over_water /= 'n') then + maxmini(stormct) = ix + maxminj(stormct) = jx + endif + + else + + if ( verb .ge. 3 ) then + print *,'---max stormct reached, stormct= ', stormct + endif + + endif + else + + if ( verb .ge. 3 ) then + print *,'!!! contour check negative, ccflag= ',ccflag + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*-----------------------------------------------*' + print *,' ' + endif + + endif + +c Regardless of whether or not the found point turns out to have +c a closed contour, we don't want to find this local minimum or +c its 8 surrounding points again in a search on a subsequent +c iteration of this loop. + + masked_out(ix,jx) = .true. + masked_out(ix,jxp1) = .true. + masked_out(ixp1,jxp1) = .true. + masked_out(ixp1,jx) = .true. + masked_out(ixp1,jxm1) = .true. + masked_out(ix,jxm1) = .true. + masked_out(ixm1,jxm1) = .true. + masked_out(ixm1,jx) = .true. + masked_out(ixm1,jxp1) = .true. + + enddo search_loop + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine mask_based_on_wind_circ (imax,jmax,dx,dy,level + & ,valid_pt,masked_outc,trkrinfo + & ,ctlon,ctlat,cmodel_type,imbowret) +c +c ABSTRACT: This subroutine masks out grid points for a storm that +c is currently being tracked. It is called after a fix has been +c made at the current forecast hour. It is only used as a backup, +c that is, if the mslp data were not there and/or a fix position +c for mslp could not be made, then that means that the mask would +c not be able to get updated using the routine in subroutine +c check_closed_contour. But we still do need to update that mask, +c so we will instead do it based on wind circulation. We will go +c out radially from the center, starting at 40 km, then every +c 40 km from there on out. When the mean cyclonic Vt drops below +c 3 m/s, stop searching, and then mask out all grid points within +c that last-searched radius. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_outc Logical. T = data point is already accounted for, +c under the influence of another nearby max or min +c center; F = data point is available to be scanned by +c this subroutine for max or min centers. +c ctlon Fix longitude for the input parameter to this routine +c ctlon Fix latitude for the input parameter to this routine +c cmodel_type character, 'global' or 'regional' + + USE set_max_parms; USE trkrparms; USE grid_bounds + USE verbose_output; USE level_parms + + implicit none + + type (trackstuff) trkrinfo + + character(*) cmodel_type + integer, parameter :: numazim=24 + integer imax,jmax,level,imbowret,nlev,iazim,i,j + integer ibiret1,ibiret2,azimuth_ct,igvtret + integer jnfix,jsfix,iefix,iwfix + real vr(numazim),vt(numazim) + real dx,dy,ctlon,ctlat,rdist,bear,targlat,targlon + real xintrp_u,xintrp_v,grid_buffer,xmax_rdist_reached + real vt_mean,vt_azim_sum,xbear,dist,degrees + logical(1) valid_pt(imax,jmax),masked_outc(imax,jmax) + logical(1) searching_valid_pts + + imbowret = 0 + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + searching_valid_pts = .true. + + rdist = 40.0 ! units in km + xmax_rdist_reached = rdist ! units in km + + radial_loop: do while (searching_valid_pts) + + azimuth_ct = 0 + vt_azim_sum = 0.0 + vt = -999.0 + vr = -999.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (ctlat,ctlon,rdist,bear,targlat,targlon) + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + xmax_rdist_reached = rdist + exit radial_loop + endif + + ! These calls to bilin_int_uneven pass a variable, level, + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (ctlon,ctlat,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim) + & ,vt(iazim),igvtret) + azimuth_ct = azimuth_ct + 1 + vt_azim_sum = vt_azim_sum + vt(iazim) + else + xmax_rdist_reached = rdist + exit radial_loop + endif + + enddo azimloop + + if (azimuth_ct > 0) then + ! Compute azimuthally-averaged Vt at this distance + vt_mean = vt_azim_sum / float(azimuth_ct) + else + vt_mean = -999.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: rdist= ',rdist,' azimuth_ct= ',azimuth_ct + & ,' vt_azim_sum= ',vt_azim_sum,' vt_mean= ',vt_mean + endif + + if (ctlat >= 0.0) then + if (vt_mean >= 3.0) then + ! For a NH storm, if the cyclonic mean Vt >= 3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + else + if (vt_mean <= -3.0 .and. vt_mean > -998.0) then + ! For a SH storm, if the cyclonic mean Vt <= -3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + enddo radial_loop + + if ( verb .ge. 3 ) then + print *,'mbow: After radial_loop, rdist= ',rdist + & ,' xmax_rdist_reached= ',xmax_rdist_reached + endif + +c ----------------------------------------------------------------- +c At this point, we are done searching radially outwards away from +c the storm center. The max radial distance we reached is called +c xmax_rdist_reached. By getting to this spot in the subroutine, +c that means that we bumped out of radial_loop above because the +c rdist being used in that loop got to a radius at which the mean +c cyclonic Vt no longer was strong enough to continue the search +c outward, so we need to reduce it by 40 km here (back to the value +c for the last successful search). At a minimum, we will mask to a +c radius of 80 km. + + if (xmax_rdist_reached > 80.0) then + xmax_rdist_reached = xmax_rdist_reached - 40.0 + else + xmax_rdist_reached = 80.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: After adjustment of xmax_rdist_reached, rdist= ' + & ,rdist,' xmax_rdist_reached= ',xmax_rdist_reached + endif + + bearloop: do i = 1,4 + + ! Now find the values of the longitude for the farthest west + ! and east points and find the values of the latitude for the + ! farthest north and south points. The i and j indices + ! associated with these lons and lats will be used to define + ! the bounds of the grid over which we scan to find points + ! that will update the mask. + + select case (i) + case (1); xbear = 0.0; + case (2); xbear = 90.0; + case (3); xbear = 180.0; + case (4); xbear = 270.0; + end select + + call distbear (ctlat,ctlon,xmax_rdist_reached,xbear + & ,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,'mbow: distbear for i= ',i,' targlon= ',targlon + & ,' targlat= ',targlat + endif + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon > glonmax for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmax= ',glonmax + imbowret = 95 + return + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon < glonmin for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmin= ',glonmin + imbowret = 95 + return + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'either targlat > glatmx or targlat < glatmin, so' + print *,'we cannot update the mask.' + print *,'targlat= ',targlat,' glatmin= ',glatmin + print *,' glatmin= ',glatmin + imbowret = 95 + return + cycle bearloop + endif + + ! Get the i & j starting and ending points for our loop where + ! we will update the mask.... + + if (i == 1) then + + ! Get j for northern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jnfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jnfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 2) then + + ! Get i for eastern longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iefix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + elseif (i == 3) then + + ! Get i for southern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jsfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jsfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 4) then + + ! Get i for western longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iwfix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + endif + + enddo bearloop + + if ( verb .ge. 3 ) then + print *,'mbow: iwfix= ',iwfix,' iefix= ',iefix + & ,' jnfix= ',jnfix,' jsfix= ',jsfix + endif + + do i = iwfix,iefix + do j = jnfix,jsfix + + call calcdist (glon(i),glat(j),ctlon,ctlat,dist,degrees) + + if (dist < xmax_rdist_reached) then + masked_outc(i,j) = .true. + endif + + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,closed_contour,cmaxmin,trkrinfo + & ,num_requested_contours,contour_info + & ,get_last_isobar_flag,plastbar,rlastbar,icccret) +c +c ABSTRACT: This subroutine checks a field of data around an input +c (ix,jx) data point to see if a closed contour exists around +c that data point. It can check for a closed contour on a max or a +c min field, depending on the value of the input variable 'cmaxmin'. +c The algorithm works by examining rings of the 8 data points +c surrounding a data point that is in the contour interval. For +c example, in the diagram below, the X represents the location of +c the local minimum value which was passed into this routine with +c the coordinates (ix,jx), let's say it's 985 mb. And let's assume +c that the data values at points A-I are all in the 4 mb contour +c interval of 985-989 mb, and that all the surrounding points have +c data values >= 989. To test for a closed contour, we first check +c the ring of 8 points immediately around point X to see what their +c data values are. If a data value is found that is below the +c lower limit of this contour interval (985 mb) or lower than the +c local minimum value at the X point that we initially targeted +c (985 mb), then we do NOT have a closed contour, and we exit this +c subroutine. But in our example, that's not the case, and we have +c 5 points (B,D,E,F,G) that are in the interval. So in our next +c iteration of the loop, we set up 5 rings, each one set up around +c the points found in the first iteration (B,D,E,F,G), and we check +c the 8 points around each of those points. A logical array is +c used so that as soon as a point is found, it is flagged as being +c found. In this way, when we look at the ring around point D, for +c example, we won't pick point X again and set up another ring +c around it in the next ring iteration and end up in an infinite +c loop, going back and forth between point X and point D. While +c checking the 8 points in a ring, if a found data value is above +c our contour interval (i.e., >= 989 mb), we just ignore the +c point; we only mark points that are in our contour interval, +c and again, if we find a point below our contour interval, we +c exit the subroutine with a flag indicating a closed contour was +c NOT found. So in this method, we keep spreading out from the +c initial local minimum and creating and checking new rings until +c we either: (a) Hit the edge of the regional grid, in which case +c we consider a closed contour NOT found, (b) Run into a data +c point that has been marked as being under the influence of +c another nearby low, in which case we consider a closed contour +c NOT found, (c) Run into a point which is below (above) our +c contour interval for a min (max) check, in which case we +c consider a closed contour NOT found, or (d) we run out of +c points to keep searching, we have no rings left to create and +c check because all of the surrounding points are above (below) +c our contour interval for a min (max) check, and by default we +c consider this a closed contour and return to the calling +c subroutine a flag indicating such. +c +c + + + + + + + + + + +c + + + + + + + + + + +c + + A B + + + + + + +c + + C D X E + + + + +c + + + + F G + + + + +c + + + + + H I + + + +c + + + + + + + + + + +c + + + + + + + + + + +c +c UPDATE: This subroutine was updated to keep searching for +c multiple closed contours until it can't find anymore. The +c input parameter num_requested_contours dictates how many +c contours to search for. In the case of just trying to roughly +c locate new centers and establish that there is a closed +c circulation, num_requested_contours will = 1, and we will exit +c after finding that 1 contour. But for a check after making a +c full center fix, we set num_requested_contours = 999 so that +c we can keep searching for all closed contours around the low. +c In this 999 case, you will eventually get to a point where +c there is no closed contour. In that case, in the standard +c output you will see a message telling you that you hit a point +c that is not in the contour and that there is no closed contour, +c but you will also notice that the ccflag = y, meaning there is +c a closed contour (because you have found at least 1 closed +c contour along the way). The reason to keep searching for more +c closed contours is that we can then return the value of the +c outermost closed isobar. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c cmaxmin character string ('max' or 'min') that tells this +c routine what we're looking for. +c trkrinfo derived type that holds/describes various tracker parms +c contour_info Type cint_stuff from module contours. Contains +c contour information +c num_requested_contours For the simple first_ges_center check, +c this will be 1 (we just want to know if there's at +c least 1 closed contour). For the verifying check after +c we've found a center, this will be 9999 (i.e., just keep +c searching for more contours) +c get_last_isobar_flag character ('y' or 'n') to indicate whether +c or not to report on the value of the last closed isobar +c and the radius of the last closed isobar. +c +c OUTPUT: +c closed_contour character; A returned value of 'y' indicates that +c this routine was able to find a closed contour. +c plastbar Contains the value of the last closed isobar (unrounded) +c rlastbar Contains the mean radius of the last closed isobar +c +c LOCAL: +c num_pts_in_all_contours Counter for the number of pts inside of +c the contour we're looking at +c next_ring_ct Counter for the number of points that have been +c tagged to be used as center points for the next +c iteration of multiple_ring_loop. +c next_contour_ct Counter for the number of points that have been +c tagged to be used as center points in the first iteration +c through single_contour_scan_loop as we begin to scan +c points in the *next* contour interval. This counter gets +c incremented when, for example, we are searching points +c around a current center point and we find one that is not +c in our current interval, but rather is in the next +c interval. We want to remember this point and store the +c location, so we increment this counter and store the +c location in next_contour_i and next_contour_j arrays. +c beyond_contour_ct Counter for the number of points that have been +c tagged to be used as center points for some subsequent +c iteration of successive_contours_loop. This is +c different from next_contour_ct, which is used to hold +c the locations of points that are definitely in the +c *next* contour interval. Here, we have points that we +c just store in a pool of potential points to be searched +c in future iterations. These points can come about in +c cases where there is a very intense, very compact low +c with a tight pressure gradient, such that multiple +c contour intervals could be spanned in between 2 adjacent +c gridpoints (this is especially the case if the contour +c interval you have chosen is small). You need to be +c careful with how you handle this array. Once you find +c that you have searchable points in next_contour_i or +c next_contour_j, do not just simply empty out this +c beyond_contour count and its i and j arrays. The +c reason being that some of these "beyond" points may end +c up being used and searched in subsequent iterations, but +c not if we just delete them now. + + + USE set_max_parms; USE trkrparms; USE contours; USE grid_bounds + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,ir,iria,irja,irx,jrx,ix,jx,imax,jmax + integer nb,ibx,jby,nct,iflip + integer mr,ringct,ixp1,ixm1,jxp1,jxm1,nring,iter + integer icenx,jcenx,icccret,next_ring_ct,igicwret + integer num_pts_in_all_contours,next_contour_ct + integer beyond_contour_ct + integer num_pts_in_one_contour + integer num_requested_contours,num_found_contours + integer nm,im,jm,inall,insingle,isc_count,rlast_distct + character found_a_point_in_our_contour*1,closed_contour*1 + character found_a_point_below_contour*1 + character found_a_point_above_contour*1,get_last_isobar_flag*1 + character(*) cmaxmin + logical(1) still_scanning + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + logical(1) point_is_already_in_our_contour(imax,jmax) + logical(1) point_is_already_in_next_contour(imax,jmax) + logical(1) point_is_already_in_beyond_pool(imax,jmax) + integer isni,isnj,inci,incj,ibci,ibcj,ihmi,ihmj,itmi,itmj + integer, allocatable :: search_next_i(:) + integer, allocatable :: search_next_j(:) + integer, allocatable :: next_contour_i(:) + integer, allocatable :: next_contour_j(:) + integer, allocatable :: beyond_contour_i(:) + integer, allocatable :: beyond_contour_j(:) + integer, allocatable :: hold_mask_i_loc(:) + integer, allocatable :: hold_mask_j_loc(:) + integer, allocatable :: temp_mask_i_loc(:) + integer, allocatable :: temp_mask_j_loc(:) + integer, allocatable :: ringposi(:),ringposj(:) + real,allocatable :: ringpos(:,:) + real fxy(imax,jmax),contvals(maxconts) + real contlo,conthi,xcentval,contlo_next,conthi_next + real dist,degrees,rlast_distsum,plastbar,rlastbar +c + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + allocate (search_next_i(imax*jmax),stat=isni) + allocate (search_next_j(imax*jmax),stat=isnj) + allocate (next_contour_i(imax*jmax),stat=inci) + allocate (next_contour_j(imax*jmax),stat=incj) + allocate (beyond_contour_i((imax*jmax)/2),stat=ibci) + allocate (beyond_contour_j((imax*jmax)/2),stat=ibcj) + allocate (hold_mask_i_loc(imax*jmax),stat=ihmi) + allocate (hold_mask_j_loc(imax*jmax),stat=ihmj) + allocate (temp_mask_i_loc(imax*jmax),stat=itmi) + allocate (temp_mask_j_loc(imax*jmax),stat=itmj) + if (isni /= 0 .or. isnj /= 0 .or. inci /= 0 .or. incj /= 0 .or. + & ibci /= 0 .or. ibcj /= 0 .or. ihmi /= 0 .or. ihmj /= 0 .or. + & itmi /= 0 .or. itmj /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various search, hold and temp arrays.' + print *,'!!! isni = ',isni,' isnj= ',isnj + print *,'!!! inci = ',inci,' incj= ',incj + print *,'!!! ibci = ',ibci,' ibcj= ',ibcj + print *,'!!! ihmi = ',ihmi,' ihmj= ',ihmj + print *,'!!! itmi = ',itmi,' itmj= ',itmj + print *,' ' + endif + + STOP 98 + endif + + closed_contour = 'n' + xcentval = fxy(ix,jx) + num_found_contours = 0 + next_contour_ct = 0 + beyond_contour_ct = 0 + num_pts_in_all_contours = 0 + hold_mask_i_loc = 0 + hold_mask_j_loc = 0 + beyond_contour_i = 0 + beyond_contour_j = 0 + point_is_already_in_our_contour = .false. + point_is_already_in_beyond_pool = .false. + icccret = 0 + isc_count = 0 + plastbar = -999.0 + rlastbar = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* Top of check_closed_contour, ix= ',ix,' jx= ',jx + print *,'*-----------------------------------------------*' + print *,' ' + print *,'fxy(ix,jx)= ',fxy(ix,jx),' xcentval= ',xcentval + endif + +c First, set up the contour intervals that will be used. In +c the original version of this code, we used preset +c standard intervals (984,988,992,996,1000,1004....). But upon +c further review, it was decided that this was too arbitrary. +c So instead, we consider the found min (max) value to be the +c bottom (top) of the list of contour intervals. In this way, +c we can clearly specify and screen storms based on the "depth" +c of the pressure field as compared to the surroundings. + + i = 1 + do while (i <= maxconts) + if (cmaxmin == 'min') then + contvals(i) = xcentval + float(i-1)*trkrinfo%contint + i = i + 1 + else + iflip = maxconts - i + 1 + contvals(iflip) = xcentval - float(i-1)*trkrinfo%contint + i = i + 1 + endif + enddo + +c This successive_contours loop is the master loop.... + + successive_contours_loop: do while (num_found_contours < + & num_requested_contours) + +c Find the contour interval in which the center value resides. +c Note that the lower bound is included for a min check, while +c the upper bound is included for a max check. Note also that +c this subroutine can be used to find the last closed contour, +c and part of that functionality shows up in the next while +c statement where we reference "num_found_contours" in the +c array indeces for the contour values. Basically, the way we +c do this is, for example, if our central value is 990.4 mb and +c our contour interval is 4 mb, then in the first run through +c successive_contours_loop we see if we have a closed contour in +c the interval 990.4-994.4. If yes, then the next time through +c this loop, we see if we have a closed contour in the interval +c 994.4-998.4. If yes, then the next loop check is for 998.4- +c 1002.4, and so on.... We stop searching if we find a value +c that is either below the xcentval input into this subroutine +c or below the lower value of the current contour interval (this +c would mean a change in the gradient and would indicate that, +c in the case of mslp, we are heading down towards another, +c different low). + + isc_count = isc_count + 1 + + point_is_already_in_next_contour = .false. + + i = 1 + do while (i < maxconts) + if (cmaxmin == 'min') then + if (contvals(i) <= xcentval .and. xcentval < contvals(i+1)) + & then + + if ( verb .ge. 3 ) then + print *,'At A, num_found_contours= ',num_found_contours + endif + + contlo = contvals(i+num_found_contours) + conthi = contvals(i+1+num_found_contours) + + if ( verb .ge. 3 ) then + print *,'At A, contlo= ',contlo,' conthi= ',conthi + endif + exit + + endif + else + if (contvals(i) < xcentval .and. xcentval <= contvals(i+1)) + & then + contlo = contvals(i-num_found_contours) + conthi = contvals(i-num_found_contours+1) + exit + endif + endif + i = i + 1 + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'num_found_contours= ',num_found_contours + print *,'contlo= ',contlo,' conthi= ',conthi + print *,'xcentval= ',xcentval + endif + + +c This single_contour_scan_loop is the main loop for searching +c for one individual contour. If it is determined that a contour +c exists, control is returned to the successive_contours_loop, +c and if more contours were requested to be found, then the +c search continues onward & outward.... + + temp_mask_i_loc = 0 + temp_mask_j_loc = 0 + + iter = 1 + num_pts_in_one_contour = 0 + still_scanning = .true. + + rlast_distsum = 0.0 + rlast_distct = 0 + + single_contour_scan_loop: do while (still_scanning) + +c print *,' ' +c print *,' top of single contour scan loop' +c print *,'+++ iter= ',iter +c print *,' N1: next_contour_ct= ',next_contour_ct + + if (iter == 1 .and. num_found_contours == 0) then + ! For the first iteration, we have only the first ring, + ! which is centered on the input minimum/maximum point. + ringct = 1 + search_next_i(1) = ix + search_next_j(1) = jx + +c point_is_already_in_our_contour(ix,jx) = .true. +c num_pts_in_one_contour = num_pts_in_one_contour + 1 +c temp_mask_i_loc(num_pts_in_one_contour) = ix +c temp_mask_j_loc(num_pts_in_one_contour) = jx + + else if (iter == 1 .and. num_found_contours > 0) then + ! This is the first iteration in a *new* contour. + ! That is, we have already found 1 or more previous + ! contours while in previous iterations of + ! successive_contours_loop and we are now beginning + ! to look for the next contour. + +c print *,' N2: next_contour_ct= ',next_contour_ct + + if (next_contour_ct == 0) then + ! This would be for the special case in which, for + ! example, you've got a very intense, compact storm + ! that "skips" a contour. That is, suppose the + ! min pressure of a storm is 982 mb, and we are + ! utilizing a 4-mb contour interval, but all + ! surrounding data points are, say, 987 mb or + ! higher. Then, next_contour_ct would be 0 since no + ! data points were found in the next contour interval + ! of 982-986 mb, but we can continue searching since the + ! gradient is still sloping the correct way. The code in + ! this if statement handles this special case. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ALERT: next_contour_ct = 0 ' + endif + + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + +c print *,'b4 ZZ, ringct= ',ringct +c print *,'at ZZ, bcc= ',beyond_contour_ct +c & ,'contlo_next= ',contlo_next +c & ,'conthi_next= ',conthi_next + + bey_con_min_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_min_loop + endif + +c print *,'-- ZZ, ibx= ',ibx,' jby= ',jby +c & ,' fxy(ibx,jby)= ',fxy(ibx,jby) + + if (fxy(ibx,jby) >= contlo_next .and. + & fxy(ibx,jby) < conthi_next) then + +c print *,'>> ZZ HIT!!, ibx= ',ibx,' jby= ',jby +c +c print *,' +++ BEYOND in NEXT: i= ',ibx,' j= ',jby +c & ,' fxy= ',fxy(ibx,jby) + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + +c print *,'.. ZZ, next_contour_ct= ',next_contour_ct + + enddo bey_con_min_loop + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + +c print *,'At A, beyond_contour_ct= ',beyond_contour_ct +c print *,' contlo_next = ',contlo_next +c print *,' conthi_next = ',conthi_next + + bey_con_max_loop: do nb = 1,beyond_contour_ct + +c print *,'in bey_con_max_loop, nb= ',nb + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_max_loop + endif + +c print *,'ibx= ',ibx,' jby= ',jby,' data= ' +c & ,fxy(ibx,jby) + + if (fxy(ibx,jby) > contlo_next .and. + & fxy(ibx,jby) <= conthi_next) then + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + +c print *,' ++ HIT! ibx= ',ibx,' jby= ',jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + enddo bey_con_max_loop + endif + + if (next_contour_ct > 0) then + ringct = next_contour_ct + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! XXX next_contour_ct not > 0 !!!' + print *,'next_contour_ct= ',next_contour_ct + print *,'beyond_contour_ct= ',beyond_contour_ct + print *,'ringct= ',ringct + print *,'next_ring_ct= ',next_ring_ct + print *,'cycling to top of successive_contours_loop..' + print *,' ' + endif + + ! The number of rings that we have available to search + ! in the next contour interval is 0, so cycle all the + ! way back to the top of the outer loop, which is + ! successive_contours_loop, so that we can increase the + ! contour bounds and search inside those new bounds. + ! Again, this is for the case in which we have an + ! intense, compact storm and we are using a small + ! contour interval, such that we are essentially + ! "skipping" over one of these intervals in one of the + ! loop iterations. We need to bump up the + ! num_found_contours by one in order to increase the + ! array index in the contvals array at the top of the + ! successive_contours_loop. It is kosher to do this + ! since the reason we are cycling back to the top of + ! that loop is that we are skipping over a contour + ! interval. + + num_found_contours = num_found_contours + 1 + cycle successive_contours_loop + + endif + + else + + ringct = next_contour_ct + + endif + + do nring = 1,ringct + search_next_i(nring) = next_contour_i(nring) + search_next_j(nring) = next_contour_j(nring) +c print *,'at A, nring= ',nring,' next_contour_i(nring)= ' +c & ,next_contour_i(nring),' next_contour_j(nring)= ' +c & ,next_contour_j(nring) + enddo + + next_contour_ct = 0 + + else + ringct = next_ring_ct + endif + + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + allocate (ringposi(ringct),stat=iria) + allocate (ringposj(ringct),stat=irja) + if (iria /= 0 .or. irja /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various ring arrays. iria = ',iria + print *,'!!! irja = ',irja + print *,' ' + endif + + STOP 98 + endif + +ctm +c print *,' ' +c print *,'ringct= ',ringct + + do nring = 1,ringct + ringposi(nring) = search_next_i(nring) + ringposj(nring) = search_next_j(nring) +ctm +c print *,'nring= ',nring,' ringposi= ',ringposi(nring) +c & ,' ringposj= ',ringposj(nring) + enddo + + next_ring_ct = 0 + + ! This next loop reviews the points that have been + ! labelled for the "beyond_contour" pool. As we get further + ! into successive iterations of successive_contours_loop, + ! some of these previously "beyond" points are now within + ! the contour interval range that we are checking, so we + ! need to go through the list of "beyond" points and remove + ! any that are no longer in that "beyond" category.... + + check_beyond_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! This point may have been removed already in a + ! previous iteration of successive_contours_loop. + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle check_beyond_loop + endif + + ! Check to see if any of the points being searched in the + ! upcoming multiple_ring_loop are points that had previously + ! been saved as "beyond_contour" points. If so, remove + ! their status as "beyond_contour" points by setting the + ! logical flag to false. + + do nring = 1,ringct + + if (ibx == ringposi(nring) .and. jby == ringposj(nring)) + & then +c print *,' ' +c print *,'!!! beyond remove: ibx= ',ibx,' jby= ',jby + point_is_already_in_beyond_pool(ibx,jby) = .false. + endif + + enddo + + enddo check_beyond_loop + + +c In each iteration of single_contour_scan_loop, we can have a +c different number of rings to analyze. In the first +c iteration, we only have 1 ring, the initial ring around the +c local max/min that was input to this subroutine. Subsequent +c iterations will have a variable number of rings, depending on +c how many new data points within our contour interval were +c found in the previous iteration. + + multiple_ring_loop: do mr = 1,ringct + + icenx = ringposi(mr) + jcenx = ringposj(mr) + +ctm +c print *,' --- iter= ',iter,' mr= ',mr,' icenx= ',icenx +c & ,' jcenx= ',jcenx,' imax= ',imax,' jmax= ',jmax + + call get_ijplus1_check_wrap (imax,jmax,icenx,jcenx,ixp1,jxp1 + & ,ixm1,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NO CLOSED CONTOUR: The call to ' + print *,'!!! get_ijplus1_check_wrap indicates the' + print *,'!!! max/min contour extends past the edge of' + print *,'!!! our regional grid. ' + print *,' ' + print *,' ' + endif + + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c For each individual ring, we check all 8 points surrounding +c the center point. The points are numbered for each ring as +c shown in the diagram to the right of the "select case" +c statement just below. REMEMBER: The j in our grids +c increases from north to south, so that for a global grid, +c j=1 is at 90N and j=jmax is at 90S. + + individual_ring_loop: do ir = 1,9 + + select case (ir) + case (1); irx=ixm1; jrx=jcenx;! 2 3 4 + case (2); irx=ixm1; jrx=jxm1; ! + case (3); irx=icenx;jrx=jxm1; ! + case (4); irx=ixp1; jrx=jxm1; ! 1 (icenx,jcenx) 5 + case (5); irx=ixp1; jrx=jcenx;! + case (6); irx=ixp1; jrx=jxp1; ! + case (7); irx=icenx;jrx=jxp1; ! 8 7 6 + case (8); irx=ixm1; jrx=jxp1; ! + case (9); irx=icenx; jrx=jcenx; ! = center pt of ring + end select + +c Make sure the point we are looking at has valid data. +c This is an issue only on regional grids, where we have a +c buffer of bitmapped (null) data points surrounding the +c real grid. + +c print *,'ind ring loop: ir= ',ir,' irx= ',irx,' jrx= ',jrx + + if (.not. valid_pt(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a non-' + print *,'!!! valid point, meaning we are near the' + print *,'!!! bounds of the grid, or at least the ' + print *,'!!! bounds of the valid data for this ' + print *,'!!! grid. We will skip the' + print *,'!!! search for this center.' + print *,'!!! ' + print *,'!!! (i,j) of non-valid pt = (' + & ,irx,',',jrx,')' + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c Check to make sure that the point we are looking at is +c not considered under the influence of another nearby low. + + if (masked_out(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a point' + print *,'!!! that has been masked out, meaning it' + print *,'!!! belongs under the influence of ' + print *,'!!! another nearby low, so we will skip' + print *,'!!! the search for this center....' + print *,'!!! ' + print *,'!!! Min central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Masked-out value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of masked value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we have already hit this point on a previous ring +c check, then just ignore this point and cycle past it. + + if (point_is_already_in_our_contour(irx,jrx)) then +ctm +c print *,' ' +c print *,'Pt. AAA, already-in-contour.....' +c print *,'irx= ',irx,' jrx= ',jrx + cycle individual_ring_loop + endif + +c For a MIN check, check to see if the data point is below +c the contour interval or is below the local minimum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c For a MAX check, check to see if the data point is above +c the contour interval or is above the local maximum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c +c For example, for mslp, this would be as we're moving +c outward away from lower pressures to higher pressures, +c and then all of a sudden we come upon a lower pressure. +c This probably means we're heading toward another low +c pressure area, so mark the point and return to the +c calling routine. + + found_a_point_below_contour = 'n' + found_a_point_above_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) < xcentval .or. fxy(irx,jrx) < contlo) + & then + found_a_point_below_contour = 'y' + endif + else + if (fxy(irx,jrx) > xcentval .or. fxy(irx,jrx) > conthi) + & then + found_a_point_above_contour = 'y' + endif + endif + + if (found_a_point_below_contour == 'y' .or. + & found_a_point_above_contour == 'y') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a data' + print *,'!!! value that is less (greater) than the' + print *,'!!! current contour interval bound for a' + print *,'!!! min (max) and/or is less (greater) ' + print *,'!!! than the minimum (maximum) central ' + print *,'!!! value that we are centering the ' + print *,'!!! search on.' + print *,'!!! ' + print *,'!!! Central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Flagged value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of flagged value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we've made it this far, then we at least know that the +c gradient is still heading in the right direction. Do the +c check now to see if the value at this point is within our +c specific contour interval (there is the possibility that +c the value is beyond our interval, which will be checked +c for just below, and if that's the case, then that point +c will be processed in a subsequent iteration of this loop +c that encompasses that correct contour interval). + + found_a_point_in_our_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) >= contlo .and. fxy(irx,jrx) < conthi) + & then + found_a_point_in_our_contour = 'y' + endif + else + if (fxy(irx,jrx) > contlo .and. fxy(irx,jrx) <= conthi) + & then + found_a_point_in_our_contour = 'y' + endif + endif + + if (found_a_point_in_our_contour == 'y') then + ! We've found a data point in our interval, something + ! that is inside the closed contour, and it hasn't been + ! marked as being found in a previous iteration of this + ! loop, so mark it now and store the (i,j) location so + ! that we can scan a ring around this point in a + ! successive iteration of this loop for more potential + ! points within this interval... + + point_is_already_in_our_contour(irx,jrx) = .true. + + next_ring_ct = next_ring_ct + 1 + search_next_i(next_ring_ct) = irx + search_next_j(next_ring_ct) = jrx + +c print *,'at B, next_ring_ct= ',next_ring_ct +c & ,' search_next_i()= ',search_next_i(next_ring_ct) +c & ,' search_next_j()= ',search_next_j(next_ring_ct) + + num_pts_in_one_contour = num_pts_in_one_contour + 1 + temp_mask_i_loc(num_pts_in_one_contour) = irx + temp_mask_j_loc(num_pts_in_one_contour) = jrx + + if (get_last_isobar_flag == 'y') then + call calcdist (glon(ix),glat(jx) + & ,glon(irx),glat(jrx),dist,degrees) + rlast_distsum = rlast_distsum + dist + rlast_distct = rlast_distct + 1 + endif + +ctm +c print *,' ' +c print *,' PT IN! irx= ',irx,' jrx= ',jrx,' xval= ' +c & ,fxy(irx,jrx) +c print *,'next_ring_ct= ',next_ring_ct +c print *,'num_pts_in_one_contour= ' +c & ,num_pts_in_one_contour + endif + +c If we've made it this far AND the +c found_a_point_in_our_contour flag indicates that this +c point is not in our contour interval, then by default that +c means that this point is for a contour interval beyond +c what we're currently looking at. E.g., if we're looking +c at the contours around a 972 mb low and we're moving +c outward and currently checking the 984-988 mb contour +c interval, it means that we found, say, a gridpoint with +c 991 mb. So we want to mark that point for a future +c iteration of this loop that would be checking the +c 988-992 mb contour interval. + + if (found_a_point_in_our_contour /= 'y' .and. + & .not. point_is_already_in_next_contour(irx,jrx)) then + ! We've found a data point that is beyond our interval, + ! so this is not a concern for finding the bounds of + ! our current contour interval, but we want to mark + ! these points and remember them for the next iteration + ! of successive_scan_loop. (For example, suppose we + ! are currently searching for points in the 984-988 mb + ! range, and we find a point that is 990 -- mark it + ! here to be remembered when we scan for 988-992 mb). + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + if (fxy(irx,jrx) >= contlo_next .and. + & fxy(irx,jrx) < conthi_next) then + ! "NEXT_CONTOUR" Comment: + ! We've found a point that is in the very next + ! contour interval.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. + else if (fxy(irx,jrx) >= conthi_next) then + ! "BEYOND_CONTOUR" Comment: + ! This point is at least 1 contour interval beyond + ! the next contour interval. Dump the info into + ! these i and j arrays. This info will be used if + ! in the next iteration of single_contour_scan_loop, + ! next_contour_ct = 0. That would mean that we + ! have, e.g., an intensely deep low with a sharp + ! mslp gradient that essentially "skips" over a + ! contour interval. E.g., if using a 4 mb interval, + ! we go from 947 to 953 AND there are NO + ! intervening gridpoints in the 948-952 interval. + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) + endif + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + if (fxy(irx,jrx) > contlo_next .and. + & fxy(irx,jrx) <= conthi_next) then + ! See "NEXT_CONTOUR" comment just above.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. +c print *,'NEXT ncc= ',next_contour_ct +c & ,'next_contour_i()= ' +c & ,next_contour_i(next_contour_ct) +c & ,'next_contour_j()= ' +c & ,next_contour_j(next_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + else if (fxy(irx,jrx) <= contlo_next) then + ! See "BEYOND_CONTOUR" comment just above.... + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'BEYOND bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + endif + endif + endif + + enddo individual_ring_loop + + enddo multiple_ring_loop + + if (next_ring_ct > 0) then + iter = iter + 1 + else + icccret = 0 + still_scanning = .false. + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + num_found_contours = num_found_contours + 1 + closed_contour = 'y' + if (num_found_contours == 1) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Closed contour found ' + endif + + endif + endif + + enddo single_contour_scan_loop + + do insingle = 1,num_pts_in_one_contour + num_pts_in_all_contours = num_pts_in_all_contours + 1 + inall = num_pts_in_all_contours + hold_mask_i_loc(inall) = temp_mask_i_loc(insingle) + hold_mask_j_loc(inall) = temp_mask_j_loc(insingle) + enddo + + if (get_last_isobar_flag == 'y') then + if (cmaxmin == 'min') then + plastbar = conthi + else + plastbar = contlo + endif + if (rlast_distct > 0) then + rlastbar = rlast_distsum / float(rlast_distct) + rlastbar = rlastbar * 0.539638 ! convert km to nm + else + rlastbar = -999.0 + endif + endif + + enddo successive_contours_loop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'END SUM: num of iterations = ',isc_count + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_land_mask (imax,jmax,ix,jx,fract_land,valid_pt + & ,dx,dy,point_is_over_water,iclmret) +c +c ABSTRACT: This subroutine looks at the values for the land-sea +c mask surrounding an input (i,j) position to determine if less +c than 50% of the area surrounding the input (i,j) position within +c 75 km radius is land. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c +c OUTPUT: +c fract_land Fraction of points/area that is covered by land +c point_is_over_water y/n: A value of 'y' is returned if <50% +c of the points/area is covered by land +c iclmret Return code from this routine +c + USE grid_bounds; USE tracked_parms + USE trkrparms; USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + logical(1) valid_pt(imax,jmax) + character point_is_over_water*1 + integer, parameter :: numazim=8 + integer iazim,ibiret1,imax,jmax,ix,jx,iclmret,imct + real bear,targlat,targlon,xplon,yplat,rdist,xintrp_mask + real fract_land,dx,dy,xmask_sum +c + iclmret = 0 + +c First, calculate the longitude and latitude of the input ix and +c jx points. If the xplon value ends up being >360.0 (this can +c happen for basin-scale HWRF), don't worry about it. Just leave +c it be, as the trigonometry will work out the same for lons >360. + + xplon = glonmin + (ix-1)*dx + yplat = glatmax - (jx-1)*dy + + rdist = 75.0 ! (We will always look only 75 km radius out for + ! this particular land-sea mask application) + + imct = 0 + +c Now go around the storm via azimloop and get interpolated +c values of the land-sea mask at each azimuth at a radial +c distance of 75 km from the center point.... + + xmask_sum = 0.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 45.0 + + call distbear (yplat,xplon,rdist,bear,targlat,targlon) + + ! These calls to bilin_int_uneven pass a variable, level, + ! that is used for applications of interpolating wind + ! data. Here, we are instead interpolating the land-sea + ! mask data, so we don't care about the level, so just + ! pass a dummy value of 850, which never gets used. + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,850,'m',xintrp_mask,ibiret1) + + if (ibiret1 == 0) then + xmask_sum = xmask_sum + xintrp_mask + imct = imct + 1 + else + iclmret = 95 + return + endif + + enddo azimloop + +c Now get the mask value directly at the point that was input to +c this routine.... + + xmask_sum = xmask_sum + lsmask(ix,jx) + imct = imct + 1 + +c Now get the mean land fraction.... + + if (imct > 0) then + + fract_land = xmask_sum / float(imct) + if (fract_land < 0.50) then + point_is_over_water = 'y' + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Land check yes: Point is over water. ' + print *,' Land check value: fract_land= ',fract_land + endif + else + point_is_over_water = 'n' + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Land check NO: Point is over land. ' + print *,' Land check value: fract_land= ',fract_land + endif + endif + + else + + iclmret = 95 + return + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine get_ijplus1_check_wrap (imax,jmax,i,j,iplus1,jplus1 + & ,iminus1,jminus1,trkrinfo,igicwret) +c +c ABSTRACT: This subroutine takes an (i,j) position input and +c returns the four neighboring (i,j) points to the east, south, +c west and north. The routine checks for wrap around the GM, so +c that if, for example, you are on a global 360x181 grid and you +c are at point i=360, then i+1 = 361, so you need something to +c adjust that back to i = 1. Likewise, if you are at i=1 and +c looking for point i-1, it will adjust it to be point 360 +c instead of the meaningless point 0 (i=0). + + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer i,j,imax,jmax,iplus1,jplus1,iminus1,jminus1,igicwret + + igicwret = 0 + + jplus1 = j + 1 + jminus1 = j - 1 + iplus1 = i + 1 + iminus1 = i - 1 + + if (iplus1 > imax) then + if (trkrinfo%gridtype == 'global') then + iplus1 = iplus1 - imax ! If wrapping east of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is too close to the eastern bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested eastern ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',iplus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (iminus1 < 1) then + if (trkrinfo%gridtype == 'global') then + iminus1 = imax + iminus1 ! If wrapping west of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested western search boundary' + print *,'!!! is too close to the western bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested western ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested western i = ',iminus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (jplus1 > jmax .or. jminus1 < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The ' + print *,'!!! user-requested northern or southern search' + print *,'!!! boundary is too close to the bounds of the' + print *,'!!! grid. Cut back your requested northern or' + print *,'!!! southern boundary by a degree or 2 in the' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested northern j = ',jminus1 + print *,'!!! User-requested southern j = ',jplus1 + print *,'!!! jmax of grid = ',jmax + print *,' ' + endif + + igicwret = 91 + return + endif + + return + end + +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + SUBROUTINE qsort(x,ind,n) +c +c Code converted using TO_F90 by Alan Miller +c Date: 2002-12-18 Time: 11:55:47 + + IMPLICIT NONE + INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(12, 60) + + REAL (dp), INTENT(IN) :: x(n) + INTEGER, INTENT(OUT) :: ind(n) + INTEGER, INTENT(IN) :: n + +c *************************************************************************** +c +c ROBERT RENKA +c OAK RIDGE NATL. LAB. +c +c THIS SUBROUTINE USES AN ORDER N*LOG(N) QUICK SORT TO SORT A REAL (dp) +c ARRAY X INTO INCREASING ORDER. THE ALGORITHM IS AS FOLLOWS. IND IS +c INITIALIZED TO THE ORDERED SEQUENCE OF INDICES 1,...,N, AND ALL INTERCHANGES +c ARE APPLIED TO IND. X IS DIVIDED INTO TWO PORTIONS BY PICKING A CENTRAL +c ELEMENT T. THE FIRST AND LAST ELEMENTS ARE COMPARED WITH T, AND +c INTERCHANGES ARE APPLIED AS NECESSARY SO THAT THE THREE VALUES ARE IN +c ASCENDING ORDER. INTERCHANGES ARE THEN APPLIED SO THAT ALL ELEMENTS +c GREATER THAN T ARE IN THE UPPER PORTION OF THE ARRAY AND ALL ELEMENTS +c LESS THAN T ARE IN THE LOWER PORTION. THE UPPER AND LOWER INDICES OF ONE +c OF THE PORTIONS ARE SAVED IN LOCAL ARRAYS, AND THE PROCESS IS REPEATED +c ITERATIVELY ON THE OTHER PORTION. WHEN A PORTION IS COMPLETELY SORTED, +c THE PROCESS BEGINS AGAIN BY RETRIEVING THE INDICES BOUNDING ANOTHER +c UNSORTED PORTION. +c +c INPUT PARAMETERS - N - LENGTH OF THE ARRAY X. +c +c X - VECTOR OF LENGTH N TO BE SORTED. +c +c IND - VECTOR OF LENGTH >= N. +c +c N AND X ARE NOT ALTERED BY THIS ROUTINE. +c +c OUTPUT PARAMETER - IND - SEQUENCE OF INDICES 1,...,N PERMUTED IN THE SAME +c FASHION AS X WOULD BE. THUS, THE ORDERING ON +c X IS DEFINED BY Y(I) = X(IND(I)). +c +c ********************************************************************* + + ! NOTE -- IU AND IL MUST BE DIMENSIONED >= LOG(N) WHERE LOG HAS BASE 2. + + !********************************************************************* + + INTEGER :: iu(21), il(21) + INTEGER :: m, i, j, k, l, ij, it, itt, indx + REAL :: r + REAL (dp) :: t + + ! LOCAL PARAMETERS - + + ! IU,IL = TEMPORARY STORAGE FOR THE UPPER AND LOWER + ! INDICES OF PORTIONS OF THE ARRAY X + ! M = INDEX FOR IU AND IL + ! I,J = LOWER AND UPPER INDICES OF A PORTION OF X + ! K,L = INDICES IN THE RANGE I,...,J + ! IJ = RANDOMLY CHOSEN INDEX BETWEEN I AND J + ! IT,ITT = TEMPORARY STORAGE FOR INTERCHANGES IN IND + ! INDX = TEMPORARY INDEX FOR X + ! R = PSEUDO RANDOM NUMBER FOR GENERATING IJ + ! T = CENTRAL ELEMENT OF X + + IF (n <= 0) RETURN + + ! INITIALIZE IND, M, I, J, AND R + + DO i = 1, n + ind(i) = i + END DO + m = 1 + i = 1 + j = n + r = .375 + + ! TOP OF LOOP + + 20 IF (i >= j) GO TO 70 + IF (r <= .5898437) THEN + r = r + .0390625 + ELSE + r = r - .21875 + END IF + + ! INITIALIZE K + + 30 k = i + + ! SELECT A CENTRAL ELEMENT OF X AND SAVE IT IN T + + ij = i + r*(j-i) + it = ind(ij) + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) > t) THEN + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + END IF + + ! INITIALIZE L + + l = j + + ! IF THE LAST ELEMENT OF THE ARRAY IS LESS THAN T, + ! INTERCHANGE IT WITH T + indx = ind(j) + IF (x(indx) >= t) GO TO 50 + ind(ij) = indx + ind(j) = it + it = indx + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) <= t) GO TO 50 + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + GO TO 50 + + ! INTERCHANGE ELEMENTS K AND L + + 40 itt = ind(l) + ind(l) = ind(k) + ind(k) = itt + + ! FIND AN ELEMENT IN THE UPPER PART OF THE ARRAY WHICH IS + ! NOT LARGER THAN T + + 50 l = l - 1 + indx = ind(l) + IF (x(indx) > t) GO TO 50 + + ! FIND AN ELEMENT IN THE LOWER PART OF THE ARRAY WHCIH IS NOT SMALLER THAN T + + 60 k = k + 1 + indx = ind(k) + IF (x(indx) < t) GO TO 60 + + ! IF K <= L, INTERCHANGE ELEMENTS K AND L + + IF (k <= l) GO TO 40 + + ! SAVE THE UPPER AND LOWER SUBSCRIPTS OF THE PORTION OF THE + ! ARRAY YET TO BE SORTED + + IF (l-i > j-k) THEN + il(m) = i + iu(m) = l + i = k + m = m + 1 + GO TO 80 + END IF + + il(m) = k + iu(m) = j + j = l + m = m + 1 + GO TO 80 + + + ! BEGIN AGAIN ON ANOTHER UNSORTED PORTION OF THE ARRAY + + 70 m = m - 1 + IF (m == 0) RETURN + i = il(m) + j = iu(m) + + 80 IF (j-i >= 11) GO TO 30 + IF (i == 1) GO TO 20 + i = i - 1 + + ! SORT ELEMENTS I+1,...,J. NOTE THAT 1 <= I < J AND J-I < 11. + + 90 i = i + 1 + IF (i == j) GO TO 70 + indx = ind(i+1) + t = x(indx) + it = indx + indx = ind(i) + IF (x(indx) <= t) GO TO 90 + k = i + + 100 ind(k+1) = ind(k) + k = k - 1 + indx = ind(k) + IF (t < x(indx)) GO TO 100 + + ind(k+1) = it + GO TO 90 + END SUBROUTINE qsort + +c +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT11/FORT31 + subroutine open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + +C ABSTRACT: This subroutine must be called before any attempt is +C made to read from the input GRIB files. The GRIB and index files +C are opened with a call to baopenr. This call to baopenr was not +C needed in the cray version of this program (the files could be +C opened with a simple Cray assign statement), but the GRIB-reading +C utilities on the SP do require calls to this subroutine (it has +C something to do with the GRIB I/O being done in C on the SP, and +C the C I/O package needs an explicit open statement). +C +C INPUT: +c inp Contains user-input info on the date & data +C lugb The Fortran unit number for the GRIB data file +C lugi The Fortran unit number for the GRIB index file +c ifh integer index for lead time level +c gfilename If using individual files for each tau, gfilename will +c contain the grib data filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +c ifilename If using individual files for each tau, gfilename will +c contain the grib index filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +C lout The Fortran unit number for the output grib file +C +C OUTPUT: +C iret The return code from this subroutine + + USE inparms + USE verbose_output + + implicit none +c + type (datecard) inp + + logical(1) output_file_open + logical(1) file_open + character(*) gfilename,ifilename +c character(120) gopen_g_file,gopen_i_file +cPeng 05/28/2018 Bug Fixed for FV3-GFS Job Crashed. + character(255) gopen_g_file,gopen_i_file + character(2) lugb_c,lugi_c + character(6) enameb,enamei + integer igoret,iioret,iooret,lugb,lugi,lout,iret,nlen1,nlen2 + + iret=0 + + if (inp%file_seq == 'onebig') then + write(lugb_c,'(i2)')lugb + write(lugi_c,'(i2)')lugi + enameb='FORT'//adjustl(lugb_c) + enamei='FORT'//adjustl(lugi_c) + call get_environment_variable(enameb,gopen_g_file,status=igoret) + call get_environment_variable(enamei,gopen_i_file,status=iioret) +c if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then +cPENG---2018-06-07-------------------------------- + if (igoret /= 0 .or. iioret /= 0 ) then + gopen_g_file(1:5) = "fort." + gopen_i_file(1:5) = "fort." + write(gopen_g_file(6:7),'(I2)') lugb + write(gopen_i_file(6:7),'(I2)') lugi + endif + else + nlen1 = len_trim(gfilename) + gopen_g_file = trim(gfilename(1:nlen1)) + nlen2 = len_trim(ifilename) + gopen_i_file = trim(ifilename(1:nlen2)) + endif + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + +c print *,'gopen_g_file= ',gopen_g_file,'....' +c print *,'gopen_i_file= ',gopen_i_file,'....' + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end + +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT12 + subroutine read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + ii=1 + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + maxstorm = numtcv + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that you have the Fortran' + print *,'!!! unit assigned right in your script.' + endif + + iret = 99 + return + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT14 + subroutine read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + ii = numtcv + 1 + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1,1x + & ,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end + +cPENG----2018-06-07 ------------------------ + subroutine get_ushe_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanushe,igridushe,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,iushet) +c +cABSTRACT: This subroutine finds the minimum and mean U-shear values +c between 200 & 850 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cPENG----2018-06-07--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07------------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanushe,dx,dy,re,ri,parmlon,parmlat + real igridushe,imeanushe + integer n,ix1,ix2,npts,imax,jmax,iushet,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ushe_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + iushet = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_ushe_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + iushet = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max ushe values at 200 and 850 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_ushe_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_ushe_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeanushe = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (ushear(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid ushe data for this level, so + ! we first call barnes now to get the mean ushe + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then +c cvort_maxmin = 'max' +c else + cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'ushe' + & ,ushear(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanushe + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cPENG--------------------------- +c imeanushe = int (xmeanushe + 0.5) + imeanushe = xmeanushe + else + imeanushe = -999. + igridushe = -999. + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_ushe_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for ushe values will not be done.') + exit report_ushe_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanushe = -999. + igridushe = -999. + exit report_ushe_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeanushe,imeanushe + 621 format (1x,' xmeanushe= ',f9.3,' imeanushe= ',f9.3) + write (6,*) ' --- mean ushe raw = ',xmeanushe + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! ushe data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,ushear(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanushe,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cPENG--------------------------- +c igridushe = int (gridpoint_maxmin + 0.5) + igridushe = gridpoint_maxmin + else + igridushe = -999. + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridushe,ifilret + 623 format (1x, + & ' grid ushe= ',f9.3,' igrid ushe= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid ushe raw= ',gridpoint_maxmin + endif + + enddo report_ushe_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get U-shear for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +cPENG----2018-06-07-------------------------------------------------- +c----------------------------------------------------------------------- + subroutine get_rhum_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanrhum,igridrhum,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,irhumt) +c +c ABSTRACT: This subroutine finds the minimum and mean RH values +c at 500 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cPENG----2018-06-07--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07------------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanrhum,dx,dy,re,ri,parmlon,parmlat + real igridrhum,imeanrhum + integer n,ix1,ix2,npts,imax,jmax,irhumt,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_rhum_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + irhumt = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_rhum_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + irhumt = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max rhum values at 500 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_rhum_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_rhum_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeanrhum = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (rhumid(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid rhum data for this level, so + ! we first call barnes now to get the mean rhum + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then +c cvort_maxmin = 'max' +c else + cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'rhum' + & ,rhumid(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanrhum + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cPENG------------------------------------------------- +c imeanrhum = int (xmeanrhum + 0.5) + imeanrhum = xmeanrhum + else + imeanrhum = -99.0 + igridrhum = -99.0 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_rhum_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for rhum values will not be done.') + exit report_rhum_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanrhum = -99.0 + igridrhum = -99.0 + exit report_rhum_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeanrhum,imeanrhum + 621 format (1x,' xmeanrhum= ',f9.3,' imeanrhum= ',f9.3) + write (6,*) ' --- mean rhum raw = ',xmeanrhum + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! rhum data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,rhumid(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanrhum,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cPENG------------------------------------------------- +c igridrhum = int (gridpoint_maxmin + 0.5) + igridrhum = gridpoint_maxmin + else + igridrhum = -99.0 + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridrhum,ifilret + 623 format (1x, + & ' grid rhum= ',f9.3,' igrid rhum= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid rhum raw= ',gridpoint_maxmin + endif + + enddo report_rhum_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get RH for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end + +cPENG----2018-06-07------------------------------------------- +c This is for area mean caculations +c--------------------------------------------------------------------- + subroutine fix_latlon_mean_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +cPENG----2018-06-07--------------------------------------- + real dsum, dnum + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.10) then + grfact = 4 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif +cPENG----2012--04--18---10X10 average ------------------------- + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (4*grfact) + iend = ipfix + (5*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (5*grfact) + iend = ipfix + (4*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (4*grfact) + iend = ipfix + (4*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (5*grfact) + jend = jpfix + (4*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (4*grfact) + jend = jpfix + (5*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (4*grfact) + jend = jpfix + (4*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix +cPENG----2018-06-07--------------------------------------- + dsum=0.0 + dnum=0.0 + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif +cPENG----2018-06-07--------------------------------------- + dsum=dsum+fxy(i,j) + dnum=dnum+1 + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +cPENG----2018-06-07--------------------------------------- + if (dnum.gt.0.0) then + gridpoint_maxmin=dsum/dnum + else + gridpoint_maxmin=-9999.0 + endif + + print *,'istart=',istart,'iend=',iend + print *,'jstart=',jstart,'jend=',jend + print *,'End of fix_latlon_mean_ij, gridpoint_maxmin = ' + & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +cPENG----2018-06-07------10X10 area mean temperature----------------- +c----------------------------------------------------------------------- + subroutine get_temp_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeantemp,igridtemp,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,itempt) +c +c ABSTRACT: This subroutine finds the maximum and mean temperature +c between 300 & 500 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cPENG----2018-06-07--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07------------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeantemp,dx,dy,re,ri,parmlon,parmlat + real igridtemp,imeantemp + integer n,ix1,ix2,npts,imax,jmax,itempt,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_temp_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + itempt = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_temp_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + itempt = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max temp values at 300 and 500 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_temp_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_temp_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeantemp = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (tmean(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid temp data for this level, so + ! we first call barnes now to get the mean temp + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' +c else +c cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'temp' + & ,tmean(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeantemp + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cPENG------------------- +c imeantemp = int (xmeantemp + 0.5) + imeantemp = xmeantemp + else + imeantemp = -99.0 + igridtemp = -99.0 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_temp_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for temp values will not be done.') + exit report_temp_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeantemp = -99.0 + igridtemp = -99.0 + exit report_temp_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeantemp,imeantemp + 621 format (1x,' xmeantemp= ',f9.3,' imeantemp= ',f9.3) + write (6,*) ' --- mean temp raw = ',xmeantemp + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! temp data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,tmean(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeantemp,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cPENG------------------- +c igridtemp = int (gridpoint_maxmin + 0.5) + igridtemp = gridpoint_maxmin + else + igridtemp = -99.0 + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridtemp,ifilret + 623 format (1x, + & ' grid temp= ',f9.3,' igrid temp= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid temp raw= ',gridpoint_maxmin + endif + + enddo report_temp_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get T-mean for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_gfs.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_gfs.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_gfs.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_gfs.f diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_gfs.f_07292019 b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_gfs.f_07292019 new file mode 100644 index 0000000000..68e5654d23 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_gfs.f_07292019 @@ -0,0 +1,27263 @@ + program trakmain +c +c$$$ MAIN PROGRAM DOCUMENTATION BLOCK +c +c Main Program: GETTRK Track model vortices +C PRGMMR: MARCHOK ORG: NP22 DATE: 2002-05-20 +c +c ABSTRACT: This program tracks the average of the max or min +c of several parameters in the vicinity of an input +c first guess (lat,lon) position of a vortex in order to give +c forecast position estimates for that vortex for a given numerical +c model. For the levels 700 & 850 mb, the tracked parameters are: +c Relative vorticity (max), wind magnitude (min), and geopotential +c height (min). Also tracked is the min in the MSLP. So many +c parameters are tracked in order to provide more accurate position +c estimates for weaker storms, which often have poorly defined +c structures/centers. Currently, the system is set up to be able +c to process GRIB input data files from the GFS, MRF, UKMET, GDAS, +c ECMWF, NGM, NAM and FNMOC/NAVGEM models. Two 1-line files +c are output from this program, both containing the forecast fix +c positions that the tracker has obtained. One of these output +c files contains the positions at every 12 hours from forecast +c hour 0 to the end of the forecast. The other file is in ATCF +c format, which is the particular format needed by the Tropical +c Prediction Center, and provides the positions at forecast hours +c 12, 24, 36, 48 and 72, plus the maximum wind near the storm center +c at each of those forecast hours. +c +c Program history log: +c 98-03-16 Marchok - Original operational version. +c 98-07-15 Marchok - Added code to calculate radii of gale-, storm-, +c and hurricane-force winds in each quadrant. +c 99-04-01 Marchok - Added code to be able to read in 4-digit years +c off of the TC Vitals records. +c Added code, including subroutine is_it_a_storm, +c to make a better determination of whether or +c not the center that was found at each time is +c the center of a storm, and not just a passing +c vort max, etc. +c 99-06-15 Marchok - Fixed a bug in calcdist that was triggered by a +c rounding error sending a number just above 1 +c into ACOS to get the distance between 2 +c identical points (which, obviously, is 0). +c 00-06-20 Marchok - Added GDAS option for vortex relocation work. +c Changed nhalf from 3 to 5. Relaxed the +c requirements for pthresh and vthresh. +c 00-11-30 Marchok - Added ability to handle GFDL and NCEP Ensemble +c model data. Extended time range to be able to +c handle 5-day capability. Forecast hours are +c now input via a namelist (easiest way to account +c for NAM, GFS and GFDL having different forecast +c lengths at 00/12z and 06/18z). Model ID's are +c now input via a namelist (makes it easier, for +c example, to run for many different ensemble +c members). Added new output, the atcfunix +c format, needed for 5-day forecasts. +c 01-08-24 Marchok Fixed a bug in rvcal and getgridinfo. When a +c grid that was south-->north is flipped in +c conv1d2d_real to be north-->south, the scanning +c mode flag remains 64 and what we would consider +c the max and min latitudes are reversed, so I +c added code to correct this in both routines. +c 02-05-20 Marchok Weakened the mslp gradient threshold and v850 +c threshold in is_it_a_storm to cut down on the +c number of dropped storms. +c 03-03-18 Marchok Fixed a bug in get_ij_bounds that was allowing +c a cos(90) and cos(-90), which then led to a +c divide by zero. +c 05-08-01 Marchok Updated to allow tracking of ECMWF hi-res, ECMWF +c ensemble, CMC hi-res, CMC ensemble, NCEP +c ensemble. +c 06-11-07 Marchok Updated to locate, and report to the atcfunix +c file, the value of the gridpoint minimum value +c of mslp. Previously, the barnes-averaged +c value had been used. +c 08-01-10 Marchok Changed the storm ID for genesis tracking so +c that the ID includes info +c on storm detection location & time. Added +c algorithms for Hart's cyclone phase space. +c Added new output fields to the atcfunix +c records, actually creating a modified atcfunix +c record, to include things such as the mean & +c max values of zeta850 & zeta700 centered on +c the storm, the speed & direction of storm +c translation, and the Hart CPS parameters. +c 10-01-07 Marchok - input grib lead time can be hrs or minutes +c - added code for warm core check +c - added code to detect genesis +c - added code to report on sfc wind structure +c - added buffer ("grid_buffer") to avoid fixing +c center to boundaries on regional grids +c - modified rvcal to report missing zeta values +c as background coriolis instead of -999, since +c the -999 was messing up center-fixing +c - added 10-m wind and sfc zeta as center-fixing +c parms. +c +c 10-05-25 Slocum Add verbose feature to code +c 0 = Not terminal output, 1 = error messages only +c 2 = all output +c +c 10-05-26 Marchok - added flags and code to check the temporal +c consistency of the mslp closed contour and +c Vt850 checks for tcgen and midlat cases. +c +c 13-04-01 Marchok Added code to upgrade the wind radii diagnosid. +c Hurricane Sandy exposed an issue with the +c tracker for large storms. The code was modified +c to use an iterative technique that can +c diagnose radii for large storms but still +c accurately diagnost radii for small storms. See +c subroutine getradii for more details. +c +c 15-11-01 Marchok Replaced the routine which tracks the wind +c minimum at the center of a storm, as that +c routine proved troublesome with very hi-res +c grids (0.02-deg) from HWRF for very small +c storms. This has been replaced with a routine +c that looks for "wind circulation difference", +c whereby the center for this parm is located at +c the spot where the tangential wind circulation +c minus the wind magnitude at the candidate +c center position is maximized. ALSO: Added in +c tracking of thickness as an additional +c tracked parm. ALSO: Added a separate verbose +c flag for only the GRIB2 read diagnostics, which +c can be voluminous. +c +c 16-09-01 Marchok Added in the ability to read in NetCDF files. +c As with GRIB data, the NetCDF data must be on +c a lat/lon grid. +c +c 17-08-31 Marchok Added a logical bitmap capability for NetCDF +c files to prevent the accessing of missing data. +c Also modified the code to permit more accurate +c reporting of the grid point value of the +c minimum SLP for reporting to the atcfunix file. +c Finally, fixed a bug (reported by JTWC) whereby +c radii were being reported for thresholds that +c were in exceedance of the tracker-diagnosed +c Vmax (e.g., 34-kt radii for a storm with +c Vmax = 25 kts). +c +c Input files: +c unit 11 Unblocked GRIB1 file containing model data +c unit 12 Text file containing TC Vitals card for current time +c unit 31 Unblocked GRIB index file +c +c Output files: +c unit 61 Output file with forecast positions every 12h from +c vt=00h to the end of the forecast +c unit 62 Output file in ATCF format, with forecast positions +c at vt = 12, 24, 36, 48 and 72h, plus wind speeds. +c unit 63 Output file with forecast wind radii for 34, 50 and +c 64 knot thresholds in each quadrant of each storm. +c +c Subprograms called: +c read_nlists Read input namelists for input date & storm number info +c read_tcv_card Read TC vitals file to get initial storm position +c getgridinfo Read GRIB file to get basic grid information +c tracker Begin main part of tracking algorithm +c +c Attributes: +c Language: Standard Fortran_90 +c +c$$$ +c +c------- +c +c LOCAL: +c +c ifhours: Integer array holding numerical forecast times for +c the input model (99 = no more times available). +c These values are read in via a namelist. +c Model numbers used: (1) GFS, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) NAM, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble (13) SREF +c Ensemble, (14) NCEP Ensemble (from ensstat mean +c fields), (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) Ensemble RELOCATION (21) UKMET hi-res (NHC) +c (23) FNMOC Ensemble +c stormswitch: This switch tells how to handle each storm in +c the TCV file: +c 1 = process this storm for this forecast hour. +c 2 = Storm was requested to be tracked, but either +c the storm went off the grid (regional models), +c the storm dissipated, or the program was +c unable to track it. +c 3 = Storm was NOT requested to be tracked at all. +c storm: An array of type tcvcard. Each member of storm +c contains a separate TC Vitals card. +c maxstorm: Maximum number of storms the system is set up to +c handle at any 1 time. +c slonfg,slatfg: Holds first guess positions for storms. The +c very first, first guess position is read from the +c TC vitals card. (maxstorm,maxtime) +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms) +c + USE def_vitals; USE inparms; USE set_max_parms; USE level_parms + USE trig_vals; USE atcf; USE trkrparms; USE verbose_output + USE netcdf_parms +c + implicit none +c + logical(1) file_open + integer date_time(8) + character (len=10) big_ben(3) + character :: ncfile*180,ncfile_has_hour0*1 + integer itret,iggret,iicret,igcret,iret,ifhmax,maxstorm,numtcv + integer iocret,enable_timing,ncfile_id,ncfile_tmax,irnhret + integer, parameter :: lugb=11,lugi=31,lucard=12,lgvcard=14,lout=51 +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + +c -------------------------------------------------------- + + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: beginning ... ',i2.2,':',i2.2,':',i2.2) + + call w3tagb('GETTRK ',1999,0104,0058,'NP22 ') + + pi = 4. * atan(1.) ! Both pi and dtr were declared in module + dtr = pi/180.0 ! trig_vals, but were not yet defined. + ncfile_has_hour0 = 'n' ! Default value; set in read_netcdf_hours +c + call read_nlists (inp,trkrinfo,netcdfinfo) + enable_timing=trkrinfo%enable_timing + + call read_fhours (ifhmax) + + call read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_tcv_card, num vitals = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_tcv_card, rc= ',iret + endif + goto 890 + endif + + call read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_gen_vitals, total number of vitals (both' + & ,' TC and non-TC) now = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_gen_vitals, rc= ' + & ,iret + endif + goto 890 + endif + + if (inp%file_seq == 'onebig') then + if (trkrinfo%inp_data_type == 'netcdf') then + ncfile = netcdfinfo%netcdf_filename + print *,' ' + print *,'before open_ncfile call, ncfile= ',ncfile + call open_ncfile (ncfile,ncfile_id) + print *,'after open_ncfile call, ncfile_id= ',ncfile_id + call read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) + if (irnhret /= 0) then + print *,'(/,a32,a5,i4,/)','!!! ERROR: in read_netcdf_hours,' + & ,' rc= ',irnhret + goto 890 + endif + else + call open_grib_files (inp,lugb,lugi,'dummy','dummy',lout,iret) + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: in open_grib_files, rc= ' + & ,iret + goto 890 + endif + endif + endif + + call tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +890 continue + + igcret=0 + iicret=0 + iocret=0 + + inquire (unit=lugb, opened=file_open) + if (file_open) call baclose(lugb,igcret) + inquire (unit=lugi, opened=file_open) + if (file_open) call baclose(lugi,iicret) + inquire (unit=lout, opened=file_open) + if (file_open) call baclose(lout,iocret) + if ( verb .ge. 3 ) then + print *,'baclose: igcret= ',igcret,' iicret= ',iicret + print *,'baclose: iocret= ',iocret + endif + call w3tage('GETTRK ') +c + stop + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +c ABSTRACT: This subroutine is the core of the program. It contains +c the main loop for looping through all the forecast hours and all +c the storms. Basically, the way it works is that it has an outer +c loop that loops on the forecast hour. At the beginning of this +c loop, the data are read in for all parameters and levels needed +c for tracking. The full regional or global grid is read in. +c If vorticity was not read in (some of the centers do not send us +c vorticity), then vorticity calculations are done on the whole +c grid at both 850 and 700 mb. Then the program goes into the inner +c loop, which loops on storm number (program originally set up to +c handle a max of 15 storms). For each storm, subroutine +c find_maxmin is called for the following parameters: Rel Vort and +c geopotential hgt at 700 & 850 mb, and MSLP. Within find_maxmin, +c a barnes analysis is performed over the guess position of the +c storm to find the max or min value, and then iteratively, the +c grid size is cut in half several times and the barnes analysis +c rerun to refine the positioning of the max or min location. After +c the center positions for these parameters have been obtained, +c subroutine get_uv_center is called to get a center fix for the +c minimum in the wind field, specifically, a minimum in the +c magnitude of the wind speed (vmag). The calculation of the vmag +c minimum is done differently than the calculation for the other +c parameters; for vmag, the grid near the storm center guess +c position is interpolated down to a very fine grid, and then +c find_maxmin is called and a barnes analysis is done on that +c smaller grid. For vmag, there are no further calls made to barnes +c with a smaller grid, since the grid has already been interpolated +c down to a smaller grid. Once all of the parameter center fixes +c have been made, subroutine fixcenter is called to average these +c positions together to get a best guess fix position. Then a check +c is done with a call to subroutine is_it_a_storm to make sure that +c the center that we have found does indeed resemble a tropical +c cyclone. Finally, subroutine get_next_ges is called to make a +c guess position for the next forecast time for this storm. +c +c INPUT: +c inp contains input date and model number information +c maxstorm maximum # of storms to be handled +c numtcv number of storms read off of the tcvitals file +c ifhmax max number of analysis & forecast times to be handled +c trkrinfo derived type that holds/describes various tracker parms +c ncfile if the input data type is netcdf, then this ncfile +c variable contains the name of the netcdf file +c ncfile_id if the input data type is netcdf, then this ncfile_id +c variable contains an integer id assigned to the netcdf +c file from the open_ncfile subroutine +c ncfile_has_hour0 character flag (y|n) that, if the tracker is +c running on NetCDF data, tells if the NetCDF file +c actually contains hour0 data or not (some, like the +c 2016 version of FV3, do not). +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file itself in +c subroutine read_netcdf_fhours. +c +c OUTPUT: +c itret return code from this subroutine +c +c LOCAL PARAMETERS: +c storm contains the tcvitals for the storms +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c maxtime Max number of forecast times program can track +c maxtp Max number of tracked parameters program will track. +c Currently (7/2015), this maxtp is 11, and these 11 are +c listed just a few lines below. +c readflag L Indicates status of read for each of 16 parms: +c 1: 850 mb absolute vorticity +c 2: 700 mb absolute vorticity +c 3: 850 mb u-comp +c 4: 850 mb v-comp +c 5: 700 mb u-comp +c 6: 700 mb v-comp +c 7: 850 mb gp hgt +c 8: 700 mb gp hgt +c 9: MSLP +c 10: near-surface u-comp +c 11: near-surface v-comp +c 12: 500 mb u-comp +c 13: 500 mb v-comp +c 14: Mean temperature, centered at 400 mb +c 15: 500 mb gp hgt +c 16: 200 mb gp hgt +c 17: Land-Sea Mask (for use in tcgen applications, and +c even there, it's optional) +c +c calcparm L indicates which parms to track and which not to. +c Array positions are defined exactly as for clon +c and clat, listed next, except that, in general, when +c flag 3 is set to a value, flag 4 is set to the same +c value as 3, and when flag 5 is set to a value, flag +c 6 is set to the same value as 5. This is because +c 3 & 4 are for the 850 mb winds, and if either u or +c v is missing, we obviously can't calculate the +c magnitude of the wind. The same applies for 5 & 6, +c which are for the 700 mb winds. And also for reference, +c here is a list of all the variables & levels for the +c tracked parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms). +c For the third position (max_#_parms), here they are: +c 1: Relative vorticity at 850 mb +c 2: Relative vorticity at 700 mb +c 3: Wind circulation difference at 850 mb +c 4: NOT CURRENTLY USED +c 5: Wind circulation difference at 700 mb +c 6: NOT CURRENTLY USED +c 7: Geopotential height at 850 mb +c 8: Geopotential height at 700 mb +c 9: Mean Sea Level Pressure +c 10: Wind circulation difference at 10 m +c 11: Relative vorticity at 10 m +c 12: Lower-level thickness (500-850) +c 13: Upper-level thickness (200-500) +c 14: Deep-Layer thickness (200-850) +c +c xmaxwind Contains maximum near-surface wind near the storm +c center for each storm at each forecast hour. +c stderr Standard deviation of the position "errors" of the +c different parameters for each storm at each time. +c fixlat,fixlon: Contain the final coordinates for each storm at +c each forecast hour. These coordinates are a +c weighted average of all the individual parameter +c positions (hgt, zeta, mslp, vmag). +c cvort_maxmin: Contains the characters 'max' or 'min', and is +c used when calling the find_maxmin routine for the +c relative vorticity (Look for max in NH, min in SH). +c vradius Contains the distance from the storm fix position to +c each of the various near-surface wind threshhold +c distances in each quadrant. +c (3,4) ==> (# of threshholds, # of quadrants) +c See subroutine getradii for further details. +c wfract_cov Fractional coverage (areal coverage) of winds +c exceeding a certain threshold (34, 50, 64 kts) in +c each quadrant. +c (5,5,3) ==> (# of quadrants + 1, # of distance bins, +c # of thresholds). +c The "extra" array size for quadrants (5, instead of 4) +c is there to hold the total (i.e., "whole disc") +c statistics. +c See subroutine get_fract_wind_cov for further details +c +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c isastorm Character array used in the call to is_it_a_storm, +c tells whether the minimum requirement for an MSLP +c gradient was met (isastorm(1)), whether for the midlat +c and tcgen cases if a closed mslp contour was found +c (isastorm(2)), and if a circulation exists at 850 mb +c (isastorm(3)). Can have a value of 'Y' (requirement +c met), 'N' (requirement not met) or 'U' (requirement +c undetermined, due to the fact that no center location +c was found for this parameter). +c maxmini These 2 arrays contain the i and j indeces for the +c maxminj max/min centers that are found using the rough check +c in first_ges_ctr and subsequent routines. Only needed +c for a midlatitude or a genesis run, NOT needed for a +c TC tracker run. +c stormct Integer: keeps and increments a running tab of the +c number of storms that have been tracked at any time +c across all forecast hours. Used only for midlat or +c tcgen runs. +c gridprs This contains the actual value of the minimum pressure +c at a gridpoint. The barnes analysis will return an +c area-averaged value of pressure; this variable will +c contain the actual minimum value at a gridpoint near +c the lat/lon found by the barnes analysis. +c closed_mslp_ctr_flag This flag keeps track of the value of the +c closed contour flag returned from subroutine +c check_closed_contour. +c vt850_flag This flag keeps track of the value of the flag for +c the 850 mb Vt check. +c----- +c + USE def_vitals; USE inparms; USE tracked_parms; USE error_parms + USE set_max_parms; USE level_parms; USE grid_bounds; USE trkrparms + USE contours; USE atcf; USE radii; USE trig_vals; USE phase + USE gen_vitals; USE structure; USE verbose_output + USE waitfor_parms; USE module_waitfor; USE netcdf_parms + USE tracking_parm_prefs +c + implicit none +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (cint_stuff) contour_info +c + character, allocatable :: closed_mslp_ctr_flag(:,:)*1 + character, allocatable :: vt850_flag(:,:)*1 + character :: r34_check_okay*1,had_to_try_backup_850_vt_check*1 + character :: need_to_expand_r34(4)*1,ncfile_has_hour0*1 + character*(*), intent(in) :: ncfile + integer :: ncfile_id +c integer, parameter :: nreadparms=17 +cPENG----2018-06-07 ------------------------ + integer, parameter :: nreadparms=19 + + real, allocatable :: prstemp(:),iwork(:) + integer, parameter :: numdist=14,numquad=4,lout=51 + integer, allocatable :: prsindex(:) + integer imax,jmax,ifh,ist,irf,jj,istmp,ifhtemp,itret,ivpa + integer isiret1,isiret2,isiret3,idum,m,iix,jjx,imode,numtcv + integer iha,isa,iua,iva,iza,maxstorm,ivort,ifix,jfix,issret + integer imoa,imoca,iksa,isda,ileadtime,leadtime_check + integer ioaret,ioaxret,ifgcret,ifmret,igugret,isoiret,icccret + integer igrret,igmwret,iorret,ignret,iovret,icbret,igucret,ita +cPENG----2018-06-07 ------------------------ + integer ish, irh + + integer ifilret,ifret,iaret,isret,iotmret,iwa,iisa,sl_counter + integer iicret,igcret,pfcret,igwcret,imbowret,iatret + logical(1), allocatable :: valid_pt(:,:) + logical(1), allocatable :: masked_outc(:,:),masked_out(:,:) + logical(1) readflag(nreadparms),calcparm(maxtp,maxstorm) + logical(1) tracking_previously_known_storms + logical(1) need_to_flip_lats,need_to_flip_lons + logical(1) file_open,first_time_thru_getradii + character cvort_maxmin*3,isastorm(3)*1,ccflag*1,gotten_avg_value*1 + character cmaxmin*3,get_last_isobar_flag*1,wcore_flag*1 + character gfilename*120,ifilename*120,gridmove_status*7 + integer vradius(3,4),igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) +cPENG----2018-06-07 ------------------------ + real igridushe,imeanushe + real igridrhum,imeanrhum + real igridtemp,imeantemp + + integer maxmini(maxstorm),maxminj(maxstorm),pdf_ct_bin(16) + integer ifcsthour,stormct,prevstormct,kf,istmspd,istmdir,iggret + integer igiret,iuret,jdum,icount,ilonfix,jlatfix,igpret,ifhmax + integer ibeg,jbeg,iend,jend,ix1,ix2,n,ilev,npts,icpsa,igzvret +cPENG----2018-06-07 ------------------------ + integer iushet, irhumt, itempt + real wcore_mean_val,wcore_point_max + + integer igfwret,ioiret,igisret,iofwret,iowsret,igwsret,igscret + integer pdf_ct_tot,lugb,lugi,iret,icmcf,iccfh,ivt8f + integer waitfor_gfile_status,waitfor_ifile_status,ncfile_tmax + integer wait_max_ifile_wait,ivr,r34_good_ct,itha,ilma,inctcv + integer date_time(8) + character (len=10) big_ben(3) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridprs(maxstorm,maxtime) + real wfract_cov(5,5,3) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real ike(max_ike_cats) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmaxwind(maxstorm,maxtime),xmeanzeta + real stderr(maxstorm,maxtime),xval(maxtp),cps_vals(3) + real gridpoint_maxmin,dist,distnm,xknots,xmaxspeed + real uvgeslon,uvgeslat,xavg,stdv,search_cutoff,re,ri,dx,dy + real xinp_fixlat,xinp_fixlon,degrees,plastbar,rlastbar + real xinterval_fhr,cc_time_sum_tot,cc_time_sum_yes + real rmax,sdp,wdp,paramb,vtl_slope,vtu_slope + real xsfclon,xsfclat,cc_time_pct,radmax,r34_dist_thresh + real prev_latmax,prev_latmin,prev_lonmax,prev_lonmin + real vradius_km,hold_old_contint,tcv_max_wind_ms + real tcv_mslp_pa,r34_from_tcv,roci_from_tcv + real proci_from_tcv,prs_contint_thresh + integer enable_timing,igrct + character(pfc_cmd_len) :: pfc_final +c + prev_latmax = -999.0 + prev_latmin = -999.0 + prev_lonmax = -999.0 + prev_lonmin = -999.0 + enable_timing=trkrinfo%enable_timing + icmcf = 0 + ivt8f = 0 + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + allocate (closed_mslp_ctr_flag(maxstorm,ifhmax),stat=icmcf) + allocate (vt850_flag(maxstorm,ifhmax),stat=ivt8f) + ! Initialize flags to 'u', not 'n'. That way, + ! when we are evaluating its value back over recent past hours, + ! we can distinguish a "no" value from an initialized value of + ! 'u' for which a storm hadn't yet been detected. + closed_mslp_ctr_flag = 'u' + vt850_flag = 'u' + endif + + allocate (prsindex(maxstorm),stat=iisa) + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iisa /= 0 .or. iva /= 0 .or. iwa /= 0 .or. icmcf /= 0 .or. + & ivt8f /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating prsindex,' + print *,'!!! prstemp or iwork array for storms: iisa = ',iisa + print *,'!!! iva= ',iva,' iwa= ',iwa,' icmcf= ',icmcf + print *,'!!! ivt8f= ',ivt8f + endif + itret = 94 + return + endif + + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + clon = 0.0 + clat = 0.0 + stderr = stermn ! initialize stderr to 0.1 (error_parms) + itret = 0 + xmaxwind = 0.0 + stormct = 0 + + ! It is critical to initialize the gridprs array to something + ! greater than normal atmospheric pressures (I've chosen 9999.99 + ! mb). This is so that in the sort on pressure before stormloop, + ! the top of the sorting index array will be filled with pressure + ! values from active storms, while those inactive 9999 storms + ! will fill the bottom of the sorting index array (prsindex). + + gridprs = 999999.0 + fixlon = -999.0 + fixlat = -999.0 + + if (inp%file_seq == 'multi') then + ! Each tau will have a separate file, starting with unit + ! number 200 (GRIB data) and 5200 (GRIB index file) and + ! incrementing upwards from there for each tau. + if (trkrinfo%gribver == 1) then + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + else + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + endif + else + ! All lead times are included in one big file. These values + ! for lugb and lugi will remain static for all taus. + lugb = 11 + lugi = 31 + endif + + ifh = 1 + + if ( verb .ge. 3 ) then + print *,'top of tracker, ifh= ',ifh,' ifhmax= ',ifhmax + endif + + ifhloop: do while (ifh <= ifhmax) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------*' + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* New forecast hour: ',i4,':',i2.2) + print *,'*-------------------------------------------*' + endif + + if (inp%file_seq == 'multi') then + + lugb = lugb + 1 + lugi = lugi + 1 + + call get_grib_file_name (ifh,gfilename,ifilename) + + if (use_waitfor == 'y') then + + ! First check for existence of grib file.... + + call waitfor(trim(gfilename),waitfor_gfile_status + & ,wait_min_age,wait_min_size,wait_max_wait + & ,wait_sleeptime) + if (waitfor_gfile_status /= 0) then + print *,' ' + write(6,405) + write(6,406) wait_max_wait,trim(gfilename) + 405 format('ERROR: TIMEOUT from waitfor for GRIB file.') + 406 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + ! Now check for existence of index file. Use a separate + ! max_wait time -- a much shorter one -- since once the + ! grib file is there, the index file should appear within + ! a matter of seconds. Also, the index file is much + ! smaller, so set the wait_min_size accordingly. + + wait_max_ifile_wait = 180 + wait_min_size = 500 + call waitfor(trim(ifilename),waitfor_ifile_status + & ,wait_min_age,wait_min_size,wait_max_ifile_wait + & ,wait_sleeptime) + if (waitfor_ifile_status /= 0) then + print *,' ' + write(6,415) + write(6,416) wait_max_ifile_wait,trim(ifilename) + 415 format('ERROR: TIMEOUT from waitfor for INDEX file.') + 416 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + endif + + call open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: from open_grib_files, rc= ' + & ,iret + print *,'!!! Files after hour0 are missing, ' + & ,'exiting normally' + stop 0 + endif + endif + + if (trkrinfo%inp_data_type == 'grib') then + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is CLOSED' + endif + endif + + !-------------------------------------------------------------- + ! Within this next IF statement, we deal with writing out atcf + ! records for storms for the case in which we have netcdf data, + ! but that netcdf data does not have hour0 data (as of Nov 2016, + ! this is the case for FV3 data). In this case, we write out + ! missing values for the hour0 time, and then we update the + ! guess for next lead time by extrapolating data from TC Vitals. + ! Note in the IF statement itself, "iftotalmins" is the array + ! of *user-requested* lead times, meaning that the user has + ! requested to look at hour0, but the ncfile_has_hour0 flag + ! indicates the hour0 time is not in the NetCDF data. + !-------------------------------------------------------------- + + if (ifh == 1 .and. iftotalmins(ifh) == 0 .and. + & trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') then + + null_netcdf_hour0_storm_loop: do inctcv = 1,numtcv + + call output_atcfunix (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,inctcv +c & ,0,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,inctcv + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',inctcv + write (6,431) storm(inctcv)%tcv_storm_id + & ,storm(inctcv)%tcv_storm_name + 431 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + + call advect_tcvitals_from_hour0 (slonfg,slatfg,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) + + if (iatret /= 0) then + fixlon (inctcv,ifh) = -999.0 + fixlat (inctcv,ifh) = -999.0 + stormswitch(inctcv) = 2 + cycle null_netcdf_hour0_storm_loop + endif + + stormswitch(inctcv) = 1 + + enddo null_netcdf_hour0_storm_loop + + ifh = ifh + 1 + cycle ifhloop + + endif + + !-------------------------------------------------------------- + ! Make call to getgridinfo in order to get info on the imax, + ! jmax, as well as the x- and y-increments, and also to see if + ! the grid is correctly oriented for the tracker so that the + ! data go north to south and west to east or if we need to flip + ! either the lats or the lons. + !-------------------------------------------------------------- + + if (trkrinfo%inp_data_type == 'grib') then + call getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) + else + print *,' ' + print *,'!!! ERROR: trkrinfo%inp_data_type NOT VALID ' + print *,'!!! trkrinfo%inp_data_type= ',trkrinfo%inp_data_type + print *,'!!! Should have value of grib or netcdf.' + print *,'!!! EXITING....' + print *,' ' + stop 93 + endif + + if (iggret == 0) then + if ( verb .ge. 1 ) then + print *,'TEST after getgridinfo in sub tracker, ' + & ,'iggret= ',iggret + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in getgridinfo, rc= ' + & ,iggret + endif + stop 95 + endif + + if (inp%modtyp == 'regional' .and. inp%nesttyp == 'moveable') + & then + if (glatmax == prev_latmax .and. glatmin == prev_latmin .and. + & glonmax == prev_lonmax .and. glonmin == prev_lonmin) then + ! The moveable, nested regional grid has not moved since + ! the last lead time. This could be an indication that the + ! model lost the storm and so the grid has not moved to + ! stay with the cyclone center. Set a flag to indicate this. + gridmove_status = 'stopped' + else + gridmove_status = 'moving' + endif + else + gridmove_status = 'notappl' + endif + + prev_latmax = glatmax + prev_latmin = glatmin + prev_lonmax = glonmax + prev_lonmin = glonmin + + gotten_avg_value = 'n' + +c First, allocate the working data arrays.... + + if (allocated(valid_pt)) deallocate (valid_pt) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) +cPENG----2018-06-07------------------------------- + if (allocated(ushear)) deallocate (ushear) + if (allocated(rhumid)) deallocate (rhumid) + + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + + ! Allocate all of the allocatable arrays.... + + allocate (valid_pt(imax,jmax),stat=ivpa) + allocate (zeta(imax,jmax,nlevzeta),stat=iza) + allocate (u(imax,jmax,nlevs),stat=iua) + allocate (v(imax,jmax,nlevs),stat=iva) + allocate (hgt(imax,jmax,nlevhgt),stat=iha) + allocate (slp(imax,jmax),stat=isa) + allocate (tmean(imax,jmax),stat=ita) +cPENG----2018-06-07------------------------------- + allocate (ushear(imax,jmax),stat=ish) + allocate (rhumid(imax,jmax),stat=irh) + + allocate (thick(imax,jmax,nlevthick),stat=itha) + allocate (lsmask(imax,jmax),stat=ilma) + allocate (masked_out(imax,jmax),stat=imoa) + allocate (masked_outc(imax,jmax),stat=imoca) + + ita=0 + icpsa=0 + if (phaseflag == 'y') then + if (phasescheme == 'cps' .or. phasescheme == 'both') then + if (allocated(cpshgt)) deallocate (cpshgt) + allocate (cpshgt(imax,jmax,nlevs_cps),stat=icpsa) + endif + endif + +c if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. +c & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. +c & itha /= 0 .or. imoa /= 0 .or. imoca /= 0 .or. ilma /= 0) +c & then +cPENG----2018-06-07 ------------------------ + if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. + & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. + & itha /= 0 .or. imoa /= 0 .or. imoca /= 0 .or. ilma /= 0 + & .or. ish /= 0 .or. irh /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating arrays.' + print *,'!!! iza = ',iza,' iua= ',iua,' iha= ',iha + print *,'!!! iva = ',iva,' isa= ',isa,' icpsa= ',icpsa + print *,'!!! iksa = ',iksa,' isda= ',isda,' ivpa= ',ivpa + print *,'!!! ita = ',ita,' imoa= ',imoa,' imoca= ',imoca + print *,'!!! itha = ',itha,' ilma= ',ilma + endif + itret = 94 + return + endif + + masked_out = .false. ! Initialize all pts to false at each hr + masked_outc = .false. ! Initialize all pts to false at each hr + + if ( verb .ge. 3 ) then + print *,'in beginning of tracker, imax= ',imax,' jmax= ',jmax + endif + +c Initialize all readflags to NOT FOUND for this forecast time, +c then call subroutine to read data for this forecast time. + + zeta = -9999.0 + u = -9999.0 + hgt = -9999.0 + v = -9999.0 + slp = -9999.0 + tmean = -9999.0 +cPENG----2018-06-07 ------------------------ + ushear = -9999.0 + rhumid = -9999.0 + + readflag = .FALSE. + + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: b4 getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + + if (trkrinfo%inp_data_type == 'grib') then + call getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,32) date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: after getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + +c Count how many parms were successfully read for this fcst time. +c Also, for right now, put the value of readflag into all of the +c calcparms for parameters 3 through 9. Note that in getdata we +c read in 17 parms, but in this next loop we only check the +c readflags up to maxtp (= 14 as of 7/2015). That's because +c parms 12 & 13 are for 500 mb u & v, which are not used for +c tracking, only for calculating the deep layer mean wind for +c the next guess, and parm 14 is the 300-500 mb mean temperature, +c which is used for determining storm phase. Parms 10 & 11 are +c for the near-surface winds, which are used in estimating surface +c winds near the storm, and will now also be used as a +c parameter for position estimates. Finally, parm 17 is the +c land-sea mask, which is not used as a tracking parm. + + idum = 0 + do irf = 1,nreadparms + if (readflag(irf)) idum = idum + 1 + if (irf > 2 .and. irf < 10) then + ! calcparm for parms > 9 is done further below. + do jj=1,maxstorm + calcparm(irf,jj) = readflag(irf) + enddo + endif + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Of ',nreadparms,' readable parms, you read in ',idum + print *,'parms for this fcst hour from the input grib file.' + endif + +c If not enough tracked parms were read in, exit the program.... + + if (idum == 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in subroutine tracker' + print *,'!!! Not enough tracked parms read in from getdata.' + print *,'!!! Check for a problem with the input GRIB file.' + print *,'!!! Model identifier = ',inp%model + print *,'!!! STOPPING EXECUTION FOR THIS MODEL' + endif + itret = 99 + ifhtemp = ifh + do while (ifhtemp <= ifhmax) + do istmp=1,maxstorm + fixlon (istmp,ifhtemp) = -999.0 + fixlat (istmp,ifhtemp) = -999.0 + enddo + ifhtemp = ifhtemp + 1 + enddo +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) + if (ifh == 1) then + ! Per Jim Gross (1/01), if the tracker ran but was unable + ! to get an initial fix (or, in this case, unable to get + ! the data needed to run), write out zeroes for the 00h + ! fixes to indicate that the tracker ran unsuccessfully, + ! but don't write out any subsequent forecast times + ! with zeroes.... + vradius = 0 + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initial value of 'undetermined' + do istmp = 1,maxstorm + if (stormswitch(istmp) /= 3) then + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0,-999.0,inp,istmp + & ,ifcsthour,0.0,0.0,vradius,maxstorm + & ,trkrinfo,-99.0,-99.0,-99.0,cps_vals + & ,wcore_flag,ioaxret) + call output_hfip (-999.0,-999.0,inp,istmp + & ,ifh,0.0,0.0,vradius,-99.0,ioaxret) + endif + enddo + endif + return + endif + +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for z850, z700 and mslp.... + + if (user_wants_to_track_gph850 == 'n' .or. + & user_wants_to_track_gph850 == 'N') then + do jj=1,maxstorm + calcparm(7,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_gph700 == 'n' .or. + & user_wants_to_track_gph700 == 'N') then + do jj=1,maxstorm + calcparm(8,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_mslp == 'n' .or. + & user_wants_to_track_mslp == 'N') then + do jj=1,maxstorm + calcparm(9,jj) = .FALSE. + enddo + endif + + +c Parameters 1 & 2 are abs vorticity at 850 & 700. If the data +c files had this parm at 850 & 700 (ECMWF & UKMET do NOT), then +c we don't need to re-calculate relative vorticity, we just need +c to subtract out the Coriolis component. If the files did not +c have vorticity, then we need to calculate relative vorticity. +c If we're able to read vorticity or calculate it, then set the +c vorticity calcparms to TRUE for all storms for now. + + vortloop: do ivort=1,2 + + if (ivort == 1) then + if (user_wants_to_track_zeta850 == 'n' .or. + & user_wants_to_track_zeta850 == 'N') then + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (ivort == 2) then + if (user_wants_to_track_zeta700 == 'n' .or. + & user_wants_to_track_zeta700 == 'N') then + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (readflag(ivort)) then + + call subtract_cor (imax,jmax,dy,ivort) + + do jj=1,maxstorm + calcparm(ivort,jj) = .TRUE. + enddo + else + if (ivort == 1) then + if (readflag(3) .and. readflag(4)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(1,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + endif + else + if (readflag(5) .and. readflag(6)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(2,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + endif + endif + endif + + enddo vortloop + + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for user preferences for the wind circulation +c difference at 850 & 700... + + if (readflag(3) .and. readflag(4)) then + if (user_wants_to_track_wcirc850 == 'n' .or. + & user_wants_to_track_wcirc850 == 'N') then + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(3,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + endif + + if (readflag(5) .and. readflag(6)) then + if (user_wants_to_track_wcirc700 == 'n' .or. + & user_wants_to_track_wcirc700 == 'N') then + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(5,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + endif + + +c Compute the sfc vorticity if sfc_u and sfc_v have been read in. + + if (readflag(10) .and. readflag(11)) then + + if (user_wants_to_track_wcircsfc == 'n' .or. + & user_wants_to_track_wcircsfc == 'N') then + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(10,jj) = .TRUE. + enddo + endif + + if (user_wants_to_track_zetasfc == 'n' .or. + & user_wants_to_track_zetasfc == 'N') then + do jj=1,maxstorm + calcparm(11,jj) = .FALSE. + enddo + else + ! The 3 in the next call to rvcal is to indicate the 3rd + ! level for the zeta array, which is for the surface (or + ! 10m) data. + call rvcal (imax,jmax,dx,dy,3,valid_pt) + do jj=1,maxstorm + calcparm(11,jj) = .TRUE. + enddo + endif + + else + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + calcparm(11,jj) = .FALSE. + enddo + endif + + +c Compute the thicknesses for 200-850, 200-500 and 500-850 mb +c if the gp hgt fields have been read in for 200, 500 and 850. + + if (readflag(7) .and. readflag(15) .and. readflag(16)) then + + call thickness_calc (imax,jmax,valid_pt) + + do jj=1,maxstorm + + if (user_wants_to_track_thick500850 == 'n' .or. + & user_wants_to_track_thick500850 == 'N') then + calcparm(12,jj) = .FALSE. + else + calcparm(12,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200500 == 'n' .or. + & user_wants_to_track_thick200500 == 'N') then + calcparm(13,jj) = .FALSE. + else + calcparm(13,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200850 == 'n' .or. + & user_wants_to_track_thick200850 == 'N') then + calcparm(14,jj) = .FALSE. + else + calcparm(14,jj) = .TRUE. + endif + + enddo + else + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Thickness will not be tracked since at least' + print *,'one of the gp height fields was not read in.' + print *,' readflag(7) -- 850 mb ---> ',readflag(7) + print *,' readflag(15) -- 500 mb ---> ',readflag(15) + print *,' readflag(16) -- 200 mb ---> ',readflag(16) + print *,' ' + endif + do jj=1,maxstorm + calcparm(12,jj) = .FALSE. + calcparm(13,jj) = .FALSE. + calcparm(14,jj) = .FALSE. + enddo + endif + +c --------------------------------------------------------------- +c Now call find_maxmin for the variables zeta, hgt and slp. Only +c process those storms for which stormswitch is set to 1. If a +c storm is selected to be processed, we still have to check the +c calcparm for each parameter, to make sure that the particular +c parm exists at that level and is able to be processed. +c +c The following commented-out data statements are just included +c as a reference so you can see the array positioning of the +c different parameters and levels that are read in: +c +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,sfc,sfc +c ,100,100,100,100,100/ +c data iglev /850,700,850,850,700,700,850,700,0,sfc,sfc +c ,500,500,400,500,200/ +c +c And also for reference, here are the variables / levels for +c the *tracked* parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c NOTE: For mid-latitude cases, we will track ONLY mslp, which +c is why we set all the other calcparms to 'false' just below. + + if (trkrinfo%type == 'midlat') then + do m = 1,maxstorm + calcparm(1,m) = .false. + calcparm(2,m) = .false. + calcparm(3,m) = .false. + calcparm(4,m) = .false. + calcparm(5,m) = .false. + calcparm(6,m) = .false. + calcparm(7,m) = .false. + calcparm(8,m) = .false. + calcparm(10,m) = .false. + calcparm(11,m) = .false. + calcparm(12,m) = .false. + calcparm(13,m) = .false. + calcparm(14,m) = .false. + enddo + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + call sort_storms_by_pressure (gridprs,ifh,maxstorm,prsindex + & ,issret) + if ( (ifh == 1) .or. + & (ifh == 2 .and. trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') ) then + stormct = numtcv + endif + endif + + prevstormct = stormct + tracking_previously_known_storms = .true. + + stormloop: do sl_counter = 1,maxstorm + + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initialized value of 'undetermined' + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + ist = prsindex(sl_counter) + else + ist = sl_counter + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + + if (ist == (prevstormct + 1)) then + + ! For the mid-latitude and tropical cyclogenesis cases, we + ! need to scan the mslp field to find new storms. If we + ! are at this point inside the if statement in stormloop, + ! then that means we have looped through and attempted to + ! track all storms that have already been found up to this + ! point in the forecast, and we need to scan the field for + ! any new storms at this forecast hour. If this is for + ! forecast hour = 0, then right off the bat we may be + ! scanning the field (if there were no tcvitals records + ! read in for this forecast), since ist = 1 and + ! (prevstormct + 1) = 0 + 1 = 1. All that the call just + ! below to first_ges_center does is return a rough idea + ! of the location of new lows; more specific locations are + ! obtained through the barnes analysis tracking algorithm + ! further below. + + if (readflag(9)) then + if (ifh > 1) then + ! We need the use of 2 different masks. One + ! (masked_out) is to be used when looking for new lows, + ! so that after we find a new low, we mask out the + ! surrounding area so we don't find it on a subsequent + ! search for this forecast hour. The other + ! (masked_outc) is used in the routine to check for a + ! closed contour. If checking for a closed contour + ! at, say 70W/25N, this and surrounding points may have + ! already been masked out in first_ges_center, so "N" + ! would misleadingly/incorrectly be returned from + ! check_closed_contour, so that is why we need 2 masks. + ! But now after the first forecast hour (t=0), the way + ! we have this set up is that we track previously known + ! storms first, and once we're done with them, we + ! search for new storms at that same forecast hour. + ! But when looking for new storms, we need to know the + ! positions of the previously tracked storms at this + ! current forecast hour, so we copy the masked_outc + ! array to masked_out in this case.... + + masked_out = masked_outc + + endif + call first_ges_center (imax,jmax,dx,dy,'mslp',slp + & ,'min',trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) + tracking_previously_known_storms = .false. + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In subroutine tracker, readflag' + print *,'!!! for mslp indicates that the mslp data' + print *,'!!! is not available for this forecast ' + print *,'!!! hour, and it is needed for a "midlat"' + print *,'!!! or "tcgen" run of the tracker. ' + print *,'!!! We will exit....' + print *,'!!! readflag(9) = ',readflag(9) + print *,'!!! ifh= ',ifh + print *,' ' + endif + itret = 98 + return + endif + endif + endif + + xval = 0.0 ! initialize entire xval array to 0 + isastorm = 'U' ! re-initialize flag for each time, each storm + + select case (stormswitch(ist)) + + case (1) + + vradius = 0 + + if ( verb .ge. 2 ) then + print *,' ---------------------------------------------' + print *,' | *** TOP OF STORM LOOP *** ' + print *,' | Beginning of storm loop in tracker for' + print *,' | Storm number ',ist + write (6,418) ifhours(ifh),ifclockmins(ifh) + 418 format (1x,' | Forecast hour: ',i4,':',i2.2) + print *,' | Storm name = ',storm(ist)%tcv_storm_name + print *,' | Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,' ---------------------------------------------' + print *,' ' + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3 + & ,'_',i3.3,a1,'_',i4.4,a1,'_',a3) + + endif +c First, make sure storm is within the grid boundaries... + + call check_bounds (slonfg(ist,ifh),slatfg(ist,ifh),ist,ifh + & ,trkrinfo,icbret) + if (icbret == 95) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + + if (slatfg(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + if (calcparm(1,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,1),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(1,ist),clon(ist,ifh,1),clat(ist,ifh,1) + & ,xval(1),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(2,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,2),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(2,ist),clon(ist,ifh,2),clat(ist,ifh,2) + & ,xval(2),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(7,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,1),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(7,ist) + & ,clon(ist,ifh,7),clat(ist,ifh,7),xval(7) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(8,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,2),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(8,ist) + & ,clon(ist,ifh,8),clat(ist,ifh,8),xval(8) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(9,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for mslp' + endif + + call find_maxmin (imax,jmax,dx,dy,'slp' + & ,slp,'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(9,ist) + & ,clon(ist,ifh,9),clat(ist,ifh,9),xval(9) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(11,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for sfc zeta' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,3),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(11,ist),clon(ist,ifh,11),clat(ist,ifh,11) + & ,xval(11),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 + + if (calcparm(12,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 500-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,1),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(12,ist) + & ,clon(ist,ifh,12),clat(ist,ifh,12),xval(12) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(13,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-500 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,2),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(13,ist) + & ,clon(ist,ifh,13),clat(ist,ifh,13),xval(13) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(14,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,3),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(14,ist) + & ,clon(ist,ifh,14),clat(ist,ifh,14),xval(14) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c Now get centers for wind circulation at 700 & 850 mb and +c at 10m. First, get a modified guess lat/lon position for +c wind circulation. Do this because we will be searching +c for this wind circulation center over a smaller area and +c so it's more crucial to have a better first guess position. +c This modified guess position will be an average of the first +c guess position for this time and the fix positions for this +c time from some of the other parameters. + + if (slatfg(ist,ifh) >= 0.0) then + cmaxmin = 'max' + else + cmaxmin = 'min' + endif + + if (calcparm(3,ist) .and. calcparm(4,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 850 mb ' + endif + + print *,' ' + print *,'Before first call to get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' inp%modtyp= ',inp%modtyp + print *,' cmaxmin= ',cmaxmin + print *,' nlev850= ',nlev850 + print *,' u(1,1,nlev850)= ',u(1,1,nlev850) + print *,' u(imax,jmax,nlev850)= ',u(imax,jmax,nlev850) + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' calcparm(3,ist)= ',calcparm(3,ist) + print *,' clon(ist,ifh,3)= ',clon(ist,ifh,3) + print *,' clat(ist,ifh,3)= ',clat(ist,ifh,3) + print *,' xval(3)= ',xval(3) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,141) date_time(5),date_time(6),date_time(7) + 141 format (1x,'TIMING: Before GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,850,valid_pt,calcparm(3,ist) + & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,142) date_time(5),date_time(6),date_time(7) + 142 format (1x,'TIMING: After GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,850,valid_pt,calcparm(3,ist) +c & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + endif + else + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + clon(ist,ifh,3) = 0.0 + clat(ist,ifh,3) = 0.0 + endif + endif + + if (calcparm(5,ist).and. calcparm(6,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 700 mb ' + endif + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,143) date_time(5),date_time(6),date_time(7) + 143 format (1x,'TIMING: Before GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,700,valid_pt,calcparm(5,ist) + & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,144) date_time(5),date_time(6),date_time(7) + 144 format (1x,'TIMING: After GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,700,valid_pt,calcparm(5,ist) +c & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + endif + else + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + clon(ist,ifh,5) = 0.0 + clat(ist,ifh,5) = 0.0 + endif + endif + + if (calcparm(10,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for the' + print *,'surface (10m) level' + endif + + ! NOTE: The 1020 in the call here is just a number/code + ! to indicate to the subroutine to process sfc winds. + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,145) date_time(5),date_time(6),date_time(7) + 145 format (1x,'TIMING: Before GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,1020,valid_pt,calcparm(10,ist) + & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) + & ,trkrinfo,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,146) date_time(5),date_time(6),date_time(7) + 146 format (1x,'TIMING: After GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,1020,valid_pt,calcparm(10,ist) +c & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) +c & ,trkrinfo,igucret) + + if (igwcret /= 0) then + calcparm(10,ist) = .FALSE. + endif + else + calcparm(10,ist) = .FALSE. + clon(ist,ifh,10) = 0.0 + clat(ist,ifh,10) = 0.0 + endif + endif + +c ------------------------------------------------------ +c All of the parameter center fixes have been done. Now +c average those positions together to get the best guess +c fix position. If a center fix is able to be made, then +c call subroutine get_max_wind to get the maximum near- +c surface wind near the center, and then call get_next_ges +c to get a guess position for the next forecast hour. + + if (stormswitch(ist) == 1) then + + call fixcenter (clon,clat,ist,ifh,calcparm + & ,slonfg(ist,ifh),slatfg(ist,ifh),inp + & ,stderr,fixlon,fixlat,xval,maxstorm,ifret) + + if (ifret == 0) then + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'regional')then + if (fixlon(ist,ifh) > (trkrinfo%eastbd + 7.0) .or. + & fixlon(ist,ifh) < (trkrinfo%westbd - 7.0) .or. + & fixlat(ist,ifh) > (trkrinfo%northbd + 7.0) .or. + & fixlat(ist,ifh) < (trkrinfo%southbd - 7.0)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! will NOT be made for this time due' + print *,'!!! the storm being more than 7 degrees' + print *,'!!! outside the user-specified lat/lon' + print *,'!!! bounds for this run. We will stop' + print *,'!!! tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + 432 format (1x,'!!! Fcst hr = ',i4,':',i2.2) + print *,'!!! fixlat= ',fixlat(ist,ifh) + print *,'!!! fixlon= ',fixlon(ist,ifh) + print *,'!!! User East Bound = ',trkrinfo%eastbd + print *,'!!! User West Bound = ',trkrinfo%westbd + print *,'!!! User North Bound = ',trkrinfo%northbd + print *,'!!! User South Bound = ',trkrinfo%southbd + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + endif + cycle stormloop + endif + endif + else + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + +c Just because we've found a center doesn't mean there is +c actually a storm there. I noticed in the first year that +c for some decaying or just weak storms, the tracker would +c identify a center to follow, but it may have only been +c a weak trough passing by, or something else that's not +c our storm. This next subroutine checks to see that the +c surface pressure gradient and/or tangential winds at +c 850 mb resemble a storm. It is called twice; the first +c time for MSLP, the 2nd time for 850 mb winds. We will +c apply these storm-checking criteria if either the mslp +c or v850 check come back negative. Remember, there +c is the possibility that centers could not be found for +c 1 or both of these parameters, in which case the isastorm +c flag will have a value of 'U', for "undetermined". + + isiret1 = 0; isiret2 = 0; isiret3 = 0 + + print *,' ttest, ifret= ',ifret + + if (ifret == 0) then + + print *,' ttest, calcparm(9,ist)= ',calcparm(9,ist) + + if (calcparm(9,ist)) then + + ! Do a check of the mslp gradient.... + + print *,' ttest, in IF part: ' + print *,' clon(ist,ifh,9)= ',clon(ist,ifh,9) + print *,' clat(ist,ifh,9)= ',clat(ist,ifh,9) + print *,' xval(9)= ',xval(9) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,clon(ist,ifh,9),clat(ist,ifh,9) + & ,xval(9),trkrinfo,isastorm(1),isiret1) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for mslp (e.g., + ! maybe the mslp fix was too far away from the + ! guess?), then this check isn't performed. We are + ! changing this so that the mslp gradient check will + ! still be performed, but using the mean fixlat and + ! fixlon positions as the center. Still, we first + ! need to check to see if mslp was even read in. If + ! it wasn't, then we are just out of luck. + + print *,' ttest, in ELSE part: ' + + if (trkrinfo%use_backup_mslp_grad_check == 'y' .or. + & trkrinfo%use_backup_mslp_grad_check == 'Y') then + + print *,' ttest ELSE, readflag(9)= ',readflag(9) + + if (readflag(9)) then + + print *,'ttest ELSE A, ist= ',ist,' ifh= ',ifh + print *,'ttest ELSE A, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE A, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,9999.0,ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + + print *,'ttest ELSE B, ifilret= ',ifilret + + if (ifilret == 0) then + + print *,'ttest ELSE B, ifilret= ',ifilret + print *,'ttest ELSE B, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE B, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,gridpoint_maxmin,trkrinfo,isastorm(1) + & ,isiret1) + + if (isiret1 == 0) then + ! Even though calcparm(9) is FALSE and mslp + ! will not be used for center-fixing + ! purposes, we need to fill the clat and clon + ! arrays just a few lines below so that + ! calls to fix_latlon_to_ij below do not + ! get screwed up. So, into the clat and clon + ! arrays we put the mean fixlat and fixlon + ! positions for this lead time. + clat(ist,ifh,9) = fixlat(ist,ifh) + clon(ist,ifh,9) = fixlon(ist,ifh) + xval(9) = gridpoint_maxmin + endif + + endif + + endif + + endif + + endif + + ! If we have found a valid mslp gradient, then make + ! a call to fix_latlon_to_ij to (1) get the actual + ! gridpoint value of the mslp (the value previously + ! stored in xval(9) is an area-averaged value coming + ! from the barnes analysis), and (2) to get the + ! (i,j) indices for this gridpoint to be used in the + ! call to check_closed_contour below. + ! + ! NOTE: If a mslp fix was not made, or if the mslp + ! "isastorm" flag comes back as no, we make the same + ! call to fix_latlon_to_ij, but we use the mean fix + ! position as our input to search around, and then + ! basically we just find the lowest mslp near that + ! mean fix position. There is a check on the value + ! of xinp_fixlat and xinp_fixlon to make sure that + ! they contain valid values and not just the + ! initialized -999 values. + + if (isiret1 == 0 .and. isastorm(1) == 'Y') then + xinp_fixlat = clat(ist,ifh,9) + xinp_fixlon = clon(ist,ifh,9) + if (verb >= 3) then + print *,' ttest at location C IF....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + else + xinp_fixlat = fixlat(ist,ifh) + xinp_fixlon = fixlon(ist,ifh) + if (verb >= 3) then + print *,' ttest at location C ELSE....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + endif + + if (xinp_fixlat > -99.0 .and. xinp_fixlon > -990.0) + & then + if (verb >= 3) then + print *,' ttest at location D' + endif + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,xinp_fixlon,xinp_fixlat + & ,xval(9),ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (verb >= 3) then + print *,' ttest at location E, ifilret= ',ifilret + endif + if (ifilret == 0) then + gridprs(ist,ifh) = gridpoint_maxmin + else + ! Search went out of regional grid bounds.... + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + print *,' ttest at location F' + + ! For a "tracker" case, check to see if the user has + ! requested to compute and write out the ROCI. If + ! so, then we make a call to check_closed_contour, + ! being sure to specify 999 as the number of levels + ! to check.... + + if (isiret1 == 0 .and. isastorm(1) == 'Y' .and. + & trkrinfo%type == 'tracker') then + + if (trkrinfo%want_oci) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + if (xval(9) < 1100.0) then + ! Pressure units are in mb... + prs_contint_thresh = 4.0 + elseif (xval(9) >80000.0) then + ! Pressure units are in Pa... + prs_contint_thresh = 400.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' tracker. The mslp value' + print *,' (xval(9)) is not in range.' + print *,' before call to' + print *,' check_closed_contour.' + print *,' xval(9) = ',xval(9) + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + if (trkrinfo%contint < prs_contint_thresh) then + hold_old_contint = trkrinfo%contint + trkrinfo%contint = prs_contint_thresh + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before going into routine to diagnose' + print *,'the ROCI for a tracker run, the ' + print *,'requested contour interval is being ' + print *,'adjusted up (coarser) to avoid having' + print *,'the contour check routine break and ' + print *,'return an invalid value.' + print *,'User-requested contint value (Pa) = ' + & ,hold_old_contint + print *,'Modified contint value (Pa) = ' + & ,trkrinfo%contint + endif + endif + + masked_outc = .false. + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ' + & ,rlastbar,' nm' + print *,' ' + endif + + endif + + endif + + ! For the midlat & tcgen cases, do a check to see if + ! there is a closed mslp contour. The ifix and jfix + ! values passed into check_closed_contour are the + ! values for the (i,j) at the gridpoint minimum, + ! which was obtained just above from the call to + ! fix_latlon_to_ij. + ! UPDATE 7/12/2016 tpm: A change was made to fix a + ! hole in the logic. Previously, for a genesis run + ! (type = midlat or tcgen), if a fix was not made + ! for mslp, then the isastorm(1) flag would not be + ! 'Y', and so the call to check_closed_contour in + ! the following IF statement would not be made, and + ! that would prevent the mask from getting updated + ! for this particular storm, allowing the same storm + ! to be detected when the scan for new storms takes + ! place at this lead time (i.e., after all previously- + ! known storms from the last lead time have been + ! tracked). As a fix, if that isastorm(1) flag is not + ! 'Y', then we call a new subroutine which updates the + ! mask based on the circulation at 850 mb. + + if (isastorm(1) == 'Y' .and. isiret1 == 0 .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ',rlastbar + & ,' nm' + print *,' ' + endif + + ! This next bit of code adds a second layer of closed + ! contour checking. This is to decrease the + ! occurrence of interrupted midlat and tcgen tracks, + ! which usually happens when the closed contour + ! criterion is not met for one time period. So in + ! this next code, we check to see if the ccflag was + ! 'y' for at least half the time over the last 24h. + ! For time periods shorter than 24h (e.g., the storm + ! was just detected at 144h and we are now at 156h), + ! the threshold is still that for at least half of + ! the time the system has been detected as a storm, + ! it must have a ccflag value of 'y'. + + if (ccflag == 'y') then + closed_mslp_ctr_flag(ist,ifh) = 'y' + else + closed_mslp_ctr_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & closed_mslp_ctr_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (closed_mslp_ctr_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.50) then + ccflag = 'y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE ON CLOSED CONTOUR CHECK: The' + print *,' ccflag returned for this hour was' + print *,' NO, but a check of recent ccflags' + print *,' indicates that more than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + ccflag = 'n' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!! NOTE ON CLOSED CONTOUR CHECK: The' + print *,'!! ccflag returned for this hour was' + print *,' NO, and a check of recent ccflags' + print *,' indicates that less than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + if (ccflag == 'y') then + isastorm(2) = 'Y' + else if (ccflag == 'n') then + isastorm(2) = 'N' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*---------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*---------------------------------------*' + print *,' ' + endif + + + else if (isastorm(1) /= 'Y' .and. + & calcparm(3,ist) .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + ! The isastorm(1) flag indicates that a mslp gradient + ! could not be found at this lead time, so the mask + ! cannot be updated using mslp. Instead, + ! do a check of the 850 mb wind circulation + ! surrounding the 850 wind circulation fix, and then + ! set the mask to be TRUE for all points within the + ! area where mean cyclonic Vt exceed +1 m/s.... + +c call check_closed_contour (imax,jmax,ifix,jfix,slp +c & ,valid_pt,masked_outc,ccflag,'min',trkrinfo +c & ,999,contour_info,get_last_isobar_flag,plastbar +c & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Calling mask_based_on_wind_circ at ' + & ,ifcsthour + endif + + call mask_based_on_wind_circ (imax,jmax,dx,dy,850 + & ,valid_pt,masked_outc,trkrinfo + & ,clon(ist,ifh,3),clat(ist,ifh,3),inp%modtyp + & ,imbowret) + + endif + + ! For tropical cyclones, check the avg 850 mb tangential + ! windspeed close to the storm center.... + + if (trkrinfo%type == 'tcgen' .or. + & trkrinfo%type == 'tracker') then + + had_to_try_backup_850_vt_check = 'n' + + if (calcparm(3,ist)) then + + if (verb .ge. 3) then + print *,' ' + print *,'Checking 850 mb Vt speed using 850 mb ' + print *,'wind circulation fix: ' + print *,' 850 mb wcirc fix lon= ',clon(ist,ifh,3) + print *,' 850 mb wcirc fix lat= ',clat(ist,ifh,3) + print *,' Multi-parm fix lon= ',fixlon(ist,ifh) + print *,' Multi-parm fix lat= ',fixlat(ist,ifh) + print *,' ' + endif + + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,clon(ist,ifh,3),clat(ist,ifh,3) + & ,xval(3),trkrinfo,isastorm(3),isiret3) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for 850 mb wind + ! circulation (maybe the 850 mb wind circulation fix + ! was too far away from the guess?), then this check + ! isn't performed. We are changing this so that the + ! 850 mb Vt wind speed check will still be + ! performed, but using the mean fixlat and fixlon + ! positions as the center. Still, we first need to + ! check to see if 850 mb u-comp and v-comp were even + ! read in. If they weren't, then we are just out + ! of luck. + + had_to_try_backup_850_vt_check = 'y' + isiret3 = -99 + + if (trkrinfo%use_backup_850_vt_check == 'y' .or. + & trkrinfo%use_backup_850_vt_check == 'Y') then + + if (readflag(3) .and. readflag(4)) then + + if (verb .ge. 3) then + print *,' ' + print *,'!!! NOTE: 850 mb wcirc fix not ' + print *,'available. We are instead ' + print *,'checking 850 mb Vt speed using ' + print *,'multi-parm fix position: ' + print *,' Multi-parm fix lon= ' + & ,fixlon(ist,ifh) + print *,' Multi-parm fix lat= ' + & ,fixlat(ist,ifh) + print *,' ' + endif + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,0.00,trkrinfo,isastorm(3),isiret3) + + endif + + endif + + endif + + if (calcparm(3,ist) .or. + & (had_to_try_backup_850_vt_check == 'y' .and. + & isiret3 == 0) ) then + + if (trkrinfo%type == 'tcgen') then + ! This next bit of code adds a second layer of 850 + ! mb Vt magnitude checking. This is to decrease + ! the occurrence of interrupted tcgen tracks, + ! which occasionally happens for weak storms when + ! this criterion is not met for one time period. + ! So in this next code, we check to see if the + ! vt850_flag was 'y' for at least 75% of the time + ! over the last 24h. For time periods shorter + ! than 24h (e.g., the storm was just detected at + ! 144h and we are now at 156h), the threshold is + ! still that for at least 75% of the time the + ! system has been detected as a storm, it must + ! have a vt850_flag value of 'y'. + + if (isastorm(3) == 'Y') then + vt850_flag(ist,ifh) = 'y' + else + vt850_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & vt850_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - + & fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (vt850_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / + & cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.75) then + isastorm(3) = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ NOTE ON Vt_850 CHECK: The ' + print *,' isastorm flag returned for ' + print *,' this hour was NO, but a' + print *,' check of recent vt850_flags' + print *,' indicates that more than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE ON Vt_850 CHECK: The ' + print *,'!!! isastorm flag returned for ' + print *,' this hour was NO, and a' + print *,' check of recent vt850_flags ' + print *,' indicates that less than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + endif + + endif + endif + + else + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + isastorm(1) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! could not be made for mslp, ' + print *,'!!! therefore we will stop tracking ' + print *,'!!! for this storm.' + endif + + else + isastorm(1) = 'N' + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a TC tracker case, a fix could' + print *,'!!! not be made using any tracked parms,' + print *,'!!! therefore we will stop tracking for' + print *,'!!! this storm.' + endif + + endif + + if ( verb .ge. 3 ) then + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + + endif + + if (isiret1 /= 0 .or. isiret2 /= 0 .or. isiret3 /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: One of the calls to ' + print *,'!!! is_it_a_storm produced an error.' + print *,'!!! Chances are this is from a call to ' + print *,'!!! get_ij_bounds, meaning we are too close' + print *,'!!! to a regional grid boundary to do this ' + print *,'!!! analysis. Processing will continue....' + print *,'!!! isiret1= ',isiret1,' isiret2= ',isiret2 + print *,'!!! isiret3= ',isiret3 + endif + + endif + + if (isastorm(1) == 'N' .or. isastorm(2) == 'N' .or. + & isastorm(3) == 'N') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! At least one of the isastorm flags from' + print *,'!!! subroutine is_it_a_storm is "N", so ' + print *,'!!! either we were unable to find a good ' + print *,'!!! mslp gradient and/or a valid 850 mb ' + print *,'!!! circulation for the storm at this time,' + print *,'!!! or, for the cases of midlat or tcgen ' + print *,'!!! tracking, a closed mslp contour could ' + print *,'!!! not be found, thus we will stop tracking' + print *,'!!! this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! mslp gradient flag = ',isastorm(1) + print *,'!!! closed contour flag = ',isastorm(2) + print *,'!!! 850 mb winds flag = ',isastorm(3) + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + + ! Now do another check for the tracker and tcgen cases. + ! If the isastorm flags for mslp gradient and v850 BOTH + ! came back positive AND you have been able to locate an + ! 850 mb vort center, just do a check to make sure that + ! the distance between the 850 vort center and the mslp + ! center is not too great. + + if (trkrinfo%type == 'tracker' .or. + & trkrinfo%type == 'tcgen') then + if (isastorm(1) == 'Y' .and. isastorm(3) == 'Y' .and. + & calcparm(1,ist) .and. stormswitch(ist) == 1) then + +c if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) >= 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) < 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else +c trkrinfo%max_mslp_850 = 323.0 +c endif + + call calcdist (clon(ist,ifh,9),clat(ist,ifh,9) + & ,clon(ist,ifh,1),clat(ist,ifh,1),dist + & ,degrees) + + if (dist > trkrinfo%max_mslp_850) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, the dist betw' + print *,'!!! the mslp center & the 850 zeta ' + print *,'!!! center is too great, thus we will' + print *,'!!! stop tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + print *,'!!! Actual distance (km) = ',dist + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Actual distance between the parm centers' + print *,'for 850 zeta and mslp is ',dist,' (km)' + print *,'Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + endif + + endif + endif + endif + + ! Do one final check. Check the new fix position and + ! the old fix position and calculate the speed that the + ! storm would have had to travel to get to this point. + ! If that speed exceeds a certain threshold (~60 kt), + ! assume you're tracking the wrong thing and quit. + ! Obviously, only do this for times > 00h. The check + ! in the if statement to see if the previous hour's + ! lats and lons were > -999 is for the midlat and + ! tcgen cases -- remember, they can have genesis at + ! any hour of the forecast, in which case the previous + ! forecast hour's lat & lon would be -999. + + if (ifh > 1 .and. stormswitch(ist) == 1) then + if (fixlon(ist,ifh-1) > -999.0 .and. + & fixlat(ist,ifh-1) > -999.0 ) then + + if (trkrinfo%type == 'midlat') then + xmaxspeed = maxspeed_ml + else + xmaxspeed = maxspeed_tc + endif + + call calcdist (fixlon(ist,ifh-1),fixlat(ist,ifh-1) + & ,fixlon(ist,ifh),fixlat(ist,ifh),dist + & ,degrees) + + ! convert distance from km to nm and get speed. + + distnm = dist * 0.539638 + xinterval_fhr = fhreal(ifh) - fhreal(ifh-1) + xknots = distnm / xinterval_fhr + + if (xknots > xmaxspeed) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, calculated spd' + print *,'!!! of the storm from the last position' + print *,'!!! to the current position is too high,' + print *,'!!! so we will stop tracking this storm' + print *,'!!! (For fear that we are not actually ' + print *,'!!! tracking our storm, but have instead' + print *,'!!! locked onto some other feature....)' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max speed allowed (kt) = ',xmaxspeed + print *,'!!! Actual speed (kt) = ',xknots + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'The average speed that the storm moved' + print *,'at since the previous forecast time is' + & ,xknots,' knots.' + endif + + endif + + endif + + endif + + endif + +c Now get the maximum near-surface wind speed near the storm +c center (get_max_wind). Also, call getradii to get the +c radii in each storm quadrant of gale-force, storm-force +c and hurricane force winds. + + if (readflag(10) .and. readflag(11) .and. ifret == 0 + & .and. stormswitch(ist) == 1) then + call get_max_wind (fixlon(ist,ifh),fixlat(ist,ifh) + & ,imax,jmax,dx,dy,valid_pt,levsfc + & ,xmaxwind(ist,ifh),trkrinfo,rmax,igmwret) +c if (igmwret /= 0 .and. gridmove_status == 'stopped') then + if (igmwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Return code from get_max_wind is /= 0. ' + print *,'!!! rcc= igmwret= ',igmwret + print *,'!!! Also, this is a moveable, regional grid' + print *,'!!! and the grid did not change from last' + print *,'!!! lead time to current one, so what has' + print *,'!!! likely happened is that the storm has ' + print *,'!!! moved close to the edge of the nested ' + print *,'!!! grid domain, but the nested grid itself' + print *,'!!! had stopped moving, probably because it' + print *,'!!! dropped or lost the storm.' + print *,'!!! ' + print *,'!!! TRACKING WILL STOP FOR THIS STORM' + print *,'!!! ' + endif + + stormswitch(ist) = 2 + cycle stormloop + endif + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + + ! For the radii, we encountered a problem with radmax + ! being too small. It was set at 650 km. Hurricane + ! Sandy exceeded this in the models, so the values + ! returned from getradii were close to the default + ! radmax value of 650 km (350 nm), instead of higher. + ! To fix it, we now use an iterative technique, where + ! we start with radmax as a small value (500 km). If + ! getradii returns a value for R34 in a quadrant that + ! does not exceed 0.97*radmax, then that value is ok. + ! If it does exceed 0.97*radmax, then we bump up radmax + ! by 50 km and call getradii again, looking to diagnose + ! radii only in those quadrants where the + ! need_to_expand_r34 flag = 'n'. BTW... note the + ! initial IF statement... we will only go into this + ! routine if the max wind just diagnosed for this lead + ! time is at least 34 kts (17.5 m/s). + + if (xmaxwind(ist,ifh) >= 17.5) then + + vradius = 0 + first_time_thru_getradii = .true. + r34_check_okay = 'n' + do ivr = 1,4 + need_to_expand_r34(ivr) = 'y' + enddo + radmax = 500.0 ! Initial radmax, in km + + igrct = 1 + + if ( verb .ge. 3 ) then + write (6,242) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 242 format (1x,'TIMING: b4 getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + getrad_iter_loop: do while + & (r34_check_okay == 'n' .and. radmax <= 1050.) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,244) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 244 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + call getradii (fixlon(ist,ifh),fixlat(ist,ifh),imax + & ,jmax,dx,dy,valid_pt,storm(ist)%tcv_storm_id + & ,ifcsthour,xmaxwind(ist,ifh),vradius + & ,trkrinfo,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) + + if (igrret /= 0) then + if (verb >= 3) then + print *,' ' + print *,'!!! ERROR: Return code from getradii = ' + & ,igrret + print *,'!!! Searching for radii will not be ' + print *,'!!! completed for this lead time and' + print *,'!!! all radii values will be set to ' + print *,'!!! missing.' + print *,' ' + exit getrad_iter_loop + endif + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,245) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 245 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + first_time_thru_getradii = .false. + igrct = igrct + 1 + r34_dist_thresh = 0.97 * radmax + r34_good_ct = 0 + do ivr = 1,4 + vradius_km = float(vradius(1,ivr)) / 0.5396 + if (vradius_km < r34_dist_thresh) then + r34_good_ct = r34_good_ct + 1 + need_to_expand_r34(ivr) = 'n' + endif + enddo + if (r34_good_ct == 4) then + r34_check_okay = 'y' + endif + radmax = radmax + 50.0 + enddo getrad_iter_loop + + if ( verb .ge. 3 ) then + write (6,246) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 246 format (1x,'TIMING: after getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + endif + + endif + +c If the user has requested so, then call a routine to +c determine the type of cyclone, using Bob Hart's +c cyclone phase space (CPS) algorithms. It is only used +c for times after t=0, since for the first check (of the +c "parameter B" thickness asymmetry), we need to know +c in which direction the storm is moving. Pulling that +c storm movement data off of the tcvitals is not reliable +c since the model storm may not be moving in the same +c direction as the observed storm. However, we could do +c an upgrade later where this storm movement data is +c pulled from the "genesis vitals", which are derived +c from the model forecast data itself, not the obs. + + if (phaseflag == 'y' .and. stormswitch(ist) == 1) then + wcore_flag = 'u' ! 'u' = undetermined +c call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm +c & ,cps_vals,wcore_flag,igpret) +cPENG----2018-06-07 ------------------------ + call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag + & ,wcore_mean_val,wcore_point_max,igpret) + + endif + + if (structflag == 'y' .or. ikeflag == 'y') then + call get_sfc_center (fixlon(ist,ifh),fixlat(ist,ifh) + & ,clon,clat,ist,ifh,calcparm,xsfclon + & ,xsfclat,maxstorm,igscret) + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,er_wind,sr_wind,er_vr,sr_vr + & ,er_vt,sr_vt,maxstorm,trkrinfo,igwsret) + if (igwsret == 0) then + call output_wind_structure (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),er_wind,sr_wind + & ,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iowsret) + endif + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,wfract_cov,pdf_ct_bin + & ,pdf_ct_tot,maxstorm,trkrinfo,igfwret) + if (igfwret == 0) then + call output_fract_wind (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),wfract_cov,'earth' + & ,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) + endif + endif + + if (ikeflag == 'y' .and. stormswitch(ist) == 1) then + call get_ike_stats (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,ike,sdp,wdp,maxstorm + & ,trkrinfo,igisret) + if (igisret == 0) then + call output_ike (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),ike,sdp,wdp,maxstorm + & ,ioiret) + endif + endif + +c Now print out the current fix position and intensity +c (in knots) to standard output. Conversion for m/s to +c knots (1.9427) is explained in output_atcf. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to fixcenter, fix positions at ' + write (6,442) ifhours(ifh),ifclockmins(ifh) + 442 format (1x,'forecast hour= ',i4,':',i2.2,' follow:') + print *,' ' + endif + + if (ifret == 0 .and. stormswitch(ist) == 1) then + + if ( verb .ge. 3 ) then + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + & ,int((xmaxwind(ist,ifh)*1.9427) + 0.5) + print *,' ' + endif + + ! Only call output routines every atcffreq/100 hours.... + + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + + if (leadtime_check == 0) then + + ifcsthour = ileadtime / 100 + + call output_atcfunix (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + + ! Get the storm motion vector and the speed of + ! motion so that we can output this in the + ! "atcf_sink" forecast text file. + + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'vitals',trkrinfo + & ,ignret) + else + istmdir = -999 + istmspd = -999 + ignret = 0 + endif + + if ( verb .ge. 3 ) then + write (6,617) istmspd,istmdir,ignret + 617 format (1x,'+++ RPT_STORM_MOTION: istmspd= ',i5 + & ,' istmdir= ',i5,' rcc= ',i3) + endif + + ! Call a routine to find the mean & max relative + ! vorticity near the storm at 850 & 700. These will + ! be written out to the "atcf_sink" fcst text file. + + imeanzeta = -99 + igridzeta = -99 + call get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) +cPENG----2018-06-07 ------------------------ + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + + call get_temp_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeantemp,igridtemp,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,itempt) + + call get_ushe_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanushe,igridushe,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,iushet) + + call get_rhum_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanrhum,igridrhum,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,irhumt) +cPENG----2018-06-07 ------------------------ + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (fixlon(ist,ifh) +c & ,fixlat(ist,ifh),inp,ist +c & ,ifcsthour,xmaxwind(ist,ifh) +c & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo +c & ,istmspd,istmdir,plastbar,rlastbar,rmax +c & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo + & ,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + + call output_atcf_sink (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta + & ,igridzeta,cps_vals,plastbar,rlastbar + & ,ioaxret) + + if (inp%model == 12 .and. ifcsthour == 0) then + ! Write vitals for GFS ens control analysis + call output_tcvitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,iovret) + + endif + endif + + ! The exception here is for the call to the output_hfip + ! routine, which will be called for every lead time + ! that is processed.... + + call output_hfip (fixlon(ist,ifh),fixlat(ist,ifh),inp,ist + & ,ifh,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,rmax,ioaxret) + else + + if ( verb .ge. 3 ) then + write (6,452) 'fixpos ',storm(ist)%tcv_storm_id + & ,' fhr= ',ifhours(ifh),ifclockmins(ifh) + & ,' Fix not made for this forecast hour' + 452 format (1x,a7,1x,a4,a6,i4,':',i2.2,a36) + + print *,' ' + print *,'!!! RETURN CODE from fixcenter not equal to 0,' + print *,'!!! or output from is_it_a_storm indicated the' + print *,'!!! system found was not our storm, or the ' + print *,'!!! speed calculated indicated we may have ' + print *,'!!! locked onto a different center, thus a fix' + print *,'!!! was not made for this storm at this ' + print *,'!!! forecast hour.' + print *,'!!! mslp gradient check = ',isastorm(1) + print *,'!!! mslp closed contour check = ',isastorm(2) + print *,'!!! 850 mb winds check = ',isastorm(3) + print *,'!!! fixcenter return code = ifret = ',ifret + print *,' ' + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + +c if (inp%model == 1 .or. inp%model == 8 .or. +c & inp%model == 22) then +cPENG + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + + ! For the vt=00h lead time, if the tracker failed to + ! locate a position, we are going to write out an + ! atcfunix record that contains the position, + ! intensity, mslp and 34-kt wind radii from TC Vitals + ! for this storm and initial time. Only do this for + ! the GFS or GDAS runs of the tracker. + + tcv_max_wind_ms = float(storm(ist)%tcv_vmax) + tcv_mslp_pa = float(storm(ist)%tcv_pcen) * 100.0 + + ! Convert tcvitals NE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15ne) + if (r34_from_tcv > 0.0) then + vradius(1,1) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,1) = 0 + endif + + ! Convert tcvitals SE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15se) + if (r34_from_tcv > 0.0) then + vradius(1,2) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,2) = 0 + endif + + ! Convert tcvitals SW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15sw) + if (r34_from_tcv > 0.0) then + vradius(1,3) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,3) = 0 + endif + + ! Convert tcvitals NW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15nw) + if (r34_from_tcv > 0.0) then + vradius(1,4) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,4) = 0 + endif + + ! Convert tcvitals roci from km to nm + + if (storm(ist)%tcv_penvrad > 0) then + roci_from_tcv = float(storm(ist)%tcv_penvrad) + rlastbar = roci_from_tcv * 0.5396 + else + rlastbar = -99.0 + endif + + ! Convert tcvitals pressure at roci from km to nm + + if (storm(ist)%tcv_penv > 0) then + proci_from_tcv = float(storm(ist)%tcv_penv) + plastbar = proci_from_tcv * 100.0 + else + plastbar = -99.0 + endif + + write (6,291) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + & ,atcfymdh + 291 format (1x,'NOTE: TCVITALS_USED_FOR_ATCF_F00 ' + & ,' Storm ID: ',a4,' Storm name: ',a9 + & ,' YMDH: ',i10) + + call output_atcfunix (slonfg(ist,ifh) + & ,slatfg(ist,ifh),inp,ist + & ,ifcsthour,tcv_max_wind_ms + & ,tcv_mslp_pa,vradius,maxstorm,trkrinfo + & ,plastbar,rlastbar,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + else + + ! For all other models, we print out missing + ! data values at tau=00h if the tracker was + ! unable to find the storm.... + + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + + endif + + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (trkrinfo%type == 'tracker') then + ! Update 11/11: For a 'tracker' run, i.e., one in + ! which we know that there is an observed storm in + ! the area, we will assume that there was some type + ! of problem in the initialization that prevented + ! the storm from being found. In this case, even + ! though we have written out zeroes for the 00h + ! time, we want to at least try tracking again at + ! the next lead time. Requested by HWRF folks.... + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',ist + write (6,301) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + 301 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + call get_next_ges (slonfg,slatfg,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + stormswitch(ist) = 1 + endif + + endif + cycle stormloop + endif + + +c Now get first guess for next forecast time's position. +c But first, if this is the first time level (ifh=1) and +c the user has requested that storm vitals be output (this +c is usually only done for model analyses in order to get +c an analysis position from one time to the next), we will +c write out a storm vitals record for this time level. +c Note that we have already gotten the next guess position +c info just above for the case of the repeated analysis +c data, so we'll just output the genesis vitals record. + + if (ifh <= ifhmax) then + if (ifh == 1 .and. trkrinfo%out_vit == 'y') then + call output_gen_vitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,istmspd,istmdir,iovret) + endif + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: Problem getting first guess ' + print *,'!!! position for next lead time. Return' + print *,'!!! code from call to get_next_ges = ' + print *,'!!! ignret = ',ignret + print *,'!!! Storm name = ' + & ,storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! TRACKING WILL STOP FOR THIS STORM.' + endif + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + else + istmdir = -999 + istmspd = -999 + endif + endif + + case (2) + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Case 2 in tracker for stormswitch' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + endif + + case (3) + continue + +c print *,' ' +c print *,'!!! Case 3 in tracker for stormswitch' +c print *,'!!! Storm name = ',storm(ist)%tcv_storm_name +c print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + if (leadtime_check == 0) then + ifcsthour = ileadtime / 100 + endif + if (trkrinfo%inp_data_type == 'grib') then + call output_tracker_mask (masked_outc,valid_pt,ifh + & ,ifcsthour,imax,jmax,iotmret) + endif + endif + + if(use_per_fcst_command=='y') then +c User wants us to run a command per forecast time + +! Replace %[FHOUR] with forecast hour, %[FMIN] with forecast minute. + +! The %[] format is chosen to avoid shell syntax errors if someone +! includes unknown %[] constructs. A stray , for example, +! would generate syntax errors or unexpected results in some +! shells. + +! If an unrecognized %[xxx] sequence is used, it will be retained in +! the final command. This allows the underlying command to detect +! the unreplaced %[] and use suitable default values or abort, as +! appropriate. + + pfc_final=per_fcst_command + call argreplace(pfc_final,pfc_cmd_len,'%[FHOUR]', & + & ifhours(ifh)) + call argreplace(pfc_final,pfc_cmd_len,'%[FMIN]', & + & iftotalmins(ifh)) + + if(verb.ge.2) then + print *,' ' + print *,'!!! Running per-fcst command' + print *,'!!! Unparsed = ',trim(per_fcst_command) + print *,'!!! Parsed = ',trim(pfc_final) + endif + call run_command(trim(pfc_final),pfcret) + if(pfcret/=0 .and. verb.ge.1) then + print *,' ' + print *,'!!! Non-zero exit status from per-fcst command' + print *,'!!! Command = ',trim(pfc_final) + print *,'!!! Exit status = ',pfcret + print *,'!!! Continuing anyway...' + elseif(pfcret==0 .and. verb.ge.2) then + print *,' ' + print *,'!!! Per-fcst command returned success status (0)' + endif + endif + + ifh = ifh + 1 + if (ifh > ifhmax) exit ifhloop + + if (inp%file_seq == 'multi') then + call baclose(lugb,igcret) + call baclose(lugi,iicret) + if ( verb .ge. 3 ) then + print *,'baclose return code for unit ',lugb,' = igcret = ' + & ,igcret + print *,'baclose return code for unit ',lugi,' = iicret = ' + & ,iicret + endif + endif + + enddo ifhloop +c +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) +c + 73 format ('fixpos ',a4,' fhr= ',i4,':',i2.2,' Fix position= ' + & ,f7.2,'E (',f6.2,'W)',2x,f7.2,' Max Wind= ',i3,' kts') + + if (allocated(prstemp)) deallocate (prstemp) + if (allocated(prsindex)) deallocate (prsindex) + if (allocated(iwork)) deallocate(iwork) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) +cPENG----2018-06-07 ------------------------ + if (allocated(ushear)) deallocate (ushear) + if (allocated(rhumid)) deallocate (rhumid) + + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(vt850_flag)) deallocate (vt850_flag) + if (allocated(closed_mslp_ctr_flag)) + & deallocate (closed_mslp_ctr_flag) + if (allocated(netcdf_file_time_values)) + & deallocate (netcdf_file_time_values) + if (allocated(nctotalmins)) + & deallocate (nctotalmins) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine argreplace(arg,n,name,val) + ! This subroutine is used to generate the pre-forecast-command + ! It will edit the command (argument "arg") and replace string + ! name with value val. That is how the per-forecast-command + ! has these modifications: + + ! %[FHOUR] -> replace with -> last forecast hour + ! %[FMIN] -> replace with -> last forecast minute + + implicit none + + integer, intent(in) :: n + character(n), intent(inout) :: arg + character(*), intent(in) :: name + integer, intent(in) :: val + + integer found,namelen,i1,i2 + character(n) :: out + + found=index(arg,name) + namelen=len(name) + i1=found-1 ! last char that is before name + i2=found+namelen ! index of last char in name + + if(found==0) return + + out=' ' + + if(found>1 .and. i21) then +! special case: name is at end of string +! hope the value fits... + write(out,'(A,I0)') arg(1:i1),val + elseif(i2 + & ,'... gopen_i_file= ...',a,'...') + + print *,'gopen_g_file= ',gopen_g_file,'....' + print *,'gopen_i_file= ',gopen_i_file,'....' + + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + inquire (unit=lout, opened=output_file_open) + if (output_file_open) then + iooret = 0 + else + fnameo(1:5) = "fort." + write(fnameo(6:7),'(I2)') lout + call baopenw (lout,fnameo,iooret) + endif + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + inquire (file=gopen_g_file, opened=file_open4) + if (file_open4) then + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is OPEN' + else + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is CLOSED' + endif + + inquire (file=gopen_i_file, opened=file_open5) + if (file_open5) then + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is OPEN' + else + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'gettrk baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine open_ncfile (filename,ncid) + +c ABSTRACT: This subroutine opens a netcdf file specified by the +c input file "ncfile" and returns the netcdf file id that will be +c associated with that file. +c +c INPUT: +c ncfile character full-path file netcdf name +c +c OUTPUT: +c ncfile_id integer, netcdf id assigned to the netcdf file + + implicit none + + include "netcdf.inc" + + character*(*), intent(in) :: filename + integer, intent(out) :: ncid + integer :: status + + status = nf_open (filename, NF_NOWRITE, ncid) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine open_ncfile +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine is_it_a_storm (imax,jmax,dx,dy,cparm,ist + & ,defined_pt,parmlon,parmlat + & ,parmval,trkrinfo,stormcheck,isiret) + +c ABSTRACT: This subroutine is called after the center of the storm +c has been fixed. Its purpose is to determine whether or not +c the center that was found is actually a storm, and not just some +c passing trough (this has happened in the case of decaying or weak +c storms). It's called twice -- once to check for a minimum MSLP +c gradient, and once to check for a circulation at 850 mb. The +c subroutine input parameter "cparm" determines which parameter to +c check for. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is to be checked: +c slp = mslp, for a check of mslp gradient +c v850 = tangential winds at 850 mb +c ist integer storm number (internal to the tracker) +c defined_pt Logical; bitmap indicating if valid data at that pt. +c parmlon Longitude of the max/min value for the input parameter +c parmlat Latitude of the max/min value for the input parameter +c parmval Data value at parm's max/min point (used for mslp call) +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c stormcheck Character; set to 'Y' if mslp gradient or 850 mb +c tangential winds check okay. +c isiret Return code for this subroutine. +c + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE tracked_parms; USE atcf; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + real vt,vtavg,vr,parmlat,parmlon,parmval,dist + real pthresh,vthresh,degrees,dx,dy,dell,ri,radinf + real pgradient,xmaxpgrad + character(*) cparm + logical(1) defined_pt(imax,jmax) + character*1 stormcheck + integer isiret,imax,jmax,ist,npts,ilonfix,jlatfix,igvtret + integer ibeg,iend,jbeg,jend,ivt,i,j,iix,jix,bskip,igiret + + isiret = 0 + stormcheck = 'N' + + dell = (dx+dy)/2. + +c First define the radius of influence, which depends on the +c grid spacing of the model data being used. The ceiling statement +c for npts in the first if statement is needed in case the +c resolution of the grib files eventually goes very low, down to +c say a half degree or less, in order to cover enough points in +c the search. + + if (dell < 1.24) then ! GFS, MRF, NAM, NGM, NAVGEM, GDAS, + ! GFDL, NCEP Ensemble & Ensemble + ! Relocation, SREF Ensemble + ri = ritrk_most + if (cparm == 'slp') then + radinf = 300.0 + else + radinf = 225.0 + endif + npts = ceiling(radinf/(dtk*(dx+dy)/2.)) + else if (dell >= 1.24 .and. dell < 2.49) then ! UKMET + ri = ritrk_most + radinf = 275.0 + npts = 2 + else ! ECMWF + ri = ritrk_coarse + radinf = 350.0 + npts = 1 + endif + + pthresh = trkrinfo%mslpthresh ! These are read in in + vthresh = trkrinfo%v850thresh ! subroutine read_nlists.... + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,parmlon,parmlat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij B, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij B, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print*,' ' + print*,'!!! ERROR in is_it_a_storm from call to' + print*,'!!! get_ij_bounds, stopping processing for ' + print*,'!!! storm number ',ist + endif + + isiret = 92 + return + endif + +c If the input cparm is slp, then check to see that the MSLP +c gradient in any direction from the MSLP center is at least +c 1mb / 200km, or 0.005mb/km. This is based on discussions with +c Morris & Bob, who have had good results using a 2mb/200km +c requirement. Since their model has a much finer resolution than +c all of the models we run the tracker on AND a much better +c depiction of the hurricane vortex, we do not use a requirement +c as strict as theirs, and so make the requirement only half as +c strong as theirs. +c +c If the input cparm is v850, then check to see that there is +c a circulation at 850 mb. We will do this by calculating the +c tangential wind of all points within a specified radius of +c the 850 minimum wind center, and seeing if there is a net +c average tangential wind speed of at least 5 m/s. +c +c UPDATE APRIL 2000: I've relaxed the thresholds slightly from +c 0.005 mb/km to 0.003 mb/km, and the wind threshold from +c 5 m/s to 3 m/s. Also, note that a special case for GDAS has +c been hardwired in that is weaker (0.002 mb/km and 2 m/s). +c That weaker GDAS requirement is for Qingfu's relocation stuff. +c +c UPDATE JULY 2001: The relaxed requirement put in place in +c April 2000 for the GDAS relocation has also been put in place +c for the GFS ensemble relocation. + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the loop. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, ilonfix= ',ilonfix + & ,' jlatfix= ',jlatfix + print *,'ibeg jbeg iend jend = ',ibeg,jbeg,iend,jend + print *,'cparm= ',cparm,' parmlon parmlat = ',parmlon,parmlat + print *,'parmval= ',parmval + print *,' ' + endif + + vtavg = 0.0 + ivt = 0 + + xmaxpgrad = -999.0 + + jloop: do jix = jbeg,jend,bskip + iloop: do iix = ibeg,iend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine is_it_a_storm' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + i = iix - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine ' + print *,'!!! is_it_a_storm for a non-global grid.' + print *,'!!! STOPPING....' + print *,'!!! i= ',i,' imax= ',imax + print *,' ' + endif + + stop 97 + endif + endif + + call calcdist(parmlon,parmlat,glon(i),glat(j),dist,degrees) + + if (dist > radinf .or. dist == 0.0) cycle + + if (defined_pt(i,j)) then + + if (cparm == 'slp') then + pgradient = (slp(i,j) - parmval) / dist + if (pgradient > xmaxpgrad) xmaxpgrad = pgradient + + if ( verb .ge. 3 ) then + write (6,93) i,j,glon(i),glat(j),dist,slp(i,j),pgradient + endif + + if (pgradient > pthresh) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, valid pgradient found.' + print '(a23,f8.5)',' pgradient threshold = ',pthresh + print '(a23,f8.5)',' pgradient found = ',pgradient + print *,'mslp center = ',parmlon,parmlat,parmval + print *,'pgrad loc = ',glon(i),glat(j),slp(i,j) + endif + + stormcheck = 'Y' + exit jloop + endif + endif + + if (cparm == 'v850') then + call getvrvt (parmlon,parmlat,glon(i),glat(j) + & ,u(i,j,nlev850),v(i,j,nlev850),vr,vt,igvtret) + if ( verb .ge. 3 ) then + write (6,91) i,j,glon(i),glat(j),u(i,j,nlev850) + & ,v(i,j,nlev850),vr,vt + endif + + vtavg = vtavg + vt + ivt = ivt + 1 + endif + + endif + + enddo iloop + enddo jloop + + 91 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' u= ',f8.4,' v= ',f8.4,' vr= ',f9.5,' vt= ',f9.5) + + 93 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' dist= ',f8.2,' slp= ',f10.2,' pgradient= ',f8.5) + + if (stormcheck /= 'Y' .and. cparm == 'slp') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, valid pgradient NOT FOUND.' + write (6,94) '!!! (Max pgradient less than ',pthresh,' mb/km)' + 94 format (1x,a29,5x,f8.5,a7) + write (6,95) '!!! Max pgradient (mb/km) found = ',xmaxpgrad + 95 format (1x,a34,f8.5) + print *,' ' + endif + + endif + + if (cparm == 'v850') then + + if (ivt > 0) then + vtavg = vtavg / float(ivt) + else + vtavg = 0.0 + endif + + if (parmlat > 0) then + if (vtavg >= vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (>= +',vthresh,' m/s for a NH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed +',vthresh + & ,' m/s (NH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + else + if (vtavg <= -vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (<= -',vthresh,' m/s for a SH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed -',vthresh + & ,' m/s (SH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + endif + + endif +c + return + end +c +c----------------------------------------------------------------------- +ccPENG----2018-06-07 ------------------------ +c----------------------------------------------------------------------- +c subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm +c & ,cps_vals,wcore_flag,igpret) + subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag + & ,wcore_mean_val,wcore_point_max,igpret) + +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure or phase of a cyclone. Initially, we +c will just have it use the Hart cyclone phase space (CPS) scheme. + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trkrparms; USE grid_bounds + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character wcore_flag*1,okay_to_call_cps_routines*1 +cPENG----2018-06-07 ------------------------ + real wcore_mean_val,wcore_point_max + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real cps_vals(3) + real dx,dy,paramb,vtl_slope,vtu_slope + integer imax,jmax,igpret,igcpret,ist,ifh,maxstorm + integer igvpret,igcv1ret,igcv2ret + logical(1) valid_pt(imax,jmax) +c + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,611) + write (6,613) + write (6,615) + write (6,*) ' ' + + 611 format(1x,'#-----------------------------------------------#') + 613 format(1x,'# start of routine to determine cyclone phase...#') + 615 format(1x,'#-----------------------------------------------#') + endif + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + if (ifh > 1 .or. (ifh == 1 .and. trkrinfo%type == 'tracker')) + & then + + ! This condition that ifh > 1 is so that we *not* do the cps + ! stuff for fhour=0 if it's a tcgen or midlat case, since we + ! don't know the model storm motion direction for the + ! analysis. For a regular case where type = 'tracker', we + ! have the observed storm's heading direction from tc vitals, + ! so we can use that (even though the model's storm direction + ! may differ slightly from the observed storm). This current + ! if statement and the ones below carefully check for these + ! various instances. + + okay_to_call_cps_routines = 'n' + + if (ifh > 1) then + if (fixlon(ist,ifh-1) > -990.0 .and. + & fixlat(ist,ifh-1) > -990.0) then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level ' + print *,' >< since the fixlon and fixlat at the ' + print *,' >< previous lead time are undefined.' + print *,' >< This is likely the first found position' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + endif + endif + elseif (ifh == 1 .and. trkrinfo%type == 'tracker') then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level.' + print *,' >< The likely reason is that ifh=0 and' + print *,' >< this is a genesis case, so we do not ' + print *,' >< know the storm motion direction.' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + print *,' >< trkrinfo%type ',trkrinfo%type + endif + endif + + if (okay_to_call_cps_routines == 'y') then + + ! Similarly, these next two conditions (previous lat and + ! previous lon > -999) are in there in case we're doing a + ! tcgen or midlat case and this is the *first* time level + ! within a forecast that the storm has been detected (again, + ! we don't yet know the storm heading). + + call get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'lower',vtl_slope + & ,maxstorm,igcv1ret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'upper',vtu_slope + & ,maxstorm,igcv2ret) + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh) + & ,paramb,vtl_slope,vtu_slope + endif + + cps_vals(1) = paramb + cps_vals(2) = vtl_slope + cps_vals(3) = vtu_slope + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diagnostics were requested but will NOT' + print *,' >< be performed for this time level since we ' + print *,' >< are either at the first time level for a ' + print *,' >< genesis case (type = midlat or tcgen), or' + print *,' >< we are at any time level in which for some' + print *,' >< reason the fixlon and fixlat at the' + print *,' >< previous time level are not defined.' + print *,' >< ifh= ',ifh + endif + + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diags were requested but will NOT be' + print *,' >< performed for this time level since we are at' + print *,' >< time level 1 for a genesis case ' + print *,' >< (type = midlat or tcgen) and we cannot' + print *,' >< diagnose the model direction of storm' + print *,' >< movement. ifh= ',ifh + endif + + endif + + endif + + 73 format ('cps_stats: ',a4,' lead time= ',i3,':',i2,' paramb= ' + & ,f8.2,' vtl= ',f9.2,' vtu= ',f9.2) + + + if (phasescheme == 'vtt' .or. phasescheme == 'both') then +c call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +cPENG----2018-06-07 ------------------------ + call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag + & ,wcore_mean_val,wcore_point_max,igvpret) + + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + + 631 format(1x,'#-------------------------------------------------#') + 633 format(1x,'# End of routine to determine cyclone phase... #') + 635 format(1x,'#-------------------------------------------------#') + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines "Parameter B", which determines +c the degree of thermal symmetry between the "left" and "right" +c hemispheres of a storm, in the layer between 900 and 600 mb. +c We evaluate only those points that are within 500 km of the +c storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zthicksum(2) + real rlonc,rlatc,rlonb,rlatb,xdist,degrees,d,cosarg + real st_heading,st_heading_rad,ricps,dx,dy + real pt_dir,pt_dir_rad,zthick,hemval,paramb + real zthick_right_mean,zthick_left_mean + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer left_ct,right_ct,hemis,icount,maxstorm,ip + logical(1) valid_pt(imax,jmax) +c + ricps = 500.0 + +c ----------------------------------------------------------------- +c First, determine the angle that the storm took getting from the +c last position to the current one. If this is for ifh=1 for a +c regular type=tracker case, we will just use the storm direction +c as read from the tcvitals card. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + if (d == 0.0) then + + ! Storm is stationary... + st_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + print '(a43,f9.3)' + & ,' In get_cps_paramb, model storm heading = ' + & ,st_heading + print *,' ' + endif + +c ----------------------------------------------------------------- +c Now call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_paramb from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + igcpret = 92 + return + endif + +c ----------------------------------------------------------------- +c Now loop through all of the points of the subdomain. If the +c point is further than 500 km from the storm center, discard it. +c Otherwise, evaluate the angle from the storm center to this point +c to determine the hemisphere of the point, that is, if the point +c is to the left or the right of the storm track. +c ----------------------------------------------------------------- + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the + ! loop for the evaluation of parameter B. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + left_ct = 0 + right_ct = 0 + zthicksum = 0 + icount = 0 + +c print *,'CPS CORE: ibeg= ',ibeg,' iend= ',iend +c print *,'CPS CORE: jbeg= ',jbeg,' jend= ',jend + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + icount = icount + 1 + +c print *,'CPS CORE: ist= ',ist,' ifh= ',ifh,' j= ',j,' i= ',i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_paramb, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Parameter B will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_paramb' + print *,'!!! for a non-global grid.' + print *,'!!! Parameter B will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_PARAMB....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon= ',glon(ip),' glat= ',glat(j) + print *,'!!! Parameter B will not be computed.' + print *,'!!! EXITING GET_CPS_PARAMB....' + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + + !---------------------------------------------------------- + ! Calculate angle from storm center to point, in a 0-360 + ! framework, clockwise positive. + !---------------------------------------------------------- + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-fixlon(ist,ifh)) * dtr + rlatb = fixlat(ist,ifh) * dtr + d = degrees * dtr + + if (d > 0.) then + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_dir_rad = acos(cosarg) + else + pt_dir_rad = 2*pi - acos(cosarg) + endif + else + pt_dir_rad = 0.0 + endif + + pt_dir = pt_dir_rad / dtr + + !------------------------------------------------------------ + ! Based on the angle that the point is from the storm center, + ! determine if the point is to the left or the right of the + ! storm track. + !------------------------------------------------------------ + + if (st_heading >= 180.0) then + if ((st_heading - pt_dir) > 0.0 .and. + & (st_heading - pt_dir) <= 180) then + hemis = 2 + left_ct = left_ct + 1 + else + hemis = 1 + right_ct = right_ct + 1 + endif + else + if ((pt_dir - st_heading) > 0.0 .and. + & (pt_dir - st_heading) <= 180) then + hemis = 1 + right_ct = right_ct + 1 + else + hemis = 2 + left_ct = left_ct + 1 + endif + endif + + !------------------------------------------------------------ + ! Calculate the 600-900 mb thickness at this point and add + ! the thickness value to the array for the correct "storm + ! hemisphere". + !------------------------------------------------------------ + + zthick = cpshgt(ip,j,7) - cpshgt(ip,j,1) + zthicksum(hemis) = zthicksum(hemis) + zthick + + if ( verb .ge. 3 ) then + write (6,51) rlonb/dtr,rlatb/dtr,rlonc/dtr,rlatc/dtr + & ,st_heading,pt_dir,hemis,zthick + endif + + enddo iloop + enddo jloop + + 51 format (1x,'stlon stlat = ',2(f6.2,2x),' ptlon ptlat = ' + & ,2(f6.2,2x),' sthead= ',f6.2,' ptdir= ',f6.2,' hemis= ' + & ,i1,' zthick= ',f7.2) + +c ------------------------------------------------------------------ +c Now calculate parameter B. The hemval parameter = +1 for storms +c in the Northern Hemisphere and -1 for Southern Hemisphere storms. +c ------------------------------------------------------------------ + + zthick_right_mean = zthicksum(1) / float(right_ct) + zthick_left_mean = zthicksum(2) / float(left_ct) + + if (fixlat(ist,ifh) < 0.0) then + hemval = -1.0 + else + hemval = 1.0 + endif + + paramb = hemval * (zthick_right_mean - zthick_left_mean) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' right_ct= ',right_ct,' left_ct= ',left_ct + print *,' zthicksum(1)= ',zthicksum(1) + print *,' zthicksum(2)= ',zthicksum(2) + print *,' zthick_right_mean= ',zthick_right_mean + print *,' zthick_left_mean= ',zthick_left_mean + print *,' hemval= ',hemval + print *,' END of get_cps_paramb, paramb= ',paramb + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,clayer,vth_slope,maxstorm,igcvret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines the thermal wind profile for +c either the lower troposphere (i.e., between 600 and 900 mb) or the +c upper troposphere (i.e., between 300 and 600 mb). We evaluate +c only those points that are within 500 km of the storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character clayer*5 + real tmp1,tmp2,tmp3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zmax(7),zmin(7),zdiff(7),xlolevs(7),xhilevs(7),plev(7) + real dlnp(7),dzdlnp(7),dz(7),lnp(7) + real vth_slope,xdist,degrees,d,cosarg + real ricps,dx,dy,R2 + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j,k,kix + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igcvret,igiret + integer kbeg,kend,maxstorm,ip + logical(1) valid_pt(imax,jmax) + + data xlolevs /900.,850.,800.,750.,700.,650.,600./ + data xhilevs /600.,550.,500.,450.,400.,350.,300./ +c data xlolevs /90000.,85000.,80000.,75000.,70000.,65000.,60000./ +c data xhilevs /60000.,55000.,50000.,45000.,40000.,35000.,30000./ +c + ricps = 500.0 + plev = 0.0 + + if (clayer == 'lower') then + kbeg = 1 + kend = 7 + plev = xlolevs + else + kbeg = 7 + kend = 13 + plev = xhilevs + endif + +c ----------------------------------------------------------------- +c First, call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_vtl from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + igcvret = 92 + return + endif + +c ------------------------------------------------------------------ +c Now loop through all of the points of the subdomain at each level. +c If a point is further than 500 km from the storm center, discard +c it. Otherwise, evaluate the gp height at the point to determine +c if it is a max or a min for the given level. Store the max and +c min height at each level in an array. +c ------------------------------------------------------------------ + +c ! We will want to speed things up for finer resolution grids. +c ! We can do this by skipping some of the points in the +c ! loop for the evaluation of parameter B. +c +c if ((dx+dy)/2. > 0.20) then +c bskip = 1 +c else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then +c bskip = 2 +c else if ((dx+dy)/2. <= 0.10) then +c bskip = 3 +c endif + + bskip = 1 ! Don't do any skipping for now.... + + zmax = -9999999.0 + zmin = 9999999.0 + zdiff = 0.0 + lnp = 0.0 + + levloop: do k = kbeg,kend + + if (kbeg == 7) then + ! processing upper layers (600-300 mb) + kix = k - 6 + else + ! processing lower layers (900-600 mb) + kix = k + endif + + lnp(kix) = log(plev(kix)) + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_vth, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_vth' + print *,'!!! for a non-global grid.' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_VTH....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j,' k= ',k + & ,' clayer= ',clayer + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon(ip)= ',glon(ip),' glat= ',glat(j) + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! EXITING GET_CPS_VTH....' + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + + tmp1 = zmax(kix) + tmp2 = cpshgt(ip,j,k) + tmp3 = zmin(kix) + + zmax(kix) = max(tmp1,tmp2) + zmin(kix) = min(tmp3,tmp2) + +c zmax(kix) = max(zmax(kix),cpshgt(ip,j,k)) +c zmin(kix) = min(zmin(kix),cpshgt(ip,j,k)) + + enddo iloop + enddo jloop + + zdiff(kix) = zmax(kix) - zmin(kix) + + enddo levloop + +c ------------------------------------------------------------------ +c Now calculate the vertical derivative of the gp height, that is, +c d(dz)/d(ln(p)). Here, zdiff is the gp height perturbation at a +c given level, calculated in the loop above; dz is the vertical +c change in that perturbation from one level to the next. +c ------------------------------------------------------------------ + + dz = 0.0 + dlnp = 0.0 + dzdlnp = 0.0 + + do k = 2,7 + dz(k) = zdiff(k) - zdiff(k-1) + dlnp(k) = log(plev(k)) - log(plev(k-1)) + dzdlnp(k) = dz(k) / dlnp(k) + enddo + +c ------------------------------------------------------------------ +c Now call a correlation routine to get the slope of a regression +c line. The independent variable that we input is dlnp, the change +c in log of pressure with height. The dependent variable is +c dzdlnp, the vertical change in the height perturbation with +c respect to the change in pressure. The slope that is returned +c defines whether we've got a cold core or warm core system. +c See Hart (MWR, April 2003, Vol 131, pp. 585-616) for more +c details, specifically his Fig. 3 and the discussion surrounding. +c Note that in the call to calccorr, we are sending only 6 of the +c 7 elements of the dlnp and dzdlnp arrays, beginning with the +c 2nd element of each. That's because the first array value for +c each of those arrays is empty, since in the loop just above, we +c start with kbeg+1, not kbeg. +c ------------------------------------------------------------------ + + call calccorr(lnp(2),zdiff(2),6,R2,vth_slope) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ In get_cps_vth, values for vth follow for ' + & ,'lead time= ',ifhours(ifh),':',ifclockmins(ifh),' ' + & ,storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' ... clayer = ',clayer + print *,' ' + endif + + do k = kbeg,kend + + if (kbeg == 7) then + kix = k - 6 + else + kix = k + endif + + if ( verb .ge. 3 ) then + print *,' ' + write (6,31) k,plev(kix),zmax(kix),zmin(kix),zdiff(kix) + if (kix > 1) then + write (6,32) plev(kix),log(plev(kix)) + & ,plev(kix-1),log(plev(kix-1)) + write (6,33) dz(kix),dlnp(kix),dzdlnp(kix) + else + write (6,34) + endif + endif + + enddo + + 31 format (1x,' +++ k= ',i2,' press= ',f8.1,' zmax= ',f7.2 + & ,' zmin= ',f7.2,' zdiff= ',f7.2) + 32 format (1x,' ln(',f7.1,')= ',f9.6,' ln(',f7.1,')= ',f9.6) + 33 format (1x,' dz= ',f10.2,' dlnp= ',f13.6,' dzdlnp= ',f12.3) + 34 format (1x,' --- First level... no derivatives done...') +c + return + end +c +C---------------------------------------------------- +C +C---------------------------------------------------- + subroutine calccorr(xdat,ydat,numpts,R2,slope) +c +c This subroutine is the main driver for a series of +c other subroutines below this that will calculate the +c correlation between two input arrays, xdat and ydat. +c +c INPUT: +c xdat array of x (independent) data points +c ydat array of y (dependent) data points +c numpts number of elements in each of xdat and ydat +c +c OUTPUT: +c R2 R-squared, the coefficient of determination +c slope Slope of regression line +c +c xdiff array of points for xdat - xmean +c ydiff array of points for ydat - ymean +c yestim array of regression-estimated points +c yresid array of residuals (ydat(i) - yestim(i)) + + USE verbose_output + + implicit none + + real xdat(numpts),ydat(numpts) + real xdiff(numpts),ydiff(numpts) + real yestim(numpts),yresid(numpts) + real xmean,ymean,slope,yint,R2 + integer numpts,i + +c + call getmean(xdat,numpts,xmean) + call getmean(ydat,numpts,ymean) +c + call getdiff(xdat,numpts,xmean,xdiff) + call getdiff(ydat,numpts,ymean,ydiff) +c + call getslope(xdiff,ydiff,numpts,slope) + yint = ymean - slope * xmean +c + call getyestim(xdat,slope,yint,numpts,yestim) + call getresid(ydat,yestim,numpts,yresid) +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * CPS Thermal wind regression details * ' + print *,' *--------------------------------------------------* ' + endif + + call getcorr(yresid,ydiff,numpts,R2) + + if ( verb .ge. 3 ) then + print *,' i ydat xdat ydiff xdiff e' + & ,' e2 ydiff2' + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + do i = 1,numpts + write(6,'(2x,i3,2x,f7.2,2x,f7.4,2x,f7.2,2x,f7.4,3(2x,f7.2))') + & i,ydat(i),xdat(i),ydiff(i) + & ,xdiff(i),yresid(i),yresid(i)*yresid(i) + & ,ydiff(i)*ydiff(i) + enddo + + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + print *,' ' + write (6,'(1x,a13,f9.3,3x,a5,f7.2)') ' means: y: ',ymean + & ,' x: ',xmean + + write (6,*) ' ' + write (6,30) 'slope= ',slope,' y-intercept = ',yint + 30 format (2x,a7,f10.3,a23,f10.3) + if (slope .gt. 0.0) then + write(6,40) 'Regression equation: Y = ',yint,' + ',slope + else + write(6,40) 'Regression equation: Y = ',yint,' - ' + & ,abs(slope) + endif + 40 format (2x,a27,f8.2,a3,f8.2,'X') +c + print *,' ' + write (6,'(1x,a17,f7.4,5x,a7,f7.4)') ' R2(r_squared) = ',R2 + & ,' r = ',sqrt(R2) + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * End of regression details * ' + print *,' *--------------------------------------------------* ' + endif + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getmean(xarr,inum,zmean) +c +c This subroutine is part of the correlation calculation, +c and it simply returns the mean of the input array, xarr. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c +c OUTPUT: +c zmean mean of data values in xarr + + implicit none + + real xarr(inum) + real xsum,zmean + integer i,inum +c + xsum = 0.0 + do i = 1,inum + xsum = xsum + xarr(i) + enddo +c + zmean = xsum / float(MAX(inum,1)) +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getdiff(xarr,inum,zmean,zdiff) +c +c This subroutine is part of the correlation calculation, +c and it returns in the array zdiff the difference values +c between each member of the input array xarr and the +c mean value, zmean. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c zmean mean of input array (xarr) +c +c OUTPUT: +c zdiff array containing xarr(i) - zmean + + implicit none + + real xarr(inum),zdiff(inum) + real zmean + integer i,inum +c + do i = 1,inum + zdiff(i) = xarr(i) - zmean + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + + subroutine getslope(xarr,yarr,inum,slope) +c +c This subroutine is part of the correlation calculation, +c and it returns the slope of the regression line. +c +c INPUT: +c xarr input array of xdiffs (x - xmean) +c yarr input array of ydiffs (y - ymean) +c inum number of points in x & y arrays +c +c OUTPUT: +c slope slope of regression line + + real xarr(inum),yarr(inum) + real slope,sumxy,sumx2 + integer i,inum + +c First sum up the xarr*yarr products.... + + sumxy = 0.0 + do i = 1,inum + sumxy = sumxy + xarr(i) * yarr(i) + enddo + +c Now sum up the x-squared terms.... + + sumx2 = 0.0 + do i = 1,inum + sumx2 = sumx2 + xarr(i) * xarr(i) + enddo + +c Now get the slope.... + + slope = sumxy / sumx2 + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getyestim(xarr,slope,yint,inum,yestim) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the predicted y-values using the +c regression equation that has been calculated. +c +c INPUT: +c xarr array of x data points +c slope slope of the calculated regression line +c yint y-intercept of the calculated regression line +c inum number of input points +c +c OUTPUT: +c yestim array of y pts estimated from regression eqn. + + implicit none + + real xarr(inum),yestim(inum) + real slope,yint + integer i,inum +c + do i = 1,inum + yestim(i) = yint + xarr(i) * slope + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getresid(yarr,yestim,inum,yresid) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the residual values between the +c input y data points and the y-estim predicted y values. +c +c INPUT: +c yarr array of y data points +c yestim array of y pts estimated from regression eqn. +c inum number of input points +c +c OUTPUT: +c yresid array of residuals (ydat(i) - yestim(i)) + + implicit none + + real yarr(inum),yestim(inum),yresid(inum) + integer i,inum +c + do i = 1,inum + yresid(i) = yarr(i) - yestim(i) + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getcorr(yresid,ydiff,inum,R2) +c +c This subroutine is part of the correlation calculation, +c and it does the actual correlation calculation. +c +c INPUT: +c yresid array of residuals (ydat(i) - yestim(i)) +c ydiff array of points for ydat - ymean +c inum number of points in the arrays +c +c OUTPUT: +c R2 R-squared, the coefficient of determination + + USE verbose_output + + implicit none + + real yresid(inum),ydiff(inum) + real R2,sumyresid,sumydiff + integer i,inum +c + sumyresid = 0.0 + sumydiff = 0.0 + + do i = 1,inum + sumyresid = sumyresid + yresid(i) * yresid(i) + sumydiff = sumydiff + ydiff(i) * ydiff(i) + enddo + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,30) 'Sum of y-residuals squared (e2) = ',sumyresid + write (6,30) 'Sum of y-diffs squared (ydiff2) = ',sumydiff + write (6,*) ' ' + 30 format (1x,a35,f15.2) + endif + +c if (sumydiff == 0.0) then +c R2=1.0 +c else +c R2 = 1 - sumyresid / sumydiff +c endif +c PENG 05/14/2018 Bug-fixed for R2 calculation with FENS job crashed. + if (sumyresid .lt. sumydiff) then + if (sumydiff .le. 0.000001) then + R2 = 1.0 + else + R2 = 1 - sumyresid / sumydiff + endif + else + R2=0.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- +c subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +cPENG----2018-06-07 ------------------------ + subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag + & ,wcore_mean_val,wcore_point_max,igvpret) + +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. Here, we are only looking +c at the mid-to-upper tropospheric warm anomaly at the center of +c the storm. The temperature data that we are searching through in +c the tmean array should be the 300-500 mb mean temperature data. +c The criteria in this algorithm are based loosely on Vitart's +c criteria for warm core checking, but the nuts & bolts of the +c subroutine use algorithms from this tracker, including the barnes +c analysis. First, we locate the warm core with the find_maxmin +c routine. Then we use the check_closed_contour routine to see if +c there is a closed temperature contour surrounding the warm core. +c +c INPUT: +c inp +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c inp contains input date and model number information +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c ist integer storm number (internal to the tracker) +c ifh integer index for lead time +c trkrinfo derived type containing grid info on user boundaries +c fixlon array containing found fix longitudes +c fixlat array containing found fix latitudes +c valid_pt Logical; bitmap indicating if valid data at that pt. +c maxstorm maximum # of storms to be handled +c +c OUTPUT: +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c igvpret Return code for this subroutine. +c +c LOCAL: +c wcore_mean_val barnes-averaged value of the temperature at the +c location where the tracker found the warm core. +c wcore_point_max max temperature found at a gridpoint near the +c location where the tracker found the warm core using +c barnes analysis. + + USE set_max_parms; USE grid_bounds; USE trkrparms; USE contours + USE tracked_parms; USE gen_vitals; USE def_vitals; USE inparms + USE phase + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo,wcore_trkrinfo + type (cint_stuff) wcore_contour_info + type (datecard) inp + + character*1 get_last_contour_flag,wcore_flag + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dx,dy,wcore_mean_val,wcore_mean_lon,wcore_mean_lat + real wcore_point_max,tlastcont,rlastcont,tlastout,rlastout + integer imax,jmax,igvpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer icount,maxstorm,ip,ifmret,ifilret,ifix,jfix,icccret + integer num_check_conts + logical(1) valid_pt(imax,jmax),compflag,wcore_mask(imax,jmax) + logical(1) output_file_open +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of get_vtt_phase *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for warm core at hour ',i4,':',i2.2) + write (6,103) wcore_depth + 103 format (1x,'* Warm core depth threshold (wcore_depth) = ',f7.2) + print *,'*-------------------------------------------------*' + endif + +c ------------------------------------------------------------ +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + + wcore_mask = .false. + wcore_mean_lon = -999.0 + wcore_mean_lat = -999.0 + wcore_trkrinfo = trkrinfo ! set equal to values from trkrinfo... + wcore_trkrinfo%contint = wcore_depth ! ...except use the warm + ! core contour interval specified by + ! the user in the extrkr.sh script. + +c ------------------------------------------------------------ +c First, call find_maxmin to locate the warm core + + call find_maxmin (imax,jmax,dx,dy,'tmp' + & ,tmean,'max',ist,fixlon(ist,ifh),fixlat(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,compflag + & ,wcore_mean_lon,wcore_mean_lat,wcore_mean_val + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + + if (verb .ge. 3) then + print *,' ' + print *,'After call to find_maxmin for wcore, ifmret= ',ifmret + print *,' wcore_mean_val= ',wcore_mean_val + endif + +c ------------------------------------------------------------ +c Once find_maxmin returns a value and a location for the +c barnes-averaged value of a warm core, then make a call to +c fix_latlon_to_ij to (1) get the actual gridpoint value of the +c temperature (the value stored in wcore_mean_val is an +c area-averaged value coming from the barnes analysis), and +c (2) to get the (i,j) indeces for this gridpoint to be used in +c the call to check_closed_contour below. + + if (wcore_mean_lat > -99.0 .and. wcore_mean_lon > -990.0) then + call fix_latlon_to_ij (imax,jmax,dx,dy,tmean,'max' + & ,valid_pt,wcore_mean_lon,wcore_mean_lat + & ,wcore_mean_val,ifix,jfix,wcore_point_max,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Warm core stats: ' + write (6,105) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_mean_lon,360.-wcore_mean_lon + & ,wcore_mean_lat,wcore_mean_val + write (6,106) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,ifix,jfix,wcore_point_max + endif + + else + ! Search went out of regional grid bounds.... + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN get_vtt_phase. The call to ' + print *,'!!! fix_latlon_to_ij returned a non-zero return ' + print *,'!!! code, which means that the search for the fix' + print *,'!!! i and j went out of bounds for a regional ' + print *,'!!! grid. This should have been caught in a ' + print *,'!!! previous call to find_maxmin for one of the ' + print *,'!!! various fix parms. In any event, we will not' + print *,'!!! search for a warm core for this storm and ' + print *,'!!! lead time.' + print *,' ' + write (6,115) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,'U',-999.99,-9999.99 + endif + + igvpret = 95 + wcore_flag = 'u' + return + endif + endif + + 105 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' mean_lon: ',f7.2,'E' + & ,1x,'(',f7.2,'W)',2x,'mean_lat: ',f7.2,2x + & ,'wcore_mean_val(K): ',f12.3) + 106 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' ifix: ',i5,2x + & ,' jfix: ',i5,2x,'wcore_point_max(K): ',f12.3) + + +c ------------------------------------------------------------ +c The Vitart scheme specifies that the temperature must decrease +c by at least 1.0C in all directions from the warm core center +c within a distance of 8 deg. A rigorous check of this criterion +c is performed here by utilizing the check_closed_contour routine. +c If we have a closed contour in the temperature field +c surrounding the warm core (using a 1 deg K interval), that +c criterion is satisfied. For diagnostic purposes, we set the +c value of num_check_conts to 999 in order to keep searching for +c all contours surrounding the warm core, and this allows us to +c get an idea of the "depth" or magnitude of the warm core when +c the tlastcont and rlastcont values are returned. + + wcore_contour_info%numcont = maxconts + num_check_conts = 999 + + get_last_contour_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,tmean + & ,valid_pt,wcore_mask,wcore_flag,'max',wcore_trkrinfo + & ,num_check_conts,wcore_contour_info,get_last_contour_flag + & ,tlastcont,rlastcont,icccret) + + if (wcore_flag == 'y') then + tlastout = tlastcont + rlastout = rlastcont/0.539638 + else + tlastout = -999.0 + rlastout = -9999.0 + endif + + if ( verb .ge. 3 ) then + write (6,115) storm(ist)%tcv_storm_id,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_flag,tlastout,rlastout + + 115 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2 + & ,' wcore_flag= ',a1,2x,' Temp of last contour(K) = ' + & ,f7.2,2x,'Radius of last contour(km) = ',f8.2) + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_sfc_center (xmeanlon,xmeanlat,clon + & ,clat,ist,ifh,calcparm,xsfclon,xsfclat + & ,maxstorm,igscret) +c +c ABSTRACT: This subroutine computes a modified lat/lon fix position +c to use as the input center position for the subroutines that +c follow which calculate surface-wind related values. The reason +c for this is that since we are concerned with the positioning of +c low-level wind features (e.g., rmax), we want the center position +c to be based solely on low-level features. We'll use mslp and the +c min in the sfc wind speed. If a center fix was unable to be made +c at this forecast hour for mslp and low-level winds, then we will +c stick with just using the mean position we got using all the other +c parameters. +c +c INPUT: +c xmeanlon The mean center longitude computed from all the various +c parameter fixes found in array clon +c xmeanlat The mean center latitude computed from all the various +c parameter fixes found in array clat +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Index for storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c (if a parameter fix could not be made at this forecast +c hour, then calcparm is set to false for this time for +c that parameter). +c maxstorm Maximum number of storms that can be tracked +c +c OUTPUT: +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c igscret Return code from this subroutine + + USE set_max_parms + USE verbose_output + + implicit none + + integer ist,ifh,ipct,igscret,maxstorm + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmeanlon,xmeanlat + real xsfclon,xsfclat,xlonsum,xlatsum + logical(1) calcparm(maxtp,maxstorm) + + ipct = 0 + xlonsum = 0.0 + xlatsum = 0.0 + + ! Do NOT include MSLP for the surface center at this time. +c if (calcparm(9,ist)) then +c ipct = ipct + 1 +c xlonsum = xlonsum + clon(ist,ifh,9) +c xlatsum = xlatsum + clat(ist,ifh,9) +c endif + + if (calcparm(10,ist)) then +c ! NOTE: Put double weighting on surface wind center if +c ! the tracker was able to find a center for it.... +c ipct = ipct + 2 +c xlonsum = xlonsum + 2.*clon(ist,ifh,10) +c xlatsum = xlatsum + 2.*clat(ist,ifh,10) + ! Just use single weighting for the sfc wcirc fix + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,10) + xlatsum = xlatsum + clat(ist,ifh,10) + endif + + if (calcparm(11,ist)) then + ! This is for the sfc vorticity center.... + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,11) + xlatsum = xlatsum + clat(ist,ifh,11) + endif + + if (ipct > 0) then + xsfclon = xlonsum / float(ipct) + xsfclat = xlatsum / float(ipct) + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In get_fract_wind_cov, CANNOT get modified fix ' + print *,'!!! position because the parameter fixes for mslp' + print *,'!!! and the sfc winds could not be obtained at this' + print *,'!!! forecast hour. ist= ',ist,' ifh= ',ifh + print *,'!!! We will use the fixlon and fixlat values for' + print *,'!!! this forecast hour.' + endif + + xsfclon = xmeanlon + xsfclat = xmeanlat + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ In get_sfc_center, modified fix (mslp + sfc_winds)' + print *,'+++ position follows: ' + print *,'+++ ' + print *,'+++ mslp: lon: ',clon(ist,ifh,9),' lat: ' + & ,clat(ist,ifh,9) + print *,'+++ sfc_winds: lon: ',clon(ist,ifh,10),' lat: ' + & ,clat(ist,ifh,10) + print *,'+++ sfc_vorticity: lon: ',clon(ist,ifh,11),' lat: ' + & ,clat(ist,ifh,11) + print *,'+++ multi-parm mean: lon: ',xmeanlon,' lat: ' + & ,xmeanlat + print *,'+++ sfc-only mean: lon: ',xsfclon,' lat: ',xsfclat + endif + + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,er_wind,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm + & ,trkrinfo,igwsret) +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure of the low level winds of a cyclone. +c The algorithm will search out at specified distances from the +c storm center along arcs in each quadrant of the storm, +c evaluating the winds every 15 degrees along the arc. In each +c arc, start 7.5 degrees in, then make stops at 22.5, 37.5, +c 52.5, 67.5, and 82.5 degrees. At each of those points, we +c will bilinearly interpolate the winds to the points along those +c arcs. Then we compute a quadrant average of the wing magnitude, +c as well as the mean Vt and Vr values. This will be done +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 earth-relative +c quadrants: NE, SE, SW and NW. For the storm-relative estimates, +c these mean values of the wind will be computed for the same +c relative quadrants (front-right, back-right, back-left, front- +c left, but with respect (positive clockwise) to the +c direction of storm motion. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated +c er_wind: Quadrant winds in earth-relative framework +c sr_wind: Quadrant winds in storm-relative framework +c er_vr: Quadrant radial winds in earth-relative framework +c sr_vr: Quadrant radial winds in storm-relative framework +c er_vt: Quadrant tangential winds in earth-relative framework +c sr_vt: Quadrant tangential winds in storm-relative framework + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,num_qtr_azim=6 + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igvtret,ipct,maxstorm,iazim,azimuth_ct + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,xsfclon,xsfclat,wmag,wmag_sum + real vr,vt,vr_sum,vt_sum + logical(1) valid_pt(imax,jmax) +c + data rdist/10.,25.,50.,75.,100.,125.,150.,200.,250.,300.,350. + & ,400.,450.,500./ + + igwsret = 0 + + er_wind = 0.0 + sr_wind = 0.0 + er_vr = 0.0 + er_vt = 0.0 + sr_vr = 0.0 + sr_vt = 0.0 + +c ----------------------------------------------------------------- +c Now determine the angle that the storm took getting from the +c last position to the current one. If this is the initial time, +c use the observed direction of motion from the TC Vitals. This +c may not match up with the model storm's initial direction of +c motion, but it is all we have available to us in order to get +c a heading estimate for the initial time. This storm heading +c information will be used for the storm-relative profiles. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_wind_structure, fhr= ',fhreal(ifh) + & ,' ',storm(ist)%tcv_storm_id + & ,' ',storm(ist)%tcv_storm_name + print '(a25,a23,f9.3)',' In get_wind_structure, ' + & ,' model storm heading = ',st_heading + print *,' ' + endif + + endif + +c ----------------------------------------------------------------- +c Get the profiles for the earth-relative coordinate system. +c Start with NE, then SE, SW, and NW. First go through +c radiusloop, which goes from one radial distance to the next, +c then do the quadloop, which goes through each quadrant, and +c then within each quadrant, the qtr_azimloop goes through for +c six points along an arc, spaced 15 degrees apart, starting at +c 7.5 degrees clockwise from the north. +c ----------------------------------------------------------------- + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *****************************************************' + print *,' Wind Structure: distbear bilin interp starts here.' + print *,' *****************************************************' + print *,' ' + endif + + radiusloop1: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- ER structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop1: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + ! In each quadrant, run through six points along an + ! arc and evaluate the winds. + + qtr_azimloop1: do iazim = 1,num_qtr_azim + + bear = ((iquad-1) * 90.) + ((iazim-1) * 15.) + 7.5 + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' earth-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,' ' + print '(5(a10,f7.2))',' sfclat= ',xsfclat + & ,' sfclon= ',xsfclon + & ,' rdist= ',rdist(idist),' targlat= ',targlat + & ,' targlon= ',targlon + print '(19x,a8,f7.2,35x,a9,f7.2)','sfclon= ',360.-xsfclon + & ,'targlon= ',360.-targlon + endif + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop1 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + er_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + er_vr(iquad,idist) = vr_sum / float(azimuth_ct) + er_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + er_wind(iquad,idist) = -999.0 + er_vr(iquad,idist) = -999.0 + er_vt(iquad,idist) = -999.0 + endif + + enddo quadloop1 + + enddo radiusloop1 + +c ----------------------------------------------------------------- +c Get the profiles for the storm-relative coordinate system. +c Start with the front-right quadrant and go clockwise through +c back-right, back-left and front-left. +c ----------------------------------------------------------------- + + radiusloop2: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- SR structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop2: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + qtr_azimloop2: do iazim = 1,num_qtr_azim + +c temp_bear = st_heading + ((iquad-1) * 90.) + 45. + + temp_bear = st_heading + ((iquad-1) * 90.) + & + ((iazim-1) * 15.) + 7.5 + bear = mod(temp_bear,360.) + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' storm-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop2 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + sr_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + sr_vr(iquad,idist) = vr_sum / float(azimuth_ct) + sr_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + sr_wind(iquad,idist) = -999.0 + sr_vr(iquad,idist) = -999.0 + sr_vt(iquad,idist) = -999.0 + endif + + enddo quadloop2 + + enddo radiusloop2 +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,calcparm,wfract_cov,pdf_ct_bin,pdf_ct_tot,maxstorm + & ,trkrinfo,igfwret) +c +c ABSTRACT: This subroutine determines the fractional areal coverage +c of winds exceeding various thresholds within specified arcs +c (e.g., 200 km, 400 km, etc) in each quadrant of a storm. The bins +c that are used go as follows: (1) 0-100; (2) 0-200; (3) 0-300; +c (4) 0-400; (5) 0-500. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real wfract_cov(numquad+1,numbin,numthresh) + real area_total_quad_bin(numquad,numbin) + real area_exceed_quad_bin(numquad,numbin,numthresh) + real xintlon,xintlat + real :: windthresh(numthresh) = (/17.5,25.74,32.94/) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,conv_ms_knots,vmagkts + real rads,ri,dell,vmag,xarea,grdintincr,xsfclon,xsfclat + real sum_exceed_area(numbin,numthresh) + real sum_total_area(numbin,numthresh) + integer pdf_ct_bin(16) + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igfwret,ipct,i,j,numinterp,ixoa,ixaa,iq,ib,it,ii + integer jlatfix,ilonfix,npts,ibeg,iend,jbeg,jend,ngridint,ni,nj + integer itret,igiret,idistbin,ipdfbin,pdf_ct_tot,maxstorm + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) + character got_pdf*6 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*5 :: cbin(5) = + & (/'0-100','0-200','0-300','0-400','0-500'/) + character*2 :: cthresh(3) = (/'34','50','64'/) +c + igfwret = 0 + conv_ms_knots = 1.9427 + rads = 500.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + + wfract_cov = 0.0 + area_total_quad_bin = 0.0 + area_exceed_quad_bin = 0.0 + sum_exceed_area = 0.0 + sum_total_area = 0.0 + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_fract_wind_cov from call to ' + print *,'!!! get_ij_bounds, stopping processing for storm' + print *,'!!! number ',ist + endif + + igfwret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_fract_wind_cov calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igfwret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + +c When evaluating the winds at a gridpoint, keep in mind that each +c gridpoint represents area around it. There are 2 special cases +c we need to watch out for. The first is for cases in which the +c area of a gridpoint straddles across a distance threshold, so +c that some of the gridpoint's area is in the "<200" bin, while +c some is in the "<100" bin. The other is for the case in which +c the area of a gridpoint straddles between 2 adjacent quadrants +c (e.g., a gridpoint exactly to the north of the center would have +c half its area in the NW quadrant and half in the NE quadrant). +c +c To properly "partition" and assign gridpoint areas, we need to +c interpolate the current grid down to a fine resolution. +c +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the guidelines that +c will be used, keeping in mind that we want the final grid spacing +c to be on the order of between 0.05 and 0.10 degree (finer than +c 0.05 deg is superfluous, and coarser than 0.10 deg is too coarse). +c +c Original grid size (deg) # of interps +c ------------------------- ------------ +c 0.8 <= g 4 +c 0.4 <= g < 0.8 3 +c 0.2 <= g < 0.4 2 +c 0.1 <= g < 0.2 1 +c g < 0.1 0 + + + if ((dx+dy)/2. >= 0.8) then + numinterp = 4 + else if ((dx+dy)/2. < 0.8 .and. (dx+dy)/2. >= 0.4) then + numinterp = 3 + else if ((dx+dy)/2. < 0.4 .and. (dx+dy)/2. >= 0.2) then + numinterp = 2 + else if ((dx+dy)/2. < 0.2 .and. (dx+dy)/2. >= 0.1) then + numinterp = 1 + else + numinterp = 0 + endif + + grdintincr = (dx+dy)/2. + do i = 1,numinterp + grdintincr = 0.5 * grdintincr + enddo + +c Now loop through the points in this subdomain, determine if any +c are within 500 km of the center, and then determine what quadrant +c the point is in relative to the center, and then calculate the +c fractional area coverage for winds. + + pdf_ct_tot = 0 + pdf_ct_bin = 0 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_fract_wind_cov, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_fract_wind_cov' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle iloop ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > (rads+(0.75*((dx+dy)/2.)*dtk*cos(glat(j)*dtr)))) + & then + + ! If the distance is greater than "rads" (500 km at initial + ! writing) plus another 3/4 of a gridpoint, then cycle. + ! The extra 3/4 of a gridpoint is to allow for the case of + ! some portion of the area around a gridpoint (whose + ! center point > 500 km) being within the 500 km arc... + ! although that is only factored in for grids with spacing + ! >= 0.1 deg. For smaller grids, where no interpolation is + ! done in this subroutine, then the distance to that point + ! is considered representative and the point is ignored if + ! it is not less than 500 km from the center. + + cycle iloop + + else + + ! First interpolate the area surrounding each grid point to + ! get fine resolution of lats & lons for determining how to + ! partition the area of a gridpoint among quadrants as well + ! as among distance thresholds. + + vmag = sqrt (u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + vmagkts = vmag * conv_ms_knots + + if (numinterp > 0) then + + grdintincr = ((dx+dy)/2.) / 2**numinterp ! "grid spacing" + ! of interpolated grid + ngridint = (2**numinterp) / 2 + + got_pdf = 'notyet' + + njloop: do nj= ngridint,-ngridint,-1 + + xintlat = glat(j) + float(nj) * grdintincr + + niloop: do ni= -ngridint,ngridint + + xintlon = glon(ii) + float(ni) * grdintincr + + call calcdist (xintlon,xintlat,xsfclon + & ,xsfclat,xdist,degrees) + + if (xdist <= 350. .and. got_pdf == 'notyet') then + ! The got_pdf flag is needed because in these loops + ! for niloop & njloop, we are actually looking at + ! tiny areas around the same grid point. So we + ! want to make sure we only count each gridpoint + ! once. + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + got_pdf = 'got_it' + endif + + if (xdist < 500.) then + + ! Compute area of this fraction of a grid box + xarea = (grdintincr * 111195) * + & (grdintincr * 111195 + & * cos(xintlat * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Go through a loop of the bins. The purpose of + ! this is that these "bins" all go from the + ! the center out to a specified radius, they are + ! NOT 100-km wide bins. So if we are dealing with + ! a point at r = 250 km, then that falls in the + ! 0-300 km bin, but it also falls in the 0-400 and + ! 0-500 km bins as well. So we need to run through + ! this binloop multiple times to get the area data + ! into multiple bins. Here are the bins & indices: + ! 1: 0-100 km + ! 2: 0-200 km + ! 3: 0-300 km + ! 4: 0-400 km + ! 5: 0-500 km + + binloop: do ib = idistbin,numbin + + if (xintlon >= xsfclon .and. + & xintlat >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (xintlon >= xsfclon .and. + & xintlat < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop + + endif + + enddo niloop + + enddo njloop + + else + + ! In this else statement is the case for a grid whose + ! resolution is already fine enough that we don't need + ! to interpolate any further. For example, we will have + ! the H*Wind data on a 0.05 degree grid, so that's already + ! fine enough. + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat + & ,xdist,degrees) + + if (xdist <= 350.) then + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + endif + + if (xdist < 500.) then + + ! Compute area of this grid box + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Why the binloop2? See explanation above in the "if" + ! part of this if-then block, where binloop is. + + binloop2: do ib = idistbin,numbin + + if (glon(ii) >= xsfclon .and. + & glat(j) >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (glon(ii) >= xsfclon .and. + & glat(j) < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop2 + + endif + + endif + + endif + + enddo iloop + + enddo jloop + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different quadrants, bins and thresholds... +c ------------------------------------------------- + + if ( verb .ge. 3 ) then + write (6,109) ' ' + & ,' ' + & ,' ' + write (6,109) ' Quadrant Bin Wind_Thresh ' + & ,'Fract_coverage (%) Area_exceeded' + & ,' Area_total' + write (6,109) ' -------- --- ----------- ' + & ,'------------------ -------------' + & ,' ----------' + write (6,109) ' ' + & ,' ' + & ,' ' + + do iq = 1,numquad + do ib = 1,numbin + do it = 1,numthresh + wfract_cov(iq,ib,it) = area_exceed_quad_bin(iq,ib,it) / + & area_total_quad_bin(iq,ib) + write (6,117) cquad(iq),cbin(ib),cthresh(it) + & ,wfract_cov(iq,ib,it)*100.0 + & ,area_exceed_quad_bin(iq,ib,it) + & ,area_total_quad_bin(iq,ib) + enddo + enddo + enddo + endif + + + 109 format (1x,a33,a37,a16) + 117 format (5x,a2,5x,a5,7x,a2,13x,f6.2,10x,f16.1,2x,f16.1) + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different bins and thresholds, but for the +c entire "disc" of the storm, that is, summing all +c quadrants together. +c ------------------------------------------------- + + do it = 1,numthresh + do ib = 1,numbin + do iq = 1,numquad + sum_total_area(ib,it) = sum_total_area(ib,it) + & + area_total_quad_bin(iq,ib) + sum_exceed_area(ib,it) = sum_exceed_area(ib,it) + & + area_exceed_quad_bin(iq,ib,it) + enddo + wfract_cov(5,ib,it) = sum_exceed_area(ib,it) + & / sum_total_area(ib,it) + enddo + enddo + + if ( verb .ge. 3 ) then + do ib = 1,numbin + do it = 1,numthresh + write (6,117) 'TT',cbin(ib),cthresh(it) + & ,wfract_cov(5,ib,it)*100.0 + & ,sum_exceed_area(ib,it) + & ,sum_total_area(ib,it) + enddo + enddo + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_ike_stats (imax,jmax,inp,dx,dy,ist,ifh + & ,fixlon,fixlat,xsfclon,xsfclat,valid_pt,calcparm + & ,ike,sdp,wdp,maxstorm,trkrinfo,igisret) +c +c ABSTRACT: This subroutine computes the Integrated Kinetic Energy +c (IKE) and Storm Surge Damage Potential (SDP) values, based on +c Powell (BAMS, 2007). At this time, we are only computing the IKE +c values for TS threshold (17.5 m/s) and above. We are not yet +c computing wind damage potential (WDP) since, per Mark Powell +c (4/2008), he is currently re-formulating an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c sdp Storm surge damage potential + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4 + integer npts,ipct,igisret,imax,jmax,ist,ifh,ilonfix,jlatfix + integer ibeg,jbeg,iend,jend,igiret,i,j,maxstorm,ii + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real ike(max_ike_cats) + real dx,dy,degrees,rads,ri,dell,xdist,vmag,xarea + real xsfclon,xsfclat,sdp,wdp + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) +c + igisret = 0 + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + rads = 400.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_ike_stats from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for storm ' + print *,'!!! number ',ist + endif + + igisret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_ike_stats calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igisret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + +c Search a grid of points near the storm center, evaluate if the +c storm is within the "rads" distance threshold. If so, compute +c the IKE values for all applicable thresholds (10, 18, 33 m/s). + + do j = jbeg,jend + do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ike_stats, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_ike_stats' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > rads) then + cycle + else + + vmag = sqrt(u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + + if (vmag > 10.0) then + ! Add gridpoint to IKE_10. Compute area first... + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + ike(1) = ike(1) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 18.0) then + ! Add gridpoint to IKE_ts. Area already computed for 10 + ike(2) = ike(2) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 33.0) then + ! Add gridpoint to IKE_h. Area already computed for 10 + ike(3) = ike(3) + (0.5 * (vmag**2) * xarea) + endif + + endif + + enddo + enddo + + ike(1) = ike(1) * 1.e-12 ! Convert from J to TJ + ike(2) = ike(2) * 1.e-12 ! Convert from J to TJ + ike(3) = ike(3) * 1.e-12 ! Convert from J to TJ + +c Compute the storm surge damage potential (sdp) + + if (ike(2) >= 0.0) then + sdp = 0.676 + (0.43 * sqrt(ike(2))) + & - (0.0176 * ((sqrt(ike(2)) - 6.5)**2) ) + else + sdp = -99.0 + endif + +c Print out the IKE and SDP statistics... + + if ( verb .ge. 3 ) then + print *,' IKE_10 (storm energy) = ',ike(1) + print *,' IKE_TS (tropical storm) = ',ike(2) + print *,' IKE_H (hurricane) = ',ike(3) + print *,' SDP = ',sdp + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine distbear (xlatin,xlonin,dist,bear,xlatt,xlont) +c +c ABSTRACT: Given an origin at latitude, longitude=xlato,xlono, +c this subroutine will locate a target point at a distance dist in +c km or nautical miles (depends on what you use for "rad_earth..." +c below), at bearing bear (degrees clockwise from north). +c Returns latitude xlatt and longitude xlont of target point. +c +c *** NOTE *** +c This subroutine was written to handle input lats & lons as this: +c All latitudes are in degrees, north positive and south negative. +c All longitudes are in degrees, west positive and east negative. +c *** **** *** +c +c However, for the longitudes, the rest of the tracker uses all +c 0-360 longitudes. Therefore, we need to convert the input lons +c and then once again convert the lons that are returned back to +c the calling routine. +c +c NOTE-- When origin is at north or south pole, bearing is no +c longer measured from north. Instead, bearing is measured +c clockwise from the longitude opposite that specified in xlono. +c Example-- if xlato=90., xlono=80., the opposite longitude is +c -100 (100 East), and a target at bearing 30. will lie on the +c -70. (70 East) meridian. +c +c AUTHOR: The core of this subroutine was written by Albion +c Taylor, another NOAA employee, in 1981. +c + USE trig_vals + + implicit none +c + real, parameter :: rad_earth_nm = 3440.170 ! radius of earth + real, parameter :: rad_earth_km = 6372.797 ! radius of earth + real xlato,xlono,dist,bear,xlatt,xlont,xlatin,xlonin + real cdist,sdist,clato,slato,clono,slono,cbear,sbear + real z,y,x,r,xlattz,xlontz,ddist,dbear,dxlato,dxlono +c + xlato = xlatin + xlono = xlonin + +cstr print *,' ' +cstr print *,'+++ At top of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlon= ',xlono,'E ',360.-xlono +cstr & ,'W xlat=',xlato +cstr print '(a6,f7.2,a8,f7.2)','dist= ',dist,' bear= ',bear + + if (xlono > 180.) then + ! Longitude input for this subroutine must be positive west + xlono = 360. - xlono + else + ! Longitude input for this subroutine must be negative east + xlono = -1. * xlono + endif + +cstr print '(a31,a8,f8.2)','After conversion for distbear, ' +cstr & ,' xlono= ',xlono + + ddist = dist + dbear = bear + dxlato = xlato + dxlono = xlono + + cdist = cos(ddist/rad_earth_km) + sdist = sin(ddist/rad_earth_km) + clato = cos(dtr*dxlato) + slato = sin(dtr*dxlato) + +cstr print *,'cdist= ',cdist,' sdist= ',sdist,' clato= ',clato +cstr & ,' slato= ',slato + + clono = cos(dtr*dxlono) + slono = sin(dtr*dxlono) + +cstr print *,'dxlono= ',dxlono,' clono= ',clono +cstr & ,' slono= ',slono + + cbear = cos(dtr*dbear) + sbear = sin(dtr*dbear) + +cstr print *,'cbear= ',cbear,' sbear= ',sbear + + z=cdist*slato + clato*sdist*cbear + y=clato*clono*cdist + sdist*(slono*sbear - slato*clono*cbear) + x=clato*slono*cdist - sdist*(clono*sbear + slato*slono*cbear) + +cstr print *,'z= ',z,' y= ',y,' x= ',x + + r = sqrt(x**2 + y**2) + +cstr print *,'r = sqrt(x**2 + y**2) = ',r + + xlattz = atan2(z,r)/dtr + +cstr print *,'xlattz = datan2(z,r)/dtr = ',xlattz + + xlatt = xlattz + + if (r <= 0.) go to 20 + + xlontz = atan2(x,y)/dtr + +cstr print *,'xlontz = atan2(x,y)/dtr = ',xlontz + +c xlont = xlontz + + ! Return the target longitude back to the calling routine + ! as a 0-360 positive east longitude.... + + xlont = mod(360.-xlontz,360.) + +c xlont = mod(360.+xlontz,360.) + +cstr print *,' ' +cstr print *,'At end of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlont= ',xlont,'E ' +cstr ,360.-xlont,'W xlatt=',xlatt + + return + 20 xlont=0. +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_uneven (targlat,targlon,dx,dy + & ,imax,jmax,trkrinfo,level,cparm,xintrp_val,ibiret) +c +c ABSTRACT: This subroutine performs a bilinear interpolation to get +c a data value at a given lat/lon that may be anywhere within a box +c defined by the four surrouding grid points. In the diagram below, +c remember that for our grids we are using in the tracker, the +c latitude index starts at the north pole and increases southward. +c The point "X" indicates the target lat/lon location of the value +c for which we are bilinearly interpolating. The values to and ta +c below are ratios that determine how geographically close the +c target location is to the point of origin (pt.1 (i,j)) in terms +c of both longitude (to) and latitude (ta). +c +c +c pt.1 pt.2 +c (i,j) (i+1,j) +c +c +c +c X +c +c pt.4 pt.3 +c (i,j+1) (i+1,j+1) +c + + USE grid_bounds; USE tracked_parms; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + character cparm*1 + real targlat,targlon,xintrp_val,dx,dy + real to,ta,d1,d2,d3,d4,z,eastlon + integer ie,iw,jn,js,ibiret,imax,jmax,level,nlev + + ibiret = 0 + +c -------------------------------------------------------------- +c For the latitudes and longitudes surrounding our target +c lat/lon location, convert the lat/lon values into i- and +c j-indices. +c -------------------------------------------------------------- + +c Find the j-indices for the points just to the north and the +c south of targlat.... + + if (targlat >= 0.0) then + ! For a northern hemisphere storm, jn is the j-index for the + ! point just to the *NORTH* (poleward) of targlat. + jn = int((glatmax - targlat)/dy + 1.) + js = jn + 1 + else + ! For a southern hemisphere storm, js is the j-index for the + ! point just to the *SOUTH* (poleward) of targlat. + js = ceiling((glatmax - targlat)/dy + 1.) + jn = js - 1 + endif + + ! Check to make sure that points are not being requested beyond + ! the northern or southern boundaries of the grid. This is most + ! likely to happen for a smaller, regional grid. + + if (jn > jmax .or. js > jmax) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jmax exceeded in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + + if (jn < 1 .or. js < 1) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jn < 0 or js < 0 in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + +c Find the i-indices for the points just to the east and the +c west of targlon.... + + ie = int((targlon - glonmin)/dx + 2.) + iw = ie - 1 + + ! Check for GM wrapping. Check ie to see if it is between the + ! most eastward gridpoint and the GM (i.e., on a 1-deg global + ! grid (360x181), it would be if targlon was between 359.0 (i=360) + ! and the GM (i=1, not i=361)). Similarly then, if we adjust ie + ! to then be 1, then we have a problem with iw, + ! since iw = 1 - 1 = 0. + + if (ie > imax) then + if (trkrinfo%gridtype == 'global') then + ie = ie - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ie > imax in subroutine ' + print *,'!!! bilin_int_uneven for a non-global grid. ' + print *,'!!! Returning to calling routine after ' + print *,'!!! assigning missing wind value of -99.' + print *,'!!! ie= ',ie,' imax= ',imax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + + if (iw < 1) then + if (trkrinfo%gridtype == 'global') then + iw = iw + imax + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: iw < 1 in subroutine bilin_int_uneven' + print *,'!!! for a non-global grid. Returning to calling ' + print *,'!!! routine after assigning missing wind value ' + print *,'!!! of -99. iw= ',iw + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' +++ Interpolating winds for cparm= ',cparm +ctmwc print '(6x,4(a4,i3))','jn= ',jn,' js= ',js,' iw= ',iw,' ie= ',ie +ctmwc endif + +c ---------------------------------------------------------------- +c Calculate the longitude (to) and latitude (ta) location ratios. +c Check for GM wrapping, as we can run into a problem here if +c interpolating for points that are just west of the GM, since we +c would be interpolating using values of longitude just west of +c GM (say, glon(iw)=359.5) and the GM (glon(ie) = 0.0). This +c makes for an incorrect "to" ratio below, with 0-359.5 in the +c denominator. We have to account for this.... +c ---------------------------------------------------------------- + + if (glon(iw) > 300.0 .and. + & (glon(ie) < 10. .and. glon(ie) >= 0.)) then + eastlon = 360. - glon(ie) + else + eastlon = glon(ie) + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,'glat(js)= ',glat(js),' glat(jn)= ',glat(jn) +ctmwc endif + + to = (targlon - glon(iw)) / (eastlon - glon(iw)) + ta = (targlat - glat(jn)) / (glat(js) - glat(jn)) + +c -------------------------------------------------------------- +c Copy the data values at the 4 known points into simple scalar +c variables +c -------------------------------------------------------------- + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cparm == 'u') then + d1 = u(iw,jn,nlev) + d2 = u(ie,jn,nlev) + d3 = u(ie,js,nlev) + d4 = u(iw,js,nlev) + else if (cparm == 'v') then + d1 = v(iw,jn,nlev) + d2 = v(ie,jn,nlev) + d3 = v(ie,js,nlev) + d4 = v(iw,js,nlev) + else if (cparm == 'm') then + d1 = lsmask(iw,jn) + d2 = lsmask(ie,jn) + d3 = lsmask(ie,js) + d4 = lsmask(iw,js) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in bilin_int_uneven.' + print *,'!!! Input cparm not recognized.' + print *,'!!! cparm= ',cparm + print *,'!!! EXITING....' + endif + + stop 95 + endif + + z = 1.9427 + +cstr print '(2x,4(a4,f8.2))',' d1= ',d1*z,' d2= ',d2*z +cstr & ,' d3= ',d3*z,' d4= ',d4*z + +c ------------------------------------------------------------- +c Compute the interpolated value +c ------------------------------------------------------------- + + xintrp_val = (1.-to) * (1.-ta) * d1 + & + to * (1.-ta) * d2 + & + to * ta * d3 + & + (1.-to) * ta * d4 + +cstr print '(2x,2(a11,f8.2))',' xintrp= ',xintrp_val,' (in kts)= ' +cstr & ,xintrp_val*z +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine sort_storms_by_pressure (gridprs,ifh,maxstorm,sortindex + & ,issret) +c +c ABSTRACT: This subroutine sorts storms by mslp. It is called by +c subroutine tracker just before the loop for "stormloop" is done +c for all the storms at a particular forecast hour. It is only +c called for the "midlat" and "tcgen" cases. The end result of +c this sort is an array (prsindex) that contains the indeces of +c the storms, arranged from lowest pressure to highest (and note +c that the "undefined" storms have a pressure of 9999.99 mb and +c thus get sorted to the bottom of the array). The purpose of +c doing this is so that we track the most intense storms first. +c Why go to the trouble? Imagine a scenario in which we are +c tracking a complex system in which there are 2 low pressure +c centers. Let's say that one is becoming dominant and +c intensifying, while the other is weakening. Now, let's assume +c that the weakening one eventually gets absorbed into the +c stronger, more dominant low. Now we only have 1 low, but if in +c the tracker stormloop, we first process the data for the +c weakening low, we will attribute the track to that storm, and +c then when we get to the point in the loop where we are trying +c to get the track for the stronger storm, we will (erroneously) +c stop the tracking for that storm since the storm center has +c already been attributed to the weaker storm. But by using this +c subroutine, we will track the stronger storm first, and thus +c avoid this problem. +c +c NOTE: The pressures used in the sort are those obtained at the +c previous forecast hour. At forecast hour = 0, just use the +c values as they were input to this routine, since they were +c found in first_ges_center from strongest to weakest already. +c +c INPUT: +c gridprs real array of storm mslp values +c ifh integer index for the current forecast hour +c maxstorm max num of storms that can be handled in this run +c +c OUTPUT: +c sortindex contains a sorted array of indeces. The orders +c sort routine does NOT rearrange the data. Rather, it +c returns this array of sorted indeces which point to +c the correct order of data values in the data array. +c issret return code from this subroutine +c + USE set_max_parms + USE verbose_output + + real, allocatable :: iwork(:) + real gridprs(maxstorm,maxtime) + integer ifh,maxstorm + integer sortindex(maxstorm) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: prstemp(:) +c + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iva /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub sort_storms_by_pressure allocating' + print *,'!!! prstemp or iwork arrays: ' + print *,'!!! iva= ',iva,' iwa= ',iwa + endif + + STOP 94 + return + endif + + if (ifh > 1) then + +c print *,' ' +c print *,'--- Before sort, original prs values follow:' +c print *,' ' + + do ist = 1,maxstorm + prstemp(ist) = gridprs(ist,ifh-1) +c write (6,81) ist,prstemp(ist)/100.0 + enddo + + imode = 2 + sortindex = 0 + call qsort (prstemp,sortindex,maxstorm) + +ccccc call orders (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) +ccccc call orders_4byte (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Pressure-sorted storm list:' + print *,' ' + + do ist = 1,maxstorm + if (prstemp(sortindex(ist))/100.0 < 9999.0) then + write (6,82) ist,sortindex(ist) + & ,prstemp(sortindex(ist))/100.0 + endif + enddo + + 81 format (1x,'ist= ',i5,' Original (unsorted) prstemp= ',f7.2) + 82 format (1x,'ist= ',i5,' sortindex(ist)= ',i5 + & ,' prstemp= ',f7.2) + endif + + else + do ist = 1,maxstorm + sortindex(ist) = ist + enddo + endif + + deallocate (prstemp); deallocate (iwork) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getvrvt (centlon,centlat,xlon,xlat + & ,udat,vdat,vr,vt,igvtret) +c +c ABSTRACT: This subroutine takes as input a u-wind and v-wind value +c at an input (xlon,xlat) location and returns the tangential and +c radial wind components relative to the input center lat/lon +c position (centlon,centlat). The only trick to this whole +c subroutine is figuring out the angle from the center point to the +c data point, and we do this by creating a triangle with the leg +c from the center point to the data point being the hypotenuse. +c +c NOTE: All longitudes must be in positive degrees east (0-360) !!! +c +c INPUT: +c centlon Longitude of center point +c centlat Latitude of center point +c xlon Longitude of pt at which vr & vt will be computed +c xlat Latitude of pt at which vr & vt will be computed +c udat u-value of wind at the point (xlon,xlat) +c vdat v-value of wind at the point (xlon,xlat) +c +c OUTPUT: +c vr Radial wind component at (xlon,xlat) wrt (centlon,centlat) +c vt Tang wind component at (xlon,xlat) wrt (centlon,centlat) +c igvtret Return code from this subroutine +c + USE trig_vals + USE verbose_output + + implicit none + + real centlon,centlat,xlon,xlat,udat,vdat,vr,vt,degrees,tmpxlon + real angle,xlondiff,xlatdiff,opp_dist,hyp_dist,sin_value + real cos_value,adj_dist,tmpangle,sin_angle,cos_angle + real uvrcomp,vvrcomp,uvtcomp,vvtcomp + integer igvtret +c + call calcdist(centlon,centlat,xlon,xlat,hyp_dist,degrees) + +c xxxx + + tmpxlon = xlon + + if (centlon > 330.0) then + + if (xlon > 360.0) then + + tmpxlon = xlon ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (xlon < 30.0) then + + tmpxlon = xlon + 360. ! In this case, the fix center is just + ! to the west of the GM with a lon (centlon) + ! > 330, while the point being evaluated + ! (xlon) is just east of the GM, but with a + ! lon (centlon) < 30. Need to adjust here to + ! to get the xlon in the 330+ frame of + ! reference. + + endif + + elseif (centlon >= 0 .and. centlon < 30.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = 360. - xlon + + endif + + elseif (centlon < 0.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = -1 * (360. - xlon) + + endif + + endif + + xlatdiff = abs(centlat - xlat) + xlondiff = abs(centlon - tmpxlon) + + if (centlon > 355.0) then + write (6,91) centlon,tmpxlon,hyp_dist,degrees,xlondiff + 91 format (1x,'centlon= ',f8.3,' tmpxlon= ',f8.3,' hyp_dist= ' + & ,f10.2,' degrees= ',f10.2,' xlondiff= ',f12.2) + endif + + if (xlondiff == 0 .and. xlatdiff > 0) then + + if (centlat > xlat) angle = 180 ! pt directly south of ctr + if (centlat < xlat) angle = 0 ! pt directly north of ctr + + else if (xlondiff > 0 .and. xlatdiff == 0) then + + if (centlon > tmpxlon) angle = 270 ! pt directly west of ctr + if (centlon < tmpxlon) angle = 90 ! pt directly east of ctr + + else + + ! This next part figures out the angle from the center point + ! (centlon,centlat) to the data point (tmpxlon,xlat). It does + ! this by setting up a triangle and then using inverse trig + ! functions to get the angle. Since this is a kludgy way to + ! do it that doesn't account for the curvature of the earth, + ! we'll do it 2 ways, using asin and then acos, then take the + ! average of those 2 for the angle. hyp_dist, calculated just + ! above, is the distance from the center pt to the data pt. + + opp_dist = xlatdiff/360. * ecircum + sin_value = opp_dist / hyp_dist + if (sin_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, sin_value > 1, setting to 1.' + print *,'!!! opp_dist= ',opp_dist,' hyp_dist= ',hyp_dist + print *,'!!! sin_value = ',sin_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + sin_value = 0.99999 + endif + sin_angle = asin(sin_value) / dtr + + call calcdist(centlon,centlat,tmpxlon,centlat,adj_dist,degrees) + cos_value = adj_dist / hyp_dist + if (cos_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, cos_value > 1, setting to 1.' + print *,'!!! adj_dist= ',adj_dist,' hyp_dist= ',hyp_dist + print *,'!!! cos_value = ',cos_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + cos_value = 0.99999 + endif + cos_angle = acos(cos_value) / dtr + + tmpangle = 0.5 * (sin_angle + cos_angle) + + ! The previous lines of code just calculated an angle between + ! 0 and 90. This next if structure adjusts that angle to + ! instead be between 0 and 360. + + if (centlat <= xlat .and. centlon <= tmpxlon) then + angle = 90 - tmpangle + else if (centlat > xlat .and. centlon <= tmpxlon) then + angle = 90 + tmpangle + else if (centlat >= xlat .and. centlon >= tmpxlon) then + angle = 270 - tmpangle + else if (centlat < xlat .and. centlon >= tmpxlon) then + angle = 270 + tmpangle + endif + + endif + + uvrcomp = udat * sin(angle * dtr) + vvrcomp = vdat * cos(angle * dtr) + vr = uvrcomp + vvrcomp + + uvtcomp = (-udat) * cos(angle * dtr) + vvtcomp = vdat * sin(angle * dtr) + vt = uvtcomp + vvtcomp + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcfunix (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in the new ATCF UNIX format. +c Unlike the old atcf DOS format in which you waited until the +c whole tracking was over to write the output for all forecast +c hours, with this atcfunix format, each time we are calling this +c subroutine, it is to only write out 1 record, which will be the +c fix info for a particular storm at a given time. Also, even +c though we have some data (GFS, NAM) at 6-hour intervals, Jim +c Gross informed me that TPC does not need the positions at such +c frequency, and keeping the reporting at 12 hour intervals is fine. +c +c While this new atcfunix format contains much more information than +c the old 1-line atcf dos message, for our purposes we will use the +c slots for mslp and wind radii. An example set of output records +c will look like the following: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c plastbar pressure of the outermost closed isobar +c rlastbar radius (nm) of the outermost closed isobar +c rmax radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c cps_vals real array with the values for the 3 cyclone phase +c space parameters: (1) is for Parameter B (thermal +c asymmetry); (2) is for lower level (600-900 mb) thermal +c wind; (3) is for upper level (300-600 mb) thermal wind. +c wcore_flag character for value of 300-500 mb warm core: y, n, or +c 'u' for undetermined. +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE phase + USE verbose_output + + type (datecard) inp + type (trackstuff) trkrinfo + + real cps_vals(3) + real outlon,outlat,rmax,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,irmax,output_fhr,ic,iplastbar,irlastbar + integer vradius(3,4),icps_vals(3) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + character comma_fill1*48,comma_fill2*31,comma_filler*79 + + if ( verb .ge. 3 ) then + print *,'TTT top of atcfunix, ist= ',ist,' ifh= ',ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcfunix. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'in output_atcfunix, tcv_storm_id= ' + & ,storm(ist)%tcv_storm_id + print *,'in output_atcfunix, tcv_storm_id(3:3)= ' + & ,storm(ist)%tcv_storm_id(3:3) + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' +!zhang case ('A','a'); basinid = 'NA' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + if (trkrinfo%want_oci) then + if (plastbar > 0.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -99 + endif + if (rlastbar > 0.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -99 + endif + else + iplastbar = -99 + irlastbar = -99 + endif + + if ( verb .ge. 3 ) then + print *, 'output: rlastbar=',rlastbar,' irlastbar=',irlastbar + print *, 'output: plastbar=',plastbar,' iplastbar=',iplastbar + endif + +c Now convert all of the cyclone phase space parameter values from +c real to integer. + + do ic = 1,3 + if (cps_vals(ic) > -9999.0) then + if (cps_vals(ic) >= 0.0) then + icps_vals(ic) = int(cps_vals(ic)*10. + 0.5) + else + icps_vals(ic) = int(cps_vals(ic)*10. - 0.5) + endif + else + icps_vals(ic) = -9999 + endif + enddo + + if (wcore_flag == 'y'.or. wcore_flag == 'Y') then + wcore_flag = 'Y' + elseif (wcore_flag == 'n' .or. wcore_flag == 'N') then + wcore_flag = 'N' + elseif (wcore_flag == 'u' .or. wcore_flag == 'U') then + wcore_flag = 'U' + else + wcore_flag = 'U' + endif + + comma_fill1 = ', 0, 0, , 0, , 0, 0, ,' + comma_fill2 = ' , , , 0, 0, 0, 0' + comma_filler = comma_fill1//comma_fill2 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + else + + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,'rmax= ',rmax,' irmax= ',irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,a79,', THERMO PARAMS' + & ,3(', ',i7),', ',a1,', ',i2,', DT, -999') + 91 format (a2,', ',a4,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,2(', ',i3),', ',a3) + +c bug fix for IBM: flush the output stream so it actually writes + flush(64) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each +c storm. This message contains the model identifier, the forecast +c initial date, and the positions for 0, 12, 24, 36, 48, 60 and 72 +c hours. In the case of the regional models (NGM, Eta), which +c only go out to 48h, zeroes are included for forecast hours +c 60 and 72. +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by Steve Lord for plotting purposes, and his +c plotting routines need the longitudes in 0 - 360, increasing +c westward. Thus, a necessary adjustment is made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + print *,'top of output_all' + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + intlon(ifh) = 0 + intlat(ifh) = 0 + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + endif + + enddo ifhloop + + print *,'before select case, atcfname= ' + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(5),intlon(5),intlat(9),intlon(9),intlat(13) + & ,intlon(13),intlat(17),intlon(17),intlat(21),intlon(21) + & ,0,0,storm(ist)%tcv_storm_id + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(3),intlon(3),intlat(5),intlon(5),intlat(7) + & ,intlon(7),intlat(9),intlon(9),intlat(11),intlon(11) + & ,intlat(13),intlon(13),storm(ist)%tcv_storm_id + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3),intlat(4) + & ,intlon(4),intlat(5),intlon(5),intlat(6),intlon(6) + & ,intlat(7),intlon(7),storm(ist)%tcv_storm_id + + case ('GDA','HDA') ! GDAS, HDAS + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),0,0,0,0,0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case default +c print *,'!!! ERROR in subroutine output_all. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + print *,'!!! Model name is not identified: ',atcfname + + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + 81 format (i2,a4,4i2.2,14i4,1x,a3) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm + & ,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each storm +c in ATCF format. This message contains the model identifier, the +c forecast initial date, and the positions for 12, 24, 36, 48 +c and 72 hours. This message also contains the intensity +c estimates (in knots) for those same hours. The conversion for +c m/s to knots is to multiply m/s by 1.9427 (3.281 ft/m, +c 1 naut mile/6080 ft, 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by the atcf system at TPC for plotting +c purposes, and the atcf plotting routines need the longitudes in +c 0 - 360, increasing westward. Thus, a necessary adjustment is +c made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real xmaxwind(maxstorm,maxtime) + real conv_ms_knots + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4,basinid*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + conv_ms_knots = 1.9427 + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + + intlon(ifh) = 0 + intlat(ifh) = 0 + + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + + endif + + enddo ifhloop + + basinid = ' ' + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid(1:2) = 'AL' + case ('E','e'); basinid(1:2) = 'EP' + case ('C','c'); basinid(1:2) = 'CP' + case ('W','w'); basinid(1:2) = 'WP' + case ('O','o'); basinid(1:2) = 'SC' + case ('T','t'); basinid(1:2) = 'EC' + case ('U','u'); basinid(1:2) = 'AU' + case ('P','p'); basinid(1:2) = 'SP' + case ('S','s'); basinid(1:2) = 'SI' + case ('B','b'); basinid(1:2) = 'BB' +cPENG case ('A','a'); basinid(1:2) = 'NA' + case ('A','a'); basinid(1:2) = 'AA' + case default; basinid(1:2) = '**' + end select + basinid(3:4) = storm(ist)%tcv_storm_id(1:2) + + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(5),intlon(5) + & ,intlat(9),intlon(9),intlat(13),intlon(13),intlat(17) + & ,intlon(17),0,0 + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,17)*conv_ms_knots) + 0.5) + & ,0 + & ,basinid,inp%byy + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble, ECMWF hi-res + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4),intlat(5) + & ,intlon(5),intlat(7),intlon(7) + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('GDA','HDA') ! GDAS, HDAS + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4) + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,0,0,0,0 + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case default +c print *,'!!! ERROR in subroutine output_atcf. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + end select + + enddo stormloop + + 82 format (i2,a4,4i2.2,10i4,5i3,1x,a4,i2.2) +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_hfip (outlon,outlat,inp,ist + & ,ifh,vmaxwind,xminmslp,vradius,rmax,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified ATCF UNIX format. +c The modification is to allow for sub-hourly output. That is, +c instead of just integer output hours, we can have output at +c 10, 15 or 20 past an hour. This necessitates a change in the +c "forecast hour" placeholder in the ATCF format. Instead of it +c being an I3, we'll make it an I5, with something like a lead time +c of 36.25h being rounded and truncated to 03625 for output. +c +c An example set of output records using the standard atcf format +c looks like the following: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c An example set of modified output records will look like the +c following, for the case of a lead time of 36:15 (36.25): +c +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifh index for the lead time array +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c rmax Radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms + USE verbose_output + + type (datecard) inp + + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,rmax + integer intlon,intlat,output_fhr,irmax,ileadtime + integer vradius(3,4) + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_hfip. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + ! ST: ifcsthour does not exist, so output_fhr is always + ! filled with invalid data here. However, output_fhr is + ! never used, so it is safe to remove. + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + ! output_fhr = ifcsthour + 3 + ileadtime = nint((fhreal(ifh) + 3.0) * 100.0) + else + ! output_fhr = ifcsthour + ileadtime = nint(fhreal(ifh) * 100.0) + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4),irmax + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4),irmax + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4),irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i5.5,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', 0, 0, ',i3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(69) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_fract_wind (outlon,outlat,xsfclon,xsfclat + & ,inp,ist,ifcsthour,vmaxwind,xminmslp,wfract_cov + & ,wfract_type,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values for the fractional areal coverage of various wind +c thresholds. In addition, this subroutine also writes out +c records to a file containing data on the PDF of wind magnitudes +c within r=350 km. +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with areal coverage thresholds. +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, NEE, 981, 857, 629, 810 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, NEE, 874, 732, 319, 610 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, NEE, 454, 327, 99, 270 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, AAE, 721, 721, 721, 721 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, AAE, 465, 465, 465, 465 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, AAE, 298, 298, 298, 298 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the pctgs for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind pctg info; all the +c other info is identical for each entry. +c +c Listed after the "XX" in each record is the radius from which +c the coverage is valid (000 km in this case); Next is the radius +c at which the coverage stops (100 km in this case). Next is the +c wind threshold (34, 50, 64). Next is an identifier for which +c quadrant the coverage starts in (first 2 characters are NE, SE, +c SW, NW); the last character indicates if the coverages are +c computed in the quadrants as earth-relative ("E") or +c storm-motion relative ("R"). The ones listed there as "AAE" +c are for the full disc (i.e., 4-quadrant average), earth-relative. +c Next are the wind coverage percentages, listed as percentage * 10 +c (e.g., 981 = 98.1%). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c wfract_cov percent areal coverage for various wind thresholds +c wfract_type 'earth' or 'storm' relative analysis +c pdf_ct_bin array for pdf of wind magnitudes within r=350 km +c pdf_ct_tot total count of pdf points for r < 350 km +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,pdfval + real wfract_cov(numquad+1,numbin,numthresh) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer :: windthresh(numthresh) = (/34,50,64/) + integer pdf_ct_bin(16) + integer intlon,intlat,output_fhr,intlon100,intlat100,pdf_ct_tot + integer maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (wfract_type == 'earth') then + wt = 'E' + else if (wfract_type == 'storm') then + wt = 'R' + else + wt = 'X' + endif + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'NE',wt + & ,int((1000.*wfract_cov(1,ib,it))+0.5) + & ,int((1000.*wfract_cov(2,ib,it))+0.5) + & ,int((1000.*wfract_cov(3,ib,it))+0.5) + & ,int((1000.*wfract_cov(4,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'AA',wt + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a6,i3.3,', ',i3.3,', ' + & ,i3,', ',a2,a1,4(', ',i4),', ',i4,a1,', ',i5,a1) + +c -------------------------------------------------- +c Now compute and write out the pdf values for the +c wind magnitude.... +c -------------------------------------------------- + + do ip = 1,16 + pdfval = float(pdf_ct_bin(ip)) / float(pdf_ct_tot) + write (76,85) atcfymdh,basinid,storm(ist)%tcv_storm_id(1:2) + & ,output_fhr,10*(ip-1),10*ip,pdf_ct_bin(ip) + & ,pdf_ct_tot,pdfval + enddo + + 85 format (1x,i10.10,3x,a2,a2,3x,i3,3x,i3.3,'_',i3.3,3x,i7,2x,i7 + & ,2x,f6.3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(73) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_wind_structure (outlon,outlat,xsfclon + & ,xsfclat,inp,ist,ifcsthour,vmaxwind,xminmslp,er_wind + & ,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values of the winds at specified distances along 45-degree +c radials in each storm quadrant. These are output +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 radials: NE, SE, +c SW and NW (45,135,225,315). For the storm-relative estimates, +c these radials will be computed at the same relative angles (i.e., +c 45,135,225,315), but with respect (positive clockwise) to the +c direction of storm motion. For example, for a storm moving with +c a heading of 280, the wind structure is evaluated at these +c radials: 325 (front-right; 45 deg CW from heading), 55 (back- +c right; 135 deg CW from heading), 145 (back-left; 225 deg CW from +c heading), 235 (front-left; 315 deg CW from heading). +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with wind values at the 13 specified distances +c (10, 25, 50, 75, 100, 150, 200, 250, 300, 350, 400, 450, 500 km) +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NEE, 1137, 1221, 854, 655, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SEE, 947, 982, 474, 396, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SWE, 645, 683, 328, 277, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NWE, 725, 753, 619, 429, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FRR, 1134, 1224, 852, 654, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BRR, 944, 984, 472, 393, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BLR, 649, 686, 321, 272, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FLR, 729, 756, 613, 421, etc., ... out to 500 km +c +c NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text. +c NOTE: These winds are in m/s coming into this routine and will +c be converted to knots*10 for output (e.g., 1221 = 122.1 kts) +c +c The "71" ID indicates earth-relative winds, the "72" ID indicates +c storm-relative winds. Here are the other IDs that will be used: +c 81: Tangential winds, earth-relative (m/s) +c 82: Tangential winds, storm-relative (m/s) +c 91: Radial winds, earth-relative (m/s) +c 92: Radial winds, storm-relative (m/s) +c +c Note that in this example, for this 36h forecast hour, there are +c 8 entries. This is so that we can include the wind values for +c the 4 different quadrants, for both the earth relative analyses +c (NEE, SEE, SWE, NWE) and the storm-relative analyses (FRR, BRR, +c BLR, FLR). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + integer ioutwind(numdist) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real outlon,outlat + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,id,intlon100,intlat100,ir + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*2 :: crel(4) = (/'FR','BR','BL','FL'/) + + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + +c Total wind (converted to knots*10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 71, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Total wind (converted to knots*10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 72, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 81, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 82, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 92, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a10,a2,a1,14(', ',i4) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(72) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_ike (outlon,outlat,xsfclon,xsfclat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,ike,sdp,wdp,maxstorm + & ,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the Integrated Kinetic Energy (IKE) and Storm Surge Damage +c Potential (SDP), based on Powell (BAMS, 2007). At this time, we +c are only computing the IKE values for TS threshold (17.5 m/s) and +c above. We are not yet computing wind damage potential (WDP) +c since, per Mark Powell (4/2008), he is currently re-formulating +c an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with WDP, SDP and IKE values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, 340, 560, 212, 174, 42, 93, 12, 0 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, WDP, SDP, I10, ITS, IH ,I2540,I4154, I55 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Values for WDP and SDP are multiplied by 10 in this routine +c before being written out. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c ike integrated kinetic energy, in units of TJ +c sdp storm surge damage potential +c wdp wind damage potential +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,sdp,wdp + real ike(max_ike_cats) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,intlon100,intlat100,maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (74,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, IKE',int((wdp*10)+0.5),int((sdp*10)+0.5) + & ,int(ike(1)+0.5),int(ike(2)+0.5),int(ike(3)+0.5) + & ,int(ike(4)+0.5),int(ike(5)+0.5),int(ike(6)+0.5) + & ,intlat100,clatns,intlon100,clonew +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a14,8(',',i5) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(74) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_phase (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,paramb,vtl_slope + & ,vtu_slope,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the three parameters that comprise Bob Hart's cyclone phase +c space (CPS). These parameters are his "parameter B", which +c assesses the left-right thermal asymmetry, and the upper +c troposphere (300-600 mb) and lower troposphere (900-600 mb) +c thermal wind values. +c +c LOCAL: +c +c Arrays: +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with paramb, vtl_slope and vtu_slope values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, 340, 560, 212 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, B, VTL, VTU +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c paramb thermal asymmetry +c vtl_slope thermal wind value for lower troposphere (900-600 mb) +c vtu_slope thermal wind value for upper troposphere (600-300 mb) +c +c OUTPUT: +c ioiret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + real outlon,outlat,paramb,vtl_slope,vtu_slope + real vmaxwind,conv_ms_knots,xminmslp + integer intlon,intlat,output_fhr + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (71,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 95, CPS',int(paramb+0.5),int(vtl_slope+0.5) + & ,int(vtu_slope+0.5) +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a14,3(',',i6)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(71) + + return + end +c +c----------------------------------------------------------------------- + subroutine output_atcf_gen1 (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different. Here's an example of the +c TPC standard atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c be the one that is pulled from the tcvitals or gen_vitals +c record: +c +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 50, NEQ, 155, 000, 000, 000 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 64, NEQ, 000, 000, 000, 000 +c +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd storm translation speed +c istmdir direction of storm movement +c plastbar pressure of last closed isobar +c rlastbar radius of last closed isobar +c rmax radius of max winds +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals; USE level_parms + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real outlon,outlat,plastbar,rlastbar,rmax + real vmaxwind,conv_ms_knots,xminmslp,mslp_outp_adj + real cps_vals(3) + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar,irmax + integer ivtl,ivtu,iparamb,output_fhr + integer vradius(3,4) + integer imeanzeta(nlevgrzeta),igridzeta(nlevgrzeta) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + + if ( verb .ge. 3) then + print *,'+++ Top of output_atcf_gen, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (rmax > -90.0) then + irmax = int(rmax + 0.5) + else + irmax = -99 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(66) + + return + end +c +c----------------------------------------------------------------------- +cPENG---2018-06-07------------------------------------------------- +c----------------------------------------------------------------------- +c subroutine output_atcf_gen (outlon,outlat,inp,ist +c & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm +c & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax +c & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + subroutine output_atcf_gen (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different. Here's an example of the +c TPC standard atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c be the one that is pulled from the tcvitals or gen_vitals +c record: +c +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 50, NEQ, 155, 000, 000, 000 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 64, NEQ, 000, 000, 000, 000 +c +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd storm translation speed +c istmdir direction of storm movement +c plastbar pressure of last closed isobar +c rlastbar radius of last closed isobar +c rmax radius of max winds +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real outlon,outlat,plastbar,rlastbar,rmax + real vmaxwind,conv_ms_knots,xminmslp + real cps_vals(3) + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar,irmax + integer ivtl,ivtu,iparamb,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + +cJ.Peng----2014-10-01----------------------------- + real wcore_mean_val,wcore_point_max + real imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + + if ( verb .ge. 3) then + print *,'+++ Top of output_atcf_gen, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + + if (xminmslp == 999999.0) xminmslp = 0.0 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/100. + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (rmax > -90.0) then + irmax = int(rmax + 0.5) + else + irmax = -99 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- +c & ,imeanzeta(1),abs(igridzeta(1)),imeanzeta(2),abs(igridzeta(2)) + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- +c & ,imeanzeta(1),abs(igridzeta(1)),imeanzeta(2),abs(igridzeta(2)) + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + endif + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4)) +cJ.Peng----2010-10-01----------------------------------------------- + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4) + & ,8(', ',f8.1)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(66) + + return + end +c +c----------------------------------------------------------------------- + subroutine output_atcf_sink (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta,igridzeta + & ,cps_vals,plastbar,rlastbar,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The "sink" in the subroutine name indicates that this output +c contains the whole kitchen sink of forecast storm info. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different, and the part after the radii +c data is different. Here's an example of the TPC standard +c atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c indicate the lat/lon at which the storm was *first* found in +c the model. The position may be either found within this run +c of the tracker, or that position may have been pulled from the +c tcvitals or gen_vitals record: +c +c 2000092500_F000_206N_0623W_13L, 2000092500, 03, GFSO, 036 +c , 243N, 675W, 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c , PLAS, RLAS, RMX, DIR, SPD, B, VTU, VTL +c , Z8MN, Z8MX, Z7MN, Z7MX +c +c As noted above, there is extra info at the end, after the +c "34, NEQ, 242, 163, 124, 208" radii info. Here is a key +c to indicate what these items are: +c +c PLAS: Pressure (mb) of last closed isobar +c RLAS: Radius of the last closed isobar in nm, 0 - 9999 nm. +c RMX: Radius of max winds, 0 - 999 nm. +c DIR: Direction of storm motion. +c SPD: Speed of storm motion (m/s * 10). +c B: Hart's CPS "Parameter B" thickness asymmetry value (m). +c VTL: Hart's CPS thermal wind (Lower, 900-600) value. +c VTU: Hart's CPS thermal wind (Upper, 600-300) value. +c Z8MN: Mean value of 850 mb zeta surrounding storm. +c Z8MX: Max value of 850 mb zeta near storm. +c Z7MN: Mean value of 700 mb zeta surrounding storm. +c Z7MX: Max value of 700 mb zeta near storm. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd speed of storm translation +c istmdir direction of storm motion +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c plastbar pressure of last closed isobar (pa) +c rlastbar radius of last closed isobar (nm) +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real cps_vals(3) + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar + integer iparamb,ivtl,ivtu,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1 + + if ( verb .ge. 3 ) then + print *,'+++ Top of output_atcf_sink, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4)) + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',2(i4,', '),4(i3,', '),2(i5,', '),4(i4,', '),a9) + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',2(i4,', '),i3,', ',2(i4,', '),3(i6,', '),4(i6,', ') + & ,a9) + +c write (68,87) gstm%gv_gen_date,gstm%gv_gen_lat +c & ,gstm%gv_gen_latns,gstm%gv_gen_lon +c & ,gstm%gv_gen_lonew,gstm%gv_gen_type +c & ,inp%bcc,inp%byy,inp%bmm,inp%bdd,inp%bhh +c & ,adjustr(atcfname),ifcsthour,intlat,clatns,intlon,clonew +c & ,int((vmaxwind*conv_ms_knots) + 0.5) +c & ,int(xminmslp/100.0 + 0.5) +c & ,'XX, 34, NEQ' +c & ,istmspd,istmdir,imeanzeta(1),igridzeta(1) +c & ,imeanzeta(2),igridzeta(2) +c +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,6(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(68) + + return + end + +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_tcvitals (xlon,xlat,inp,ist,iovret) +c +c ABSTRACT: This subroutine outputs a tcvitals record. The +c lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE inparms; USE set_max_parms + USE verbose_output + + type (tcvcard) stm + type (datecard) inp + real xlon,xlat +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "storm" +c components for this storm, then we will change the specific +c components that we need to. + + stm = storm(ist) + + stm%tcv_center = 'AEAR' + + stm%tcv_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + stm%tcv_latns = 'S' + else + stm%tcv_latns = 'N' + endif + + if (xlon >= 180.) then + stm%tcv_lon = 3600 - int(xlon * 10. + 0.5) + stm%tcv_lonew = 'W' + else + stm%tcv_lon = int(xlon * 10. + 0.5) + stm%tcv_lonew = 'E' + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) stm + endif + + write (65,21) stm + + 21 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + +c +c bug fix for IBM: flush the output stream so it actually writes + flush(65) + + return + end + +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_gen_vitals (xlon,xlat,inp,ist,istmspd,istmdir + & ,iovret) +c +c ABSTRACT: This subroutine outputs a modified vitals record. +c The lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c The storm identifier is different than that for a standard +c tcvitals. The storm identifier contains the date/time that +c the storm was first identified, and the lat/lon position at +c which it was first identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE gen_vitals; USE inparms; USE set_max_parms + USE verbose_output + + implicit none + + type (gencard) gstm + type (datecard) inp + real xlon,xlat + integer ist,iovret,istmspd,istmdir +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the vitals record. + + if (gstm%gv_gen_date /= 99999) then + + if (gstm%gv_gen_type /= 'FOF') then + ! If this is not a 'FOF' storm (found on the fly storm), then + ! it must be a TC vitals storm, or a tropical cyclone, and we + ! don't want to create a vitals record for a tropical cyclone, + ! since we will rely on reading them from the TC Vitals + ! database instead. + return + endif + + else + + ! This storm is new in this forecast/analysis and was found on + ! the fly in the first time level for this run and there was no + ! previous vitals record for this system + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = 0 + + gstm%gv_gen_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_gen_latns = 'S' + else + gstm%gv_gen_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_gen_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'W' + else + gstm%gv_gen_lon = int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'E' + endif + + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + gstm%gv_obs_ymd = inp%bcc * 1000000 + & + inp%byy * 10000 + & + inp%bmm * 100 + & + inp%bdd + + gstm%gv_obs_hhmm = inp%bhh * 100 + + gstm%gv_obs_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_obs_latns = 'S' + else + gstm%gv_obs_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_obs_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'W' + else + gstm%gv_obs_lon = int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'E' + endif + + gstm%gv_stdir = istmdir + gstm%gv_stspd = istmspd + + gstm%gv_depth = 'U' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) gstm + endif + + write (67,21) gstm + + 21 format (i10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1,'_',a3,1x,i8,1x + & ,i4.4,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x + & ,i3,4(1x,i4),1x,a1) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(67) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_tracker_mask (masked_outc,lb,ifh,ifcsthour + & ,imax,jmax,iotmret) +c +c ABSTRACT: This subroutine outputs a GRIB record that contains the +c "mask" used to mask out areas surrounding low pressure centers +c that have been found during the search at each forecast hour. This +c mask is written out purely for diagnostic purposes. The GRIB +c identifier given to the mask in the pds is 850 mb height (you can +c make it anything you want). This is only done for the "midlat" +c and "tcgen" cases, since the runs for those cases use a mask while +c the regular "tracker" run (that is, the run which strictly tracks +c only those storms in the TC vitals file) does not. +c +c INPUT: +c masked_outc logical array containing mask +c ifh integer counter for current forecast hour +c ifcsthour integer current forecast hour +c imax num points is i-direction of input grid +c jmax num points is j-direction of input grid +c +c OUTPUT: +c iotmret return code from this subroutine + + implicit none +c + integer ifh,imax,jmax,iotmret,kf,igoret,iix,jjx,ipret + integer ifcsthour + integer kpds(200),kgds(200) + logical(1) masked_outc(imax,jmax),lb(imax,jmax) + real xmask(imax,jmax) +c + if (ifh == 1) then + call baopenw (77,"fort.77",igoret) + print *,'baopenw: igoret= ',igoret + + if (igoret /= 0) then + print *,' ' + print *,'!!! ERROR in sub output_tracker_mask opening' + print *,'!!! **OUTPUT** grib files. baopenw return codes:' + print *,'!!! grib file 1 return code = igoret = ',igoret + STOP 95 + return + endif + endif + + xmask = 0.0 + do jjx = 1,jmax + do iix = 1,imax + if (masked_outc(iix,jjx)) then + xmask(iix,jjx) = 1.0 + else + xmask(iix,jjx) = 0.0 + endif + enddo + enddo + + kf = imax * jmax + +c kpds(5) = 7 +c kpds(6) = 100 +c kpds(7) = 850 +c kpds(22) = 0 + + kpds(1) = 7 ; kpds(2) = 80 + kpds(3) = 255 ; kpds(4) = 192 + kpds(5) = 7 ; kpds(6) = 100 + kpds(7) = 850 ; kpds(8) = 99 + kpds(9) = 7 ; kpds(10) = 20 + kpds(11) = 12 ; kpds(12) = 0 + kpds(13) = 1 ; kpds(14) = ifcsthour + kpds(15) = 0 ; kpds(16) = 10 + kpds(17) = 0 ; kpds(18) = 1 + kpds(19) = 2 ; kpds(20) = 0 + kpds(21) = 20 ; kpds(22) = 0 + kpds(23) = 0 ; kpds(24) = 0 + kpds(25) = 0 + kgds(1) = 0 ; kgds(2) = imax + kgds(3) = jmax ; kgds(4) = -90000 + kgds(5) = 0 ; kgds(6) = 128 + kgds(7) = 90000 ; kgds(8) = 359750 + kgds(9) = 250 ; kgds(10) = 250 + kgds(11) = 64 ; kgds(12) = 0 + kgds(13) = 0 ; kgds(14) = 0 + kgds(15) = 0 ; kgds(16) = 0 + kgds(17) = 0 ; kgds(18) = 0 + kgds(19) = 0 ; kgds(20) = 255 + + write(*,980) kpds(1),kpds(2) + write(*,981) kpds(3),kpds(4) + write(*,982) kpds(5),kpds(6) + write(*,983) kpds(7),kpds(8) + write(*,984) kpds(9),kpds(10) + write(*,985) kpds(11),kpds(12) + write(*,986) kpds(13),kpds(14) + write(*,987) kpds(15),kpds(16) + write(*,988) kpds(17),kpds(18) + write(*,989) kpds(19),kpds(20) + write(*,990) kpds(21),kpds(22) + write(*,991) kpds(23),kpds(24) + write(*,992) kpds(25) + write(*,880) kgds(1),kgds(2) + write(*,881) kgds(3),kgds(4) + write(*,882) kgds(5),kgds(6) + write(*,883) kgds(7),kgds(8) + write(*,884) kgds(9),kgds(10) + write(*,885) kgds(11),kgds(12) + write(*,886) kgds(13),kgds(14) + write(*,887) kgds(15),kgds(16) + write(*,888) kgds(17),kgds(18) + write(*,889) kgds(19),kgds(20) + write(*,890) kgds(21),kgds(22) +c + 980 format('tmow kpds(1) = ',i7,' kpds(2) = ',i7) + 981 format('tmow kpds(3) = ',i7,' kpds(4) = ',i7) + 982 format('tmow kpds(5) = ',i7,' kpds(6) = ',i7) + 983 format('tmow kpds(7) = ',i7,' kpds(8) = ',i7) + 984 format('tmow kpds(9) = ',i7,' kpds(10) = ',i7) + 985 format('tmow kpds(11) = ',i7,' kpds(12) = ',i7) + 986 format('tmow kpds(13) = ',i7,' kpds(14) = ',i7) + 987 format('tmow kpds(15) = ',i7,' kpds(16) = ',i7) + 988 format('tmow kpds(17) = ',i7,' kpds(18) = ',i7) + 989 format('tmow kpds(19) = ',i7,' kpds(20) = ',i7) + 990 format('tmow kpds(21) = ',i7,' kpds(22) = ',i7) + 991 format('tmow kpds(23) = ',i7,' kpds(24) = ',i7) + 992 format('tmow kpds(25) = ',i7) + 880 format('tmow kgds(1) = ',i7,' kgds(2) = ',i7) + 881 format('tmow kgds(3) = ',i7,' kgds(4) = ',i7) + 882 format('tmow kgds(5) = ',i7,' kgds(6) = ',i7) + 883 format('tmow kgds(7) = ',i7,' kgds(8) = ',i7) + 884 format('tmow kgds(9) = ',i7,' kgds(10) = ',i7) + 885 format('tmow kgds(11) = ',i7,' kgds(12) = ',i7) + 886 format('tmow kgds(13) = ',i7,' kgds(14) = ',i7) + 887 format('tmow kgds(15) = ',i7,' kgds(16) = ',i7) + 888 format('tmow kgds(17) = ',i7,' kgds(18) = ',i7) + 889 format('tmow kgds(19) = ',i7,' kgds(20) = ',i7) + 890 format('tmow kgds(20) = ',i7,' kgds(22) = ',i7) +c + print *,'just before call to putgb, kf= ',kf + call putgb (77,kf,kpds,kgds,lb,xmask,ipret) + print *,'just after call to putgb, kf= ',kf + if (ipret == 0) then + print *,' ' + print *,'+++ IPRET = 0 after call to putgb' + print *,' ' + else + print *,' ' + print *,'!!!!!! ERROR: IPRET NE 0 AFTER CALL TO PUTGB !!!' + print *,' ' + endif +c +c bug fix for IBM: flush the output stream so it actually writes + flush(6) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_next_ges (fixlon,fixlat,ist,ifh,imax,jmax + & ,dx,dy,modelid,valid_pt,readflag,maxstorm,istmspd + & ,istmdir,ctype,trkrinfo,ignret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. It does this by using two different +c methods and averaging the results from those two. The +c first method is a simple linear extrapolation made by +c basically drawing a line from the previous position +c through the current fix position and assuming straight +c line motion. The second method is to do a barnes +c smoothing of u & v in the vicinity of the storm at 850, +c 700 & 500 mb to get an average environmental wind +c vector at each level, and then move the storm according +c to the vector at each level. Then a weighted average is +c taken of all these positions from methods 1 & 2 to get +c the consensus for the guess position. NOTE: For a +c regional model and a storm that is relatively close to +c the model boundary, there is a strong possibility that +c the barnes analysis subroutine will fail due to trying +c to access grid points beyond the model's lateral +c boundary. In this case, the redlm & ridlm are halved +c and barnes is called again. If it still fails, then +c just use the result from method 1 as a default. +c +c INPUT: +c fixlon Array with longitudes of fix positions +c fixlat Array with latitudes of fix positions +c ist Storm number currently being processed +c ifh Forecast hour currently being processed +c imax Max number of pts in x-direction for this grid +c jmax Max number of pts in y-direction for this grid +c dx grid-spacing of the model in the i-direction +c dy grid-spacing of the model in the j-direction +c modelid Integer indicating what model's data is being processed +c valid_pt Logical; bitmap indicating if valid data at that pt. +c readflag Logical; Tells whether or not a variable was read in +c for this model +c maxstorm Max # of storms that can be handled in this run +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, eventually +c in the barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c istmspd The speed that the storm would have to move to get from +c the current position to the next guess position +c istmdir The direction in which the storm would have to move to +c get from the current position to the next guess position +c +c LOCAL: +c dt Number of seconds between successive forecast times +c for this particular model. +c dtkm Distance in meters of 1 degree latitude +c icutmax Max number of times to cut the ridlm and redlm in half, +c for use in calling barnes. If you're using a regional +c model and on the first call to barnes you try to access +c a point that's outside the model grid boundary, we'll +c call barnes again, but not before cutting the redlm and +c ridlm in half. icutmax says how many times to allow +c this cutting in half before giving up and just going +c with the extrapolation method. At first writing, we'll +c set icutmax to 2, so that it will allow the ridlm to +c get down to 500 km (originally 2000 km) and the redlm +c to 125 km (originally 500 km). +c *** NOTE: After testing the system, it was found that if +c we cut these radii, the u and v values that are +c calculated from barnes are too strongly influenced by +c the near-storm environment and, especially for asymmetric +c systems, resulted in u and v values being much too strong. +c As such, we will not allow these values to be cut, and if +c we hit the boundaries in barnes, we'll just use the +c extrapolation method, which has seemed to work just fine. +c +c OTHER: (slonfg, slatfg & storm defined in module def_vitals) +c slonfg Array containing first guess longitude positions +c slatfg Array containing first guess latitude positions +c storm Contains tcvitals information +c + USE radii; USE def_vitals; USE set_max_parms; USE grid_bounds + USE tracked_parms; USE level_parms; USE trig_vals; USE trkrparms + USE gen_vitals + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer icutmax,istmspd,istmdir,bskip,ileadtime,ifcsthour + integer ifh,ist,npts,ilonfix,jlatfix,ibeg,jbeg,iend,jend + integer igiret,ignret,icut,iuret,ivret,ibarnct,n,ix1,ix2 + integer icount,imax,jmax,modelid,maxstorm + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real barneswt,extrapwt,dtkm,dt,ucomp,vcomp,xdist,ydist,ydeg + real extraplat,avglat,cosfac,xdeg,extraplon,ylatdegmove_last + real xlondegmove_last,xnumh_last,ylatdegmove_last_perhour + real xlondegmove_last_perhour,xnumh_next,yoldavglat + real yoldcosfac,xdistmove_last,xdistmove_last_perhour + real ynewavglat,ynewcosfac,xdegnew,dx,dy,re,ri,ubar,vbar + real wgttot,uavg,vavg,reold,riold,barnlat,barnlon,wt_total + real tmp_fix_lon_curr,tmp_fix_lon_prev + character*1 :: in_grid, extrap_flag, barnes_flag + character(*) ctype +c logical(1) valid_pt(imax,jmax),readflag(14) +cPENG----2018-06-07 ------------------------ + logical(1) valid_pt(imax,jmax),readflag(19) +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c For updating the first guess, if Method 1 and Method 2 are both +c able to be done, give the following weights to the 2 methods. +c + data barneswt /0.50/, extrapwt /0.50/ +c +c ------------------------------- +c METHOD 1: LINEAR EXTRAPOLATION +c ------------------------------- +c First, just do a simple linear extrapolation from the previous +c fix position through the current fix position. If it's the +c first time (vt=0), then use the storm motion vector and storm +c speed information from the TC Vitals card. +c + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(ist)%tcv_stdir == -99 .or. + & storm(ist)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GET_NEXT_GES, at fcst hour = 0, either ' + print *,'!!! storm motion or storm speed = -99 on TCV card.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(ist)%tcv_stdir + print *,'!!! storm motion speed= ',storm(ist)%tcv_stspd + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + extrap_flag = 'n' + else + ucomp = sin(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + vcomp = cos(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(ist,ifh) + ydeg + avglat = 0.5 * (extraplat + fixlat(ist,ifh)) + if (avglat > 89.5) avglat = 89.0 + if (avglat < -89.5) avglat = -89.0 + cosfac = cos(avglat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(ist,ifh) + xdeg + endif + else + +c Do a simple linear extrapolation of the current motion of the +c storm. Follow a line from the fix position from the last fix +c through the current fix and extrapolate out. To figure out the +c new latitude, just see how many deg lat the storm moved since +c last time and add it to the current fix latitude. To calculate +c the new fix longitude, though, we need to see how many deg lon +c the storm moved since the last time, convert that to the +c distance (km) the storm travelled in the x-direction (at an +c average latitude between the current and previous latitudes), +c and then add that distance on to the current longitude and +c convert that distance to the num of degrees the storm has +c travelled in the x-direction (at an average latitude between +c the current and next(extrap) latitudes). +c +c UPDATE Feb 2009: To account for the possibility of using +c irregularly spaced forecast hours (e.g., 6,10,10.5,...etc), +c I had to modify this linear extrapolation. + + print *,' ' + print *,'xxxx get_next_ges, prev fix lon= ',fixlon(ist,ifh-1) + print *,'xxxx get_next_ges, curr fix lon= ',fixlon(ist,ifh) + print *,' ' + + if (fixlat(ist,ifh-1) > -900.0 .and. + & fixlon(ist,ifh-1) > -900.0) then + + ylatdegmove_last = fixlat(ist,ifh) - fixlat(ist,ifh-1) + + tmp_fix_lon_curr = fixlon(ist,ifh) + tmp_fix_lon_prev = fixlon(ist,ifh-1) + + if (tmp_fix_lon_prev < 0.0 .and. tmp_fix_lon_prev > -25.0) + & then + ! previous lon position is within 25 deg west of the GM + ! and is listed in negative degrees. + if (tmp_fix_lon_curr < 0.0 .and. tmp_fix_lon_curr > -25.0) + & then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 1 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both negative. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr < 25.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 2 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous time' + print *,' is negative, while lon for current time' + print *,' is positive, but within 0-25 deg East.' + print *,' All ok!' + endif + endif + + elseif (tmp_fix_lon_prev > 335.0 .and. + & tmp_fix_lon_prev <= 360.0) then + ! previous lon position is within 25 deg west of the GM + ! and is listed in positive degrees. + if (tmp_fix_lon_curr > 335.0 .and. + & tmp_fix_lon_curr <= 360.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 3 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both positive. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr <= 25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 4 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between 0 & 25. Current tmp_lon' + print *,' has been adjusted to be > 360 for the' + print *,' purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < 0.0 .and. + & tmp_fix_lon_curr >= -25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 5 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is west of the GM and' + print *,' is between 0 & -25. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < -335.0 .and. + & tmp_fix_lon_curr >= -360.0) then + tmp_fix_lon_curr = 720.0 - tmp_fix_lon_curr + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 6 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between -335 & -360. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + + endif + + endif + + xlondegmove_last = tmp_fix_lon_curr - tmp_fix_lon_prev + + xnumh_last = fhreal(ifh) - fhreal(ifh-1) + + ylatdegmove_last_perhour = ylatdegmove_last / xnumh_last + xlondegmove_last_perhour = xlondegmove_last / xnumh_last + + xnumh_next = fhreal(ifh+1) - fhreal(ifh) + + extraplat = fixlat(ist,ifh) + & + (ylatdegmove_last_perhour * xnumh_next) + + yoldavglat = 0.5 * (fixlat(ist,ifh) + fixlat(ist,ifh-1)) + yoldcosfac = cos (dtr * yoldavglat) + xdistmove_last = xlondegmove_last * dtk * yoldcosfac + + xdistmove_last_perhour = xdistmove_last / xnumh_last + + ynewavglat = 0.5 * (extraplat + fixlat(ist,ifh)) + ynewcosfac = cos(dtr * ynewavglat) + xdegnew = (xdistmove_last_perhour * xnumh_next) + & / (dtk * ynewcosfac) + extraplon = tmp_fix_lon_curr + xdegnew + + else + + if ( verb .ge. 3 ) then + print *,' ' + write(6,92) '!!! IN GET_NEXT_GES, at fcst hour = ' + & ,ifhours(ifh),ifclockmins(ifh) + print *,'!!! the lon and lat positions for the previous' + print *,'!!! forecast hour are -999, meaning that this is a' + print *,'!!! new storm, so we cannot use the extrap method.' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + 92 format (1x,a36,i4,':',i2.2) + + extrap_flag = 'n' + + endif + + endif + +c ------------------------------- +c METHOD 2: Barnes analysis +c ------------------------------- +c Do a barnes analysis on the u & v components of the wind near the +c storm to get an average u & v, then advect the storm according to +c the average wind vector obtained. The call to get_ij_bounds is +c needed in order to restrict the number of grid points that are +c searched in the barnes subroutine. See Abstract from this +c subroutine for further details. + + npts = ceiling(ridlm/(dtk*((dx+dy)/2))) + + call get_ij_bounds (npts,0,ridlm,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for ' + print *,'!!! storm number ',ist + endif + + ignret = 92 + return + endif + + if (verb >= 3) then + print *,' ' + print *,' +++ In get_next_ges after call to get_ij_bounds,' + print *,' getting bounds for the barnes analysis...' + print *,' glatmax= ',glatmax,' glatmin= ',glatmin + print *,' glonmax= ',glonmax,' glonmin= ',glonmin + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + print *,' ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + endif + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if ((dx+dy)/2 > 0.20) then + bskip = 1 + else if ((dx+dy)/2 > 0.10 .and. (dx+dy)/2 <= 0.20) then + bskip = 2 + else if ((dx+dy)/2 > 0.05 .and. (dx+dy)/2 <= 0.10) then + bskip = 3 + else if ((dx+dy)/2 > 0.03 .and. (dx+dy)/2 <= 0.05) then + bskip = 5 + else if ((dx+dy)/2 <= 0.03) then + bskip = 10 + endif + +c Calculate average wind at each level (currently: 850, 700 & 500) + + re = redlm + ri = ridlm + icut = 0 + + if (trkrinfo%type == 'midlat') then + icutmax = 2 + else + icutmax = 1 + endif + + radmaxloop: do while (icut <= icutmax .and. in_grid == 'n') + + ubar = 0.0; vbar = 0.0 + iuret = 0; ivret = 0 + wgttot = 0.0 + ibarnct = 0 + barnes_flag = 'n' + + levelloop: do n=1,nlevg + + select case (n) + case (1); ix1=3; ix2=4 ! For 850 mb readflags + case (2); ix1=5; ix2=6 ! For 700 mb readflags + case (3); ix1=12; ix2=13 ! For 500 mb readflags + end select + + if (readflag(ix1) .and. readflag(ix2)) then + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,u(1,1,n),valid_pt + & ,bskip,re,ri,uavg,icount,ctype,trkrinfo,iuret) + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,v(1,1,n),valid_pt + & ,bskip,re,ri,vavg,icount,ctype,trkrinfo,ivret) + + if (iuret /= 0 .or. ivret /= 0) then + +c ...barnes probably tried to access a pt outside the grid +c domain. So, reduce by half the distance from the center +c of the farthest pt that barnes tries to access, exit this +c loop, and try it again with the smaller re and ri. + + iuret = 96; ivret = 96 + reold = re + riold = ri + re = 0.5 * re + ri = 0.5 * ri + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: While attempting to use the barnes ' + print *,'method to update the first guess, the ' + print *,'algorithm tried to access a grid point that ' + print *,'does not have valid data, meaning that too ' + print *,'large a radius is being searched. So, the 2 ' + print *,'radii, re and ri, are being halved and, if the' + print *,'value of icutmax > 0, the algorithm will be ' + print *,'run again. Otherwise, if icutmax = 0, only ' + print *,'the extrapolation method will be used.' + print *,'iuret= ',iuret,' ivret= ',ivret,' icut= ',icut + print *,'Old re = ',reold,' New re = ',re + print *,'Old ri = ',riold,' New ri = ',ri + endif + + exit levelloop + + else + ubar = ubar + wgts(n) * uavg + vbar = vbar + wgts(n) * vavg + wgttot = wgttot + wgts(n) + ibarnct = ibarnct + 1 + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, ix1= ',ix1,' ix2= ',ix2 + print *,' uavg= ',uavg,' vavg= ',vavg + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' n= ',n,' wgts(n)= ',wgts(n),' wgttot= ' + & ,wgttot + print *,' ibarnct= ',ibarnct + print *,' ' + print *,' ' + endif + endif + + endif + + enddo levelloop + + if (ibarnct > 0 .and. wgttot > 0.0) then + barnes_flag = 'y' + in_grid = 'y' + ubar = ubar / wgttot + vbar = vbar / wgttot + barnlat = fixlat(ist,ifh) + (vbar * dt)/dtkm + cosfac = cos (dtr * 0.5 * (fixlat(ist,ifh) + barnlat)) + barnlon = fixlon(ist,ifh) + (ubar * dt)/(dtkm * cosfac) + + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, mean stats follow: ' + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' wgttot= ',wgttot + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' barnlon= ',barnlon,' barnlat= ',barnlat + print *,' dt= ',dt,' dtkm= ',dtkm,' cosfac= ',cosfac + endif + + +c This next if statement says that if we've had to reduce the +c size of the barnes analysis domain twice already, then we've +c only done the analysis on a much smaller area, and this +c doesn't give us as good a picture of the average winds in the +c area of the storm, so reduce the emphasis we place on the +c barnes method. + + if (icut >= 2) barneswt = barneswt / 2. + + else + barnes_flag = 'n' + endif + + icut = icut + 1 + + enddo radmaxloop + +c --------------------- +c Average the results +c --------------------- +c Now do a weighted average of the positions obtained from the +c linear extrapolation and the barnes analysis methods. + + if (extrap_flag == 'y' .and. barnes_flag == 'y') then + wt_total = barneswt + extrapwt + slatfg(ist,ifh+1) = (barneswt * barnlat + extrapwt * extraplat) + & / wt_total + + ! Note that in any of these statements just below, in order for + ! any of these to be > 360, the original fixlon must be close + ! to 360, i.e., in the far eastern part of the grid, as opposed + ! to being in the far western part (e.g., 0-2 deg East or so). + ! Conversely, for any of these to be < 0, the original fixlon + ! must be close to 0, i.e., in the far *western* part of the + ! grid. + +c yyyy + + if (fixlon(ist,ifh) > 330.0) then + + ! In this part of the IF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being 330+, to be consistent with the fixlon for + ! this time. + + if (extraplon > 330. .and. barnlon > 330.) then + + continue ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (extraplon > 330. .and. + & (barnlon >= 0.0 .and. barnlon < 30.)) then + + ! extraplon > 330, but barnlon is in the 0-30 range, so + ! we need to convert the barnlon value to be 360+ + + barnlon = barnlon + 360. + + elseif (extraplon > 330. .and. barnlon < 0.) then + + ! extraplon > 330, but barnlon is < 0, so + ! we need to convert the barnlon value to be positive... + + barnlon = barnlon + 360. + + elseif (barnlon > 330. .and. + & (extraplon >= 0.0 .and. extraplon < 30.)) then + + ! barnlon > 330, but extraplon is in the 0-30 range, so + ! we need to convert the extraplon value to be 360+ + + extraplon = extraplon + 360. + + elseif (barnlon > 330. .and. extraplon < 0.) then + + ! barnlon > 330, but extraplon is < 0, so + ! we need to convert the extraplon value to be positive... + + extraplon = extraplon + 360. + + endif + + elseif (fixlon(ist,ifh) >= 0. and. fixlon(ist,ifh) < 30.0) then + + ! In this part of the ELSEIF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being in the reference of >360 since that is what the + ! code below this is expecting with the computation of + ! slonfg for the next lead time. + + if ((extraplon >= 0. .and. extraplon < 60.) .and. + & (barnlon >= 0. .and. barnlon < 60.)) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon < 0. .and. extraplon > -60.) .and. + & (barnlon < 0. .and. barnlon > -60.)) then + + ! convert extraplon and barnlon to be positive + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon < 0.) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon < 0.) then + + extraplon = extraplon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon > 330.) then + + barnlon = barnlon + 360. + + elseif (barnlon >= 330. .and. extraplon < 60.) then + + extraplon = extraplon + 360. + + elseif (extraplon >= 330. .and. barnlon < 60.) then + + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon > 330.) then + + extraplon = extraplon + 360. + + endif + + else + + continue ! extraplon and barnlon do not need to be modified + ! since there should be no way that a storm + ! currently east of 30E and west of 30W could make + ! it to the Greenwich Mer in one forecast interval + + endif + + print *,' ' + print *,'+++ In get_next_ges, before averaging the 2 methods, ' + print *,' Raw (no conversion for GM wrap) barnlon= ' + & ,barnlon + print *,' Raw (no conversion for GM wrap) extraplon= ' + & ,extraplon + + slonfg(ist,ifh+1) = (barneswt * barnlon + extrapwt * extraplon) + & / wt_total + + if (slonfg(ist,ifh+1) > 360.) then + ! If we've GM-wrapped past 360, adjust it to be 0-360... + slonfg(ist,ifh+1) = slonfg(ist,ifh+1) - 360. + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'y' .and. barnes_flag == 'n') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_next_ges, barnes method was not ' + print *,'!!! done for updating the first guess for this ' + print *,'!!! storm. Only the linear extrapolation method ' + print *,'!!! was used.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + slatfg(ist,ifh+1) = extraplat + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 0.0,0.0,0.0 + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'n' .and. barnes_flag == 'y') then + slatfg(ist,ifh+1) = barnlat + if (barnlon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (barnlon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = barnlon + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 360.-barnlon,barnlat + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges, new position guess not' + print *,'!!! made. Could not get guess using either barnes' + print *,'!!! method or extrapolation method.' + print *,'!!! extrap_flag = ',extrap_flag + print *,'!!! barnes_flag = ',barnes_flag + print *,'!!! Storm number = ',ist,' ifh = ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + write (6,41) 0.0,0.0,0.0 + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 95 + endif + + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| Current fix & updated fix positions |' + print *,'-------------------------------------------------- ' + print *,'| In get_next_ges, current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',ist + print *,'| Return code from get_next_ges = ',ignret + print *,'| Storm Name = ',storm(ist)%tcv_storm_name + print *,'| Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + write (6,21) fixlat(ist,ifh) + write (6,23) 360.-fixlon(ist,ifh),fixlon(ist,ifh) + write (6,25) slatfg(ist,ifh+1) + write (6,27) 360.-slonfg(ist,ifh+1),slonfg(ist,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current fix lat is ',f7.2) + 23 format (' | Current fix lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') +c 41 format (' --- barnlon= ',f7.2,'W barnlat= ',f7.2) +c 43 format (' --- extraplon= ',f7.2,'W extraplat= ',f7.2) + + 41 format (' --- barnlon= ',f7.2,'E (',f7.2 + & ,'W) barnlat= ',f7.2) + 43 format (' --- extraplon= ',f7.2,'E (',f7.2 + & ,'W) extraplat= ',f7.2) + +c Now calculate the speed that the storm would have to move at in +c order to make it to the next forecast position. We will use +c this information in writing out the "gen_vitals" record, if this +c is requested. + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh) + & ,slonfg(ist,ifh+1),slatfg(ist,ifh+1),dist,degrees) + + ! convert distance from km to meters, then get speed in m/s. + + distm = dist * 1000. + stmspd = distm / dt + istmspd = int ((stmspd * 10) + 0.5) + + xincr = slonfg(ist,ifh+1) - fixlon(ist,ifh) + yincr = slatfg(ist,ifh+1) - fixlat(ist,ifh) + + if ( verb .ge. 3 ) then + print *,'iocheck, dist= ',dist,' distm= ',distm + print *,'iocheck, stmspd= ',stmspd,' istmspd= ',istmspd + print *,'iocheck, xincr= ',xincr,' yincr= ',yincr + endif + + if (xincr < 0.0 .and. slonfg(ist,ifh+1) < 30.0 .and. + & fixlon(ist,ifh) > 300.0) then + ! This means we have a storm moving east across the GM, and + ! so we are subtracting, for example, something like + ! 0.5 - 359.5, so redo xincr, but add 360 to slonfg first... + xincr = (slonfg(ist,ifh+1) + 360.0) - fixlon(ist,ifh) + else if (xincr > 300.0) then + ! This means we have a storm moving west across the GM, and + ! so we are subtracting, for example, something like + ! 359.5 - 0.5, so redo xincr, but add 360 to fixlon first... + xincr = slonfg(ist,ifh+1) - (fixlon(ist,ifh) + 360.0) + endif + + if (xincr == 0.0) then + if (yincr == 0.0) then + stmdir = 0.0 + else if (yincr > 0) then + stmdir = 360.0 + else if (yincr < 0) then + stmdir = 180.0 + endif + else if (xincr > 0.0) then + if (yincr == 0.0) then + stmdir = 90.0 + else + arct = atan(yincr/xincr) + stmdir = 90. - arct / dtr + endif + else if (xincr < 0.0) then + if (yincr == 0.0) then + stmdir = 270.0 + else + arct = atan(yincr/xincr) + stmdir = 270. - arct / dtr + endif + endif + + istmdir = int (stmdir + 0.5) + if (istmdir > 360) then + istmdir = 360 + else if (istmdir < 0) then + istmdir = 0 + endif + + if ( verb .ge. 3 ) then + print *,'iocheck, stmdir= ',stmdir,' istmdir= ',istmdir + endif + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine advect_tcvitals_from_hour0 (fixlon,fixlat,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. As of 11/2016, it is called only for the case in +c which we've got NetCDF data and no hour0 data, and so we want to +c simply take the TC Vitals data and advect the current position to +c a position at the next lead time. We can't use the code in +c subroutine get_next_ges because there are certain allocatable +c arrays in that subroutine that need to have been allocated first, +c and at this point prior to the first lead time in hour0, they +c haven't been allocated. +c +c INPUT: +c inctcv Index for storm number currently being processed +c ifh Forecast hour currently being processed +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c iatret Return code from this subroutine +c + USE def_vitals; USE trkrparms; USE tracked_parms + USE verbose_output; USE trig_vals; USE set_max_parms + USE gen_vitals + + type (trackstuff) trkrinfo + integer iatret,inctcv + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real ucomp,vcomp,xdist,ydist,ydeg,dt,extraplat + real cosfac + real dtkm +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c ------------------------------------------------------------------ +c Using the storm motion vector and storm translation speed as read +c from the TC Vitals card, do a simple linear extrapolation from the +c current observed (TC Vitals) position and advect the storm to a +c position at the next lead time. +c ------------------------------------------------------------------ + + iatret = 0 + + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(inctcv)%tcv_stdir == -99 .or. + & storm(inctcv)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In advect_tcvitals_from_hour0, at fcst hour= 0' + print *,'!!! either storm motion or storm speed = -99 on ' + print *,'!!! TCV card, ist= inctcv= ',inctcv,' ifh= ',ifh + print *,'!!! Storm name = ',storm(inctcv)%tcv_storm_name + print *,'!!! Storm ID = ',storm(inctcv)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(inctcv)%tcv_stdir + print *,'!!! storm motion speed= ',storm(inctcv)%tcv_stspd + print *,'... CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS ...' + print *,' ' + print *,'... Instead, we will simply use the current ' + print *,'... observed position from TC Vitals and hope that' + print *,'... it is close enough at the next lead time for ' + print *,'... the tracker to be able to still track it.' + print *,' ' + endif + extraplat = slatfg(inctcv,ifh) + extraplon = slonfg(inctcv,ifh) + else + ucomp = sin(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + vcomp = cos(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(inctcv,ifh) + ydeg + cosfac = cos(extraplat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(inctcv,ifh) + xdeg + endif + else + print *,' ' + print *,'!!! ERROR: In advect_tcvitals_from_hour0, the value of' + print *,' ifh is > 1, and this routine should only be called' + print *,' if ifh=1 (i.e., for hour0). STOPPING....' + print *,' ' + stop 95 + endif + + slatfg(inctcv,ifh+1) = extraplat + + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon >360 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon < 0 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + else + slonfg(inctcv,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| In advect_tcvitals_from_hour0, info on the ' + print *,'| positions for the current and next lead times ' + print *,'| follow: ' + print *,'-------------------------------------------------- ' + print *,'| current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',inctcv + print *,'| Return code from advect_tcvitals_from_hour0= ',iatret + print *,'| Storm Name = ',storm(inctcv)%tcv_storm_name + print *,'| Storm ID = ',storm(inctcv)%tcv_storm_id + write (6,420) gstorm(inctcv)%gv_gen_date + & ,gstorm(inctcv)%gv_gen_fhr + & ,gstorm(inctcv)%gv_gen_lat + & ,gstorm(inctcv)%gv_gen_latns,gstorm(inctcv)%gv_gen_lon + & ,gstorm(inctcv)%gv_gen_lonew,gstorm(inctcv)%gv_gen_type + write (6,21) fixlat(inctcv,ifh) + write (6,23) 360.-fixlon(inctcv,ifh),fixlon(inctcv,ifh) + write (6,25) slatfg(inctcv,ifh+1) + write (6,27) 360.-slonfg(inctcv,ifh+1),slonfg(inctcv,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current TC Vitals lat is ',f7.2) + 23 format (' | Current TC Vitals lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') + + + return + end +c +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getradii (xcenlon,xcenlat,imax,jmax,dx,dy,valid_pt + & ,cstormid,ifcsthr,vmaxwind,vradius,trkrinfo + & ,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) +c +c ABSTRACT: This subroutine looks through the wind data near an +c input storm center (fixlon,fixlat) and gets the radii of various +c surface winds in each of the 4 storm quadrants (NE,NW,SE,SW). +c The wind thresholds that are sought are gale force (34kt|17.5m/s), +c storm force (50kt|25.7m/s), and hurricane force (64kt|32.9m/s). +c This subroutine calls the Cray subroutine orders, which is a +c Cray-optimized sort routine. +c +c UPDATE (AUG 2001): The Cray subroutine orders was ported to the +c SP by NCEP personnel. On the SP version, some changes were +c apparently made so that the size of the arrays for calling +c arguments 2, 3 and 4 (iwork, dtemp and isortix in my calling +c routine) must be the same. This was not the case on the Crays, +c and this was causing the tracker to crash for cases far north +c on fine grids (GFDL 1/3 grid). +c +c UPDATE (AUG 2012): The call to the Cray subroutine orders was +c replaced with a call to qsort, which uses a quicksort sorting +c algorithm. While this is not the fastest sorting routine out +c there, we don't do a lot of sorting here, and qsort is simple +c and it is portable. +c +c UPDATE (April 2013): For the radii, we encountered a problem with +c radmax being too small. It was set at 650 km. Hurricane Sandy +c exceeded this in the models, so the values returned from getradii +c were close to the default radmax value of 650 km (350 nm), instead +c of much higher as they should have been. To fix it, we now use an +c iterative technique, where we start with radmax as a small value +c (450 km). If getradii returns a value for R34 in a quadrant that +c does not exceed 0.97*radmax, then that value is ok. If it does +c exceed 0.97*radmax, then we bump up radmax by 50 km and call +c getradii again, looking to diagnose radii only in those quadrants +c where the need_to_expand_r34 flag = 'n'. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c cstormid 3-character storm ATCF ID (e.g., 03L, 11E, etc) +c ifcsthr integer value for current forecast hour +c trkrinfo derived type containing various info on the storm +c need_to_expand_r34 1-character array that specifies which of the +c 4 quadrants still need to be expanded on this time +c through getradii in order to get an R34 value that is +c not right at the outermost boundary. +c vmaxwind max wind (in m/s) that was reported from the +c get_max_wind subroutine +c radmax input max radius (km) that will be used for this +c iteration of getradii. +c first_time_thru_getradii logical flag. It is used so that any +c checking for 50- or 64-kt radii is only done on the +c first time through getradii. Only the checking for +c 34-kt radii is done on multiple iterations. +c igrct integer that indicates what iteration of getradii this +c call is. +c +c OUTPUT: +c +c igrret return code from this subroutine +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c +c LOCAL: +c +c radmax the maximum radius to look for winds for the various +c thresholds. +c quadinfo This array contains the magnitude of the near-surface +c winds and the distance from the gridpoint to the fix +c position for each point in each quadrant that is within +c the maximum allowed radius, radmax. quadinfo is +c allocated within this subroutine, and is allocated as +c (quadrant, num_pts_in_quadrant, data_type), where +c data_type is either windspeed(1) or distance(2) from +c storm center to grid point. +c quadmax This array contains the max surface wind in each +c quadrant, plus the location of it and the distance from +c the storm center. This information is critical to +c identifying when this subroutine is malfunctioning. + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE level_parms + USE trkrparms + USE verbose_output + +c + type (trackstuff) trkrinfo +c + logical(1) valid_pt(imax,jmax) + logical(1) first_time_thru_getradii +c dimension iwork(257) + real, allocatable :: quadinfo(:,:,:),iwork(:) + real quadmax(4,4) + real exactdistnm,exactdistkm,radmax,degrees,cosarg + real rlonb,rlonc,rlatb,rlatc,vmaxwind + real pt_heading_rad,pt_heading,d + integer, allocatable :: isortix(:) + integer iwindix,ipoint,ifcsthr,igrct + integer quadct(4),vradius(3,4) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: dtemp(:) + real :: windthresh(3) = (/17.5,25.7,32.9/) + character cstormid*3 + character :: need_to_expand_r34(4)*1 + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *************************************************** ' + print *,' AT BEGINNING OF GETRADII, input radmax= ',radmax + print *,' *************************************************** ' + print *,' ' + print *,'xcenlon= ',xcenlon,' xcenlat= ',xcenlat + print *,'imax= ',imax,' jmax= ',jmax,' dx= ',dx,' dy= ',dy + endif + + igrret = 0 + +c ----------------------------------------------------------- +c PART 1: Define the maximum radius for which you'll search +c for the wind values, and then get the beginning and ending +c i and j points for that sub-region to search. Define this +c maximum radius (radmax) in terms of km. +c ----------------------------------------------------------- + +c radmax = 650.0 ! This value is in units of km. With April 2013 +c ! update, this is now defined in calling routine + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-A....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine getradii' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-B....' + stop 98 + endif + + igrret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmax is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmax/(dtk*dx))/cosfac) + numjpts = ceiling(radmax/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (ibeg < 1) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-C...' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jbeg < 1) jbeg = 1 + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in getradii calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Wind radii will not be calculated for this time.' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-D....' + stop 98 + endif + + igrret = 99 + return + endif + + if (iend > imax) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-E....' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getradii, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + +c ----------------------------------------------------------- +c PART 2: Within the area of grid points defined by jbeg, +c jend, ibeg and iend, (1) calculate all the wind speeds at +c each grid point, (2) calculate all of the distances from +c each grid point to the storm center, (3) assign each grid +c point to one of the 4 quadrants (NE,NW,SE,SW), (4) in each +c quadrant, sort the points, based on windspeed. +c ----------------------------------------------------------- + + jnum = jend - jbeg + 1 + inum = iend - ibeg + 1 +c numalloc = ((jnum * inum) / 2) + inum/2 + jnum/2 + numalloc = jnum * inum + inum/2 + jnum/2 + + if ( verb .ge. 3 ) then + print *,'in getradii, numalloc= ',numalloc,' radmax= ',radmax + endif + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + allocate (quadinfo(4,numalloc,2),stat=iqa) + + if (iqa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub getradii allocating quadinfo array.' + print *,'!!! iqa = ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-F....' + stop 98 + endif + + igrret = 94 + return + endif + + quadct = 0 + +c Calculate the distances and wind speeds at each grid point. If +c the distance is < radmax, include that wind info in the +c appropriate quadinfo array location for that quadrant. + + quadmax = 0.0 + + jloop: do j=jbeg,jend + iloop: do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point in question = ',i + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-G...' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub getradii' + print *,'!!! for a non-global grid. i= ',i + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-H....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + if (dist > radmax) cycle iloop + + if (valid_pt(ip,j)) then + + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + +cc print *,'i= ',i,' j= ',j,' dist= ',dist,' vmag= ',vmag + + ! Calculate the angle from the center point to this point + ! and then assign this point to the appropriate quadrant bin + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-xcenlon) * dtr + rlatb = xcenlat * dtr + d = degrees * dtr + +c write (6,59) 360.-xcenlon,xcenlat,360.-glon(ip),glat +c +c write (6,61) d/dtr,rlatc/dtr,360.-(rlonc/dtr),rlatb/dtr +c & ,360.-(rlonb/dtr),sin(rlatc),sin(rlatb),cos(d) +c & ,sin(d),cos(rlatb) +c +c +c 59 format (1x,'+++ gr, xcenlon= ',f8.3,'W xcenlat= ' +c & ,f8.3,' glon= ',f8.3,'W glat= ',f8.3) +c +c 61 format (1x,'+++ gr, d rlatc rlonc rlatb rlonb= ',5f9.4 +c & ,' sin(rlatc)= ',f8.6,' sin(rlatb)= ',f8.6 +c & ,' cos(d)= ',f8.6,' sin(d)= ',f8.6 +c & ,' cos(rlatb)= ',f8.6) + + if (d == 0.0) then + + pt_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d)) / + & (sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_heading_rad = acos(cosarg) + else + pt_heading_rad = 2*pi - acos(cosarg) + endif + + pt_heading = pt_heading_rad / dtr + + endif + + if (pt_heading >= 0.0 .and. pt_heading < 90.) then + ! NE quadrant + iq = 1 + else if (pt_heading >= 90.0 .and. pt_heading < 180.) then + ! SE quadrant + iq = 2 + else if (pt_heading >= 180.0 .and. pt_heading < 270.) then + ! SW quadrant + iq = 3 + else if (pt_heading >= 270.0 .and. pt_heading <= 360.) then + ! NW quadrant + iq = 4 + endif + +c write (6,73) xcenlat,360.-xcenlon,j,i,ip,glat(j) +c & ,360.-glon(ip),pt_heading,iq + + 73 format (1x,'+++ getradii clat clon: ',f6.2,' ',f7.2,'W',3i4 + & ,' plat plon: ',f6.2,' ',f7.2,'W Dir: ',f7.2 + & ,' Quad: ',i2) + + quadct(iq) = quadct(iq) + 1 + quadinfo(iq,quadct(iq),1) = vmag + quadinfo(iq,quadct(iq),2) = dist + if (vmag > quadmax(iq,4)) then + quadmax(iq,1) = glon(ip) + quadmax(iq,2) = glat(j) + quadmax(iq,3) = dist + quadmax(iq,4) = vmag + endif + + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After loop, quadct(1)= ',quadct(1),' quadct(2)= ' + & ,quadct(2) + print *,' quadct(3)= ',quadct(3),' quadct(4)= ' + & ,quadct(4) + print *,' ' + + write (6,110) cstormid,ifcsthr,'NE',quadmax(1,1),quadmax(1,2) + & ,quadmax(1,3)*0.539638,quadmax(1,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SE',quadmax(2,1),quadmax(2,2) + & ,quadmax(2,3)*0.539638,quadmax(2,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SW',quadmax(3,1),quadmax(3,2) + & ,quadmax(3,3)*0.539638,quadmax(3,4)*1.9427 + write (6,110) cstormid,ifcsthr,'NW',quadmax(4,1),quadmax(4,2) + & ,quadmax(4,3)*0.539638,quadmax(4,4)*1.9427 + print *,' ' + + 110 format (' quadmax: ',a3,1x,i3.3,1x,a2,1x,' lon: ',f6.2,'E',1x + & ,' lat: ',f6.2,' radius: ',f7.2,' nm',2x,' vmag: ' + & ,f6.2,' kts') + endif + +c Now go through each quadrant and put the wind speed distance info +c into a temporary array (dtemp), sort that array, and then scan +c through that array to find the various thresholds. + + quadrantloop: do k=1,4 + + if (need_to_expand_r34(k) == 'y') then + print *,'---> R34 search underway for quadrant ',k + & ,' radmax= ',radmax + continue + else + print *,'+ R34 okay for quadrant ',k,'... skipping...' + cycle quadrantloop + endif + + if (allocated(isortix)) deallocate (isortix) + if (allocated(dtemp)) deallocate (dtemp) + if (allocated(iwork)) deallocate (iwork) + allocate (isortix(quadct(k)),stat=iisa) + allocate (dtemp(quadct(k)),stat=idta) + allocate (iwork(quadct(k)),stat=iwa) + if (iisa /= 0 .or. idta /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii allocating isortix, dtemp' + print *,'!!! or iwork array for quadrant= ',k + print *,'!!! iisa = ',iisa,' idta= ',idta,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-I....' + stop 98 + endif + + itret = 94 + return + endif + +c ------------------- + + do m=1,quadct(k) + dtemp(m) = quadinfo(k,m,2) + enddo + + imode = 2 + isortix = 0 + + call qsort (dtemp,isortix,quadct(k)) + +ccccc call orders (imode,iwork,dtemp,isortix,quadct(k),1,8,1) +cccc call orders_4byte (imode,iwork,dtemp,isortix +cccc & ,quadct(k),1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' +c ************************************************************** +c--- mf 20100609 +c CAUSE OF SEG FAULT!!!!!!!! -- not sure still an issue if dtemp +c properly allocated +c + !print *,' dtemp(isortix(1)) = ',dtemp(isortix(1)) + print *,' dtemp(isortix(quadct(k)))= ' + & ,dtemp(isortix(quadct(k))) + print *,' isortix(1) = ',isortix(1) + print *,' isortix(quadct(k)) = ',isortix(quadct(k)) + endif + +c ! Uncomment these next lines to see a listing in the output of +c ! all wind values & distances in this quadrant less than radmax +c do iqq = 1,quadct(k) +c print *,' iqq= ',iqq,' vmag= ',quadinfo(k,isortix(iqq),1) +c & ,' dist= ',quadinfo(k,isortix(iqq),2) +c enddo + +c ------------------- + + if (quadct(k) < 2) then ! not enough members in array + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GETRADII, NOT ENOUGH MEMBERS IN ARRAY FOR' + print *,'!!! QUADRANT #',k,' .... # members = quadct(k)= ' + & ,quadct(k) + print *,'!!! SETTING ALL VRADII = 0 for quadrant = ',k + endif + + vradius(1,k) = 0 + vradius(2,k) = 0 + vradius(3,k) = 0 + cycle quadrantloop + endif + +c Within this quadrant, go through the sorted array of wind +c magnitudes and compare those wind values against the set +c wind thresholds to get the wind radii. The array has +c been sorted by distance from the storm center in order of +c closest (ipoint=1) to farthest (ipoint=quadct(k)). We +c analyze these wind values by starting at the farthest +c point and moving inward until we hit a point that has a +c wind value of at least 34-knot winds (17.5 m/s). When +c we find that point, we interpolate between that point and +c the next farthest out point to get the distance that would +c be for the exact 17.5 m/s value. We then continue searching +c through the wind values down closer to the storm center to +c see if we can find values for the 50- and 64-knot winds. + + iwindix = 1 + ipoint = quadct(k) + 1 + +c print *,'drp: quad= ',k,' quadct= ',quadct(k) + + threshloop: do while (iwindix <= 3 .and. ipoint > 1) + + if (iwindix > 1) then + if (first_time_thru_getradii) then + + ! We are only doing the wind radii for 50 and 64 kts on + ! the first time through subroutine getradii (we only + ! need to do the multiple call iterations for 34 kts). + ! + ! Make sure vmax for this lead time exceeds the radii + ! threshold being diagnosed. The check below avoids, + ! for example, reporting 50-kt wind radii when the max + ! wind diagnosed was only 44 kts. This can happen since + ! the radius for searching for radii is larger than the + ! radius for searching for the max wind. + if (vmaxwind >= windthresh(iwindix)) then + if (verb >= 3) then +c print *,' ' +c print *,' +++ vmaxwind of ',vmaxwind,' m/s exceeds' +c print *,' +++ threshold of ',windthresh(iwindix) +c print *,' +++ (m/s), so radii checking will continue' +c print *,' +++ for this threshold.' +c print *,' +++ igrct= ',igrct,' ipoint= ',ipoint +c & ,' iwindix= ',iwindix + continue + endif + continue + else + if (verb >= 3) then + print *,' ' + print *,' --- vmaxwind of ',vmaxwind,' m/s does NOT' + print *,' - - exceed threshold of ' + & ,windthresh(iwindix) + print *,' - - (m/s), so radii checking will NOT be ' + print *,' - - performed for this threshold.' + endif + iwindix = iwindix + 1 + cycle threshloop + endif + else + iwindix = iwindix + 1 + cycle threshloop + endif + endif + + ipoint = ipoint - 1 + + if (quadinfo(k,isortix(ipoint),1) < windthresh(iwindix)) then + cycle threshloop + else + if (ipoint == quadct(k)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In getradii, a max wind radius was' + print *,'!!! found at the maximum radius checked, so ' + print *,'!!! you may want to make sure that you are' + print *,'!!! checking at a far enough distance from ' + print *,'!!! the fix position, that is, you may want to' + print *,'!!! increase the value of radmax in subroutine' + print *,'!!! getradii. Currently, radmax (km) = ',radmax + print *,'!!! iwindix = ',iwindix,' quadrant= ',k + endif + + vradius(iwindix,k) = int( ((quadinfo(k,isortix(ipoint),2) + & * 0.5396) / 5.0) + 0.5) * 5 + else + +c Interpolate between the 2 closest distances to each wind +c threshold to get "exact" distance to that wind threshold +c radius, convert from km to nm, and then round to the +c nearest 5 nm (since TPC uses this precision). +c 7/23/98 UPDATE: Jim Gross has asked that values not be +c rounded to the nearest 5 nm, but rather only to the +c nearest 1 nm. + + exactdistkm = quadinfo(k,isortix(ipoint),2) + + & ( (quadinfo(k,isortix(ipoint),1) - windthresh(iwindix)) / + & (quadinfo(k,isortix(ipoint),1) - + & quadinfo(k,isortix(ipoint+1),1)) * + & ( (quadinfo(k,isortix(ipoint+1),2) - + & quadinfo(k,isortix(ipoint),2)) ) ) + + exactdistnm = exactdistkm * 0.5396 ! Convert km to nm + vradius(iwindix,k) = int(exactdistnm + 0.5) + +cc vradius(iwindix,k) = int( (exactdistnm / 5.0) + 0.5) * 5 + + + if ( verb .ge. 3 ) then + print *,'iwindix= ',iwindix,' exactdistnm = ' + & ,exactdistnm + print *,'vradius(iwindix,k) =',vradius(iwindix,k) + endif + + endif + +c The possibility exists, especially for coarse output +c grids, that there could be a jump over more than 1 wind- +c thresh category when going from 1 grid point to the next, so +c we need to account for this. For example, if 1 point has +c vmag = 15 m/s and the next point closer in has vmag = 28 +c m/s, then between those 2 points you have the thresholds +c for gale force AND storm force winds, so to be safe, we +c actually need to add 1 to ipoint and re-check the current +c point, if the wind value at that point is found to be +c greater than a wind threshold value (which it has if you've +c gotten to this point in threshloop). + + ipoint = ipoint + 1 + + iwindix = iwindix + 1 + + endif + + enddo threshloop + + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (idta /= 0 .or. iisa /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating isortix or' + print *,'!!! dtemp or work for quadrant= ',k + print *,'!!! idta= ',idta,' iisa= ',iisa,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-J....' + stop 98 + endif + + itret = 94 + return + endif + + enddo quadrantloop + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating quadinfo array.' + print *,'!!! iqa= ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-K....' + stop 98 + endif + + itret = 94 + return + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_max_wind (xcenlon,xcenlat,imax,jmax,dx,dy + & ,valid_pt,levsfc,vmax,trkrinfo,rmax,igmwret) +c +c ABSTRACT: This subroutine looks for the maximum near-surface wind +c near the storm center. This subroutine is only concerned with the +c value of the max wind, NOT where it's located radially with +c respect to the center. The value that's returned in vmax is the +c max wind speed in m/s, which are the units the data are stored in. +c However, when the max wind values are output in output_atcf, they +c will be converted from m/s to knots. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c levsfc integer holding the value of the array member that holds +c the near-surface winds in the u and v arrays (at orig +c writing, it's = 4). +c +c OUTPUT: +c +c vmax value of maximum near-surface wind near the storm ctr +c rmax radius of max winds +c igmwret return code from this subroutine +c +c LOCAL: +c +c radmaxwind the maximum radius to look for a max wind near the +c storm center. You have to allow this to be bigger for +c model grids with coarse resolution (ECMWF 2.5 degree). + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real radmaxwind,degrees,dx,dy,rmax + logical(1) valid_pt(imax,jmax) +c + igmwret = 0 + rmax = -99.0 + + if ((dx+dy)/2. <= 1.25) then + if ((dx+dy)/2. <= 0.25) then + radmaxwind = 300.0 + else + radmaxwind = 300.0 + endif + else + radmaxwind = 500.0 + endif + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmaxwind is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmaxwind/(dtk*dx))/cosfac) + numjpts = ceiling(radmaxwind/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_max_wind calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Value of vmax will be set to 0 for this time.' + endif + + vmax = 0.0 + igmwret = 99 + return + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + + print *,' ' + print *,'In get_max_wind, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + + vmax = 0.0 + do j=jbeg,jend + do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point = ',i + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + + if (dist > radmaxwind) cycle + + if (valid_pt(ip,j)) then + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + if (vmag > vmax) then + vmax = vmag + rmax = dist * 0.539638 ! convert from km to nm + endif + endif + + enddo + enddo + + if ( verb .ge. 3 ) then + print *,'At end of get_max_wind, vmax= ',vmax,' rmax= ',rmax + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine fixcenter (clon,clat,ist,ifh,calcparm,geslon,geslat + & ,inp,stderr,fixlon,fixlat,xvalues,maxstorm,ifret) +c +c ABSTRACT: This subroutine loops through the different parameters +c for the input storm number (ist) and calculates the +c center position of the storm by taking an average of +c the center positions obtained for those parameters. +c First we check to see which parameters are within a +c max error range (errmax), and we discard those that are +c not within that range. Of the remaining parms, we get +c a mean position, and then we re-calculate the position +c by giving more weight to those estimates that are closer +c to this mean first-guess position estimate. +c +c INPUT: +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c geslon Initial guess longitude for this storm at this fcst hour +c geslat Initial guess latitude for this storm at this fcst hour +c inp contains the input date and model number information +c xvalues The actual max or min data values for each parameter +c maxstorm max # of storms to be handled in this run +c +c INPUT/OUTPUT: +c stderr Standard deviation of the position "error" of the parms +c relative to the guess storm position. As long as the +c distance of a parm center to the guess center is <= +c errpmax, it is included in the std dev calculation. +c +c OUTPUT: +c fixlon Best approximation of storm center's longitude +c fixlat Best approximation of storm center's latitude +c ifret Return code from this subroutine +c +c LOCAL: +c storm Contains tcvitals info for the storms (def_vitals) +c trkerr_avg Sum/avg of the track errors for all parms for this +c fcst hour, regardless of whether or not the error was +c > errmax. It's used for getting the std deviation of +c the position error for this forecast time, to be used +c as part of the errmax calculation for the next fcst +c time. +c iclose Number of parameters whose position estimates are +c found to be within a distance errmax of the guess pos +c wtpos The weight given to each position estimate. It's +c based on the distance from the average position. +c errdist The "error" of the parameter center position relative +c to the storm's guess position. +c avgerr Average "error" of the parameter center positions +c relative to the storm's guess position. +c use4next Logical; If a parm center has been calculated but its +c distance from the guess position is > errmax, we don't +c use this center in calculating the new guess position, +c however we will use this position in calculating the +c standard deviation of the current time's guess +c positions, to be used in calculating the new errmax +c for the next forecast time. So in this subroutine, +c calcparm may be set to FALSE if errdist > errmax, but +c use4next will not be set to FALSE (Actually, it is +c only set to FALSE if errdist > errpmax, which is +c defined in error_parms and is roughly 600km). +c stderr_close Standard deviation of position errors for parms that +c have center estimates that are within a distance +c errmax of the guess position. +c clon_fguess These are the first-guess mean position estimates, +c clat_fguess which are the means of the position estimates that +c are within a distance errmax. These first-guess mean +c positions will be refined by giving more weight to +c individual parameter estimates that are closer to +c this first-guess mean position. +c dist_from_mean Contains the "error" distance of each parameter +c from the first-guess mean position (clon_fguess, +c clat_fguess). NOTE: If a parameter is not within +c a distance errmax of the guess position for this +c time (geslon,geslat), then there will be NO +c dist_from_mean calculated for that parm. +c + USE error_parms; USE set_max_parms; USE inparms; USE def_vitals + USE atcf; USE gen_vitals; USE tracked_parms + USE verbose_output + + type (datecard) inp + + real clon(maxstorm,maxtime,maxtp),temp_clon(maxtp) + real clat(maxstorm,maxtime,maxtp),temp_clat(maxtp) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real trkerr(maxtp),errdist(maxtp),xvalues(maxtp) + real stderr(maxstorm,maxtime),devia(maxtp),wtpos(maxtp) + real dist_from_mean(maxtp) + real degrees,errtmp + integer gt345_ct,lt15_ct + logical(1) calcparm(maxtp,maxstorm),use4next(maxtp) + character charparm(maxtp)*8,charmaxmin(maxtp)*8 +c + data charparm /'zeta 850','zeta 700','circ 850','NOT USED' + & ,'circ 700','NOT USED',' gph 850',' gph 700',' MSLP' + & ,'circ sfc','zeta sfc',' thk 5-8',' thk 2-5',' thk 2-8'/ + data charmaxmin /' Max ',' Max ',' Min ','NOT USED' + & ,' Min ','NOT USED',' Min ',' Min ',' Min ' + & ,' Min ',' Max ',' Max ',' Max ',' Max '/ +c + ifret=0 +c +c We need to judge whether each parameter position is reasonable, +c so we'll check to make sure that the dist from each parameter's +c estimate to the guess position is less than a maximum allowable +c error. If it's the first forecast time, use the initial error max +c (defined as errinit in error_parms) as errmax. Otherwise, the +c max error criterion is that the distance error must not exceed 3 +c times the previous forecast time's standard deviation (after a +c small growth factor has been applied). +c UPDATE 3/5/98: During testing, it was found that just using the +c previous time's stdev made errmax too "jumpy" (i.e., at vt=48h, +c errmax could = 380, and then at vt=54h, errmax could jump down +c to 190, so we've changed it so that it uses an average of the +c stdev's from the 3 previous forecast times to maintain some +c continuity between successive forecast times). +c + if (ifh == 1) then + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR' ) then + errmax = err_gfs_init + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errmax = err_ecm_max + errinit = err_ecm_max + else + errmax = err_reg_init + errinit = err_reg_init + endif + else + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR') then + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errinit = err_ecm_max + else + errinit = err_reg_max + endif + + if (ifh >= 4) then + xavg_stderr = (stderr(ist,ifh-3) + stderr(ist,ifh-2) + & + stderr(ist,ifh-1)) / 3.0 + else if (ifh == 3) then + xavg_stderr = (stderr(ist,ifh-2) + stderr(ist,ifh-1)) / 2.0 + else if (ifh == 2) then + xavg_stderr = stderr(ist,ifh-1) + endif + +c The following errmax statement was replaced by the ensuing 4 +c lines due to a compiler bug on some other platforms: +c errmax = amin1(amax1(3.0*xavg_stderr*errpgro,errinit) +c & ,errpmax) + + errtmp = 3.0*xavg_stderr*errpgro + errmax = max(errtmp,errinit) + errtmp = errpmax + errmax = min(errmax,errtmp) + + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (ifh > 1) then + print '(a42,f8.2,a15,f8.2)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = ' + & ,stderr(ist,ifh-1),' xavg_stderr= ',xavg_stderr + else + print '(a45,a18)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = N/A' + & ,' xavg_stderr= N/A' + endif + print *,'At beg of fixcenter, errpgro = ',errpgro + print *,'At beg of fixcenter, errinit = ',errinit + print *,'At beg of fixcenter, errpmax = ',errpmax + print *,'At beg of fixcenter, ifh= ',ifh,' errmax= ',errmax + endif + + trkerr_avg = 0.0 + iclose = 0; itot4next = 0 + clonsum = 0.0; clatsum = 0.0 + errdist = 0.0 + use4next = .FALSE. + gt345_ct = 0 + lt15_ct = 0 + +c For each parm, check to see if the estimated center is within +c distance errmax of the guess center. If it's within errmax, +c then use that parm for locating the center. If it's NOT +c within errmax, but IS within errpmax, then we still use this +c in calculating the standard deviation of the parameters for +c helping to determine the errmax for the next forecast hour. + +c OLD NOTE: For calculating the std dev to be used for the next +c OLD forecast hour, do NOT use vmag 850, vmag 700 or vmag sfc, since +c OLD those parms are always guaranteed to be within a short range of +c OLD the guess, due to the nature of the algorithm (see subroutine +c OLD get_uv_center for further details on that). + + do ip=1,maxtp + + if (ip == 4 .or. ip == 6) then ! Parms 4 & 6 not defined. + calcparm(ip,ist) = .FALSE. + cycle + endif + if (calcparm(ip,ist)) then + call calcdist (geslon,geslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + errdist(ip) = dist + if (dist <= errpmax) then + use4next(ip) = .TRUE. + trkerr_avg = trkerr_avg + dist + itot4next = itot4next + 1 + endif + if (dist <= errmax) then + iclose = iclose + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 + endif + clonsum = clonsum + clon(ist,ifh,ip) + clatsum = clatsum + clat(ist,ifh,ip) + else + calcparm(ip,ist) = .FALSE. + endif + endif + + enddo + + if (iclose > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (clonsum) + clon_fguess = (clonsum + (360.*float(lt15_ct)))/ iclose + else + clon_fguess = clonsum / float(iclose) + endif + if (clon_fguess >= 360.0) then + clon_fguess = clon_fguess - 360. + endif + clat_fguess = clatsum / float(iclose) + endif + +c Print out a table listing of the locations of the fixes for +c the individual parameters. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'--------------------------------------------------' + write (6,95) 'Individual fixes follow..., fhr= ',ifhours(ifh) + & ,ifclockmins(ifh),' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + write (6,97) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,'Model name = ',atcfname + print *,'Values of -99.99 indicate that a fix was unable to be' + print *,'made for that paramater. Parameters 4 & 6 are not' + print *,'used. Vorticity data values are scaled by 1e5.' + print *,'Circulation data values are scaled by 1e-6.' + print *,'errdist is the distance that the position estimate is' + print *,'from the guess position for this time. MSLP value ' + print *,'here may differ from that in the atcfunix file since ' + print *,'the one here is that derived from the area-averaged ' + print *,'barnes analysis, while that in the atcfunix file is ' + print *,'from a specific gridpoint.' + write (6,21) geslon,360.-geslon,geslat + write (6,*) ' ' + write (6,23) + write (6,25) + endif + + if (geslat > 0.0) then + charmaxmin(1) = ' Max ' + charmaxmin(2) = ' Max ' + charmaxmin(3) = ' Max ' + charmaxmin(5) = ' Max ' + charmaxmin(10) = ' Max ' + charmaxmin(11) = ' Max ' + else + charmaxmin(1) = ' Min ' + charmaxmin(2) = ' Min ' + charmaxmin(3) = ' Min ' + charmaxmin(5) = ' Min ' + charmaxmin(10) = ' Min ' + charmaxmin(11) = ' Min ' + endif + + do ip=1,maxtp + if (ip == 1 .or. ip == 2 .or. ip == 11) then + ! This IF block allows vorticity values to be + ! written out and scaled up by 1e5 ... + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + elseif (ip == 3 .or. ip == 5 .or. ip == 10) then + ! This IF block allows circulation values to be + ! written out and scaled down by 1e-6 ... + + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + else + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + endif + enddo + + 21 format (' Guess location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 23 format (' parm# parm Max/Min Lon_fix(E) Lon_fix(W)' + & ,' Lat_fix Max/Min_value calcparm errdist(km)') + 25 format (' ----- ---- ------- ---------- ----------' + & ,' ------- ------------- -------- ----------') + 27 format (2x,i2,4x,a8,2x,a8,3x,f7.2,5x,f7.2,4x,f7.2,7x,f9.2 + & ,6x,L2,7x,f7.2) + 95 format (1x,a33,1x,i4,':',i2.2,a2,a4,a1,a9) + 97 format (' Gen ID (if available): ',i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3) + + +c If number of parameter centers close enough (iclose) > 0, then +c calculate the center by taking an average of all the parameter +c center positions that are within distance errmax from the guess +c position (geslon,geslat). Get a first-guess mean position, and +c then re-calculate the position estimate by giving more weight +c to those positions that are closer to the first-guess mean +c position. + + dist_from_mean = 0.0 + + if (iclose > 0) then + +c Get distances from first-guess mean position.... + + do ip=1,maxtp + if (calcparm(ip,ist)) then + call calcdist (clon_fguess,clat_fguess,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + dist_from_mean(ip) = dist + endif + enddo + +c Get the mean distance of each parameter estimate from +c the first-guess mean position + + call avgcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,iaret) + + if (iaret == 0) then + + call stdevcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,stderr_close,isret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After stdevcalc, xmn_dist_from_mean= ' + & ,xmn_dist_from_mean,' stderr_close= ' + & ,stderr_close,' isret= ',isret + endif + + endif + if (iaret /= 0 .or. isret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER -- Error occurred in either' + print *,'!!! avgcalc or stdevcalc. Storm number = ',ist + print *,'!!! RCC from avgcalc = ',iaret + print *,'!!! RCC from stdevcalc = ',isret + print *,'!!! Center fix will NOT be made, and processing' + print *,'!!! for this storm is ending. The probable cause' + print *,'!!! is that no calcparms were valid for this storm' + print *,'!!! at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + if (calcparm(1,ist) .or. calcparm(2,ist) .or. calcparm(7,ist) + & .or. calcparm(8,ist) .or. calcparm(9,ist) + & .or. calcparm(11,ist) .or. calcparm(3,ist) + & .or. calcparm(10,ist) .or. calcparm(5,ist) + & .or. calcparm(12,ist) .or. calcparm(13,ist) + & .or. calcparm(14,ist)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In fixcenter, STOPPING PROCESSING for this' + print *,'!!! storm. The reason is that none of the fix' + print *,'!!! locations for parms z850, z700, zeta 850,' + print *,'!!! zeta 700, MSLP, wcirc_850, wcirc_700, ' + print *,'!!! wcirc_sfc, sfc zeta or the various levels ' + print *,'!!! of thicknesses were within a ' + print *,'!!! reasonable distance of the guess location.' + print *,'!!! ist= ',ist,' ifh= ',ifh + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'!!! Forecast hour: ',i4,':',i2.2) + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now re-calculate the mean position by giving more weight +c to those position estimates that are closer to the first +c guess mean position. Note that if stderr_close < 5.0, we +c force it to be 5.0; we do this to avoid getting very +c large numbers for devia values, which could make the +c weights (wtpos) equal to 0. This occurred during testing +c when only 2 parameters were valid, and so, of course, the +c standard deviation from the mean of those 2 parameters +c was close to 0, which gave devia values around 6000, and +c then wtpos values of 0, leading to a divide by 0 crash +c later on in subroutine wtavrg. + + kprm=0 + + if (stderr_close > 0.0) then + if (stderr_close < 5.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Since stderr_close had a value less than' + print *,'5, stderr_close has been forced to be equal' + print *,'to 5 in order to avoid dividing by zero later' + print *,'on in subroutine wtavrg.' + endif + + stderr_close = 5.0 + endif + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + devia(kprm) = dist_from_mean(ip) / stderr_close + wtpos(kprm) = exp(-devia(kprm)/3.) + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + + if ( verb .ge. 3 ) then + write (6,113) ip,kprm,dist_from_mean(ip),devia(kprm) + & ,wtpos(kprm),temp_clon(kprm) + & ,360.-temp_clon(kprm),temp_clat(kprm) + endif + + endif + enddo + 113 format (1x,'ip= ',i2,' kprm= ',i2,' dist_from_mean= ',f7.3 + & ,' devia= ',f7.3,' wtpos= ',f8.5,2x,3(2x,f7.2)) + else +c +c This next if statement is for the case in which only 1 +c parameter is valid, for which the stderr_close will = 0 +c (obviously), but as long as we have 1 valid parameter, +c continue processing, and set the weight for that parm = 1. +c The else portion is for the case in which stderr_close +c = 0 with NO parms being close. +c + if (iclose == 1) then + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + wtpos(kprm) = 1 + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + endif + enddo + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, stderr_close not > 0' + print *,'!!! stderr_close = ',stderr_close + print *,'!!! The probable cause is that no calcparms were' + print *,'!!! valid for this storm at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + endif +c + if (kprm > 0) then + call wtavrg_lon (temp_clon,wtpos,kprm,fixlon(ist,ifh),iwtret1) + call wtavrg (temp_clat,wtpos,kprm,fixlat(ist,ifh),iwtret2) + if (iwtret1 == 0 .and. iwtret2 == 0) then + if (verb .ge. 3) then + print *,' ' + write (6,173) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + 173 format ('At end of fixcenter: ',a4,' fhr= ',i4,':',i2.2 + & ,' Fix position= ',f7.2,'E (',f6.2,'W)',2x,f7.2) + print *,' ' + endif + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER in call to wtavrg.' + print *,'!!! Return Codes from wtavrg calls follow: ' + print *,'!!! RCC from wtavrg for long fix: ',iwtret1 + print *,'!!! RCC from wtavrg for lat fix: ',iwtret2 + print *,'!!! This means a divide by zero would have ' + print *,'!!! been attempted, which means that the ' + print *,'!!! weights in wtpos are not > 0. Check in' + print *,'!!! subroutine fixcenter where devia values' + print *,'!!! are calculated to see if something is ' + print *,'!!! wrong there. Values of wtpos array follow:' + print *,'!!! ',wtpos + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + print *,' ' + endif + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, kprm NOT > 0' + print *,'!!! This means that, for whatever reason, the ' + print *,'!!! calcparm logical flag was set to .FALSE. for' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: IN FIXCENTER, No storms are within errmax ' + print *,'!!! OR the calcparm logical flag was set to .FALSE. ' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now calculate the average error of all the parms that are within +c a radius errpmax (defined in error_parms, ~600km), and the std +c dev of those errors. This standard deviation will be used in +c calculating the maximum allowable error for the next forecast +c time. + + if (itot4next > 0 .and. ifret /= 95) then + trkerr_avg = trkerr_avg / float(itot4next) + call stdevcalc (errdist,maxtp,use4next,trkerr_avg + & ,stderr(ist,ifh),isret) + if (isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in FIXCENTER calculating std deviation ' + print *,'!!! for use in next forecast hours errmax.' + print *,'!!! ist= ',ist,' ifh= ',ifh,' itot4next= ' + & ,itot4next + endif + + ifret = 95 + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine avgcalc (xdat,kmax,valid,xavg,iaret) +c +c ABSTRACT: This subroutine just calculates a straight average of +c the parameters in the input array (xdat). The logical array +c (valid) indicates whether or not to include a particular array +c member or not in the calculation. + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) +c + iaret = 0 +c + xsum = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + xsum = xsum + xdat(i) + ict = ict + 1 + endif + enddo +c + if (ict > 0) then + xavg = xsum / float(ict) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in avgcalc, ict NOT > 0' + endif + + xavg = xdat(1) + iaret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg (xdat,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xdat) using the input weights +c in the input array (wt). It is used to calculate the center lat +c and lon fix positions. +c + USE verbose_output + + real xdat(kmax),wt(kmax) +c + iwtret = 0 +c + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xdat(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg, wtot NOT > 0' + endif + + iwtret = 95 + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg_lon (xlon,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xlon) using the input weights +c in the input array (wt). This subroutine is specifically used +c to find the center lon fix positions. It contains code to +c account for wrapping around the Greenwich Meridian. +c + + USE verbose_output + + real xlon(kmax),wt(kmax) + integer gt345_ct,lt15_ct +c + iwtret = 0 + gt345_ct = 0 + lt15_ct = 0 + +c First check to see if we have lons that are both to the left +c and the right of the greenwich meridian + + do i = 1,kmax + if (xlon(i) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (xlon(i) < 15.) then + lt15_ct = lt15_ct + 1 + endif + enddo + + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some lons that are in the 300's (west of the GM), and + ! some that are in the 0's (east of the GM). We need to + ! standardize these if we want to get a meaningful average. + do i = 1,kmax + if (xlon(i) < 15.) then + xlon(i) = xlon(i) + 360.0 + endif + enddo + endif + + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xlon(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg_lon, wtot NOT > 0' + endif + + iwtret = 95 + endif + + if (xwtavg >= 360.0) then + xwtavg = xwtavg - 360.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine stdevcalc (xdat,kmax,valid,xavg,stdx,isret) + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) + + isret = 0 + + stdx = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + stdx = stdx + (xdat(i) - xavg)**2 + ict = ict + 1 + endif + enddo + + if (ict > 0) then + stdx = sqrt(stdx/float(ict)) + if (stdx == 0.0) then +c This can happen if you have just 2 points; The mean position +c will be exactly in the middle of the 2 points and so the +c standard deviation around that mean point will be 0. And +c since the calling routine will quit if the returned standard +c deviation is 0, we must force it to be 1 so the program +c continues running. Theoretically, it could also happen with +c 3 or more points, but the likelihood of the distances working +c out to exactly equidistant for 3 points is not that good. + stdx = 1.0 + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in stdevcalc, ict NOT > 0' + endif + + isret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,fxval,trkrinfo + & ,cmodel_type,maxmin,igwcret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the wind circulation near the storm center. This center fix is +c done differently than for the other parms. With this fix, +c we limit the area that is searched. This subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the +c original guess position for this lead time and the 5 other parm +c fixes that have already been made for this lead time. That +c modified guess position is passed into this subroutine as uvgeslon +c and uvgeslat, and that's where the searching for the wind +c circulation is centered. +c +c This subroutine works by converting the winds to Vt and Vr at +c each grid point evaluated, relative to each candidate center point +c that is being evaluated at the time in the loop. We then compute +c the circulation at each of 24 azimuths surrounding the storm +c center, where circulation = Vt * (length of a 1/24 arc, in meters) +c This process is repeated for 7 successive radii and the results +c are summed up over all radii, approximating a solid disk +c circulation. The point at which the circulation is maximized +c (NHEM) or minimized (SHEM) is the center of circulation. +c +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c + + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + character(*) cmodel_type,maxmin + integer, parameter :: numdist=7,numazim=24 + integer imax,jmax,ist,level,igwcret,icvpret,idist,iazim + real rdist(numdist),vr(numazim,numdist),vt(numazim,numdist) + real vt_mean(numdist),circul_band(numdist) + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + real rads,ri,uvgeslon,uvgeslat,dx,dy,ctlon,ctlat,fxval + real temp_grid_minlon,temp_guesslon,rlatt,rlont,bear + real targlon,targlat,xintrp_u,xintrp_v,vt_azim_sum,degrees + real circ_diff,circ_diff_sum,hemisphere,wind_mag_ctr,dist + real xmin_circ_diff_mean,xmax_circ_diff_mean,tlon,tlat + real dell,fmax,fmin,grid_buffer,circ_diff_mean + real circumference,arclength + real circul_disk,xmax_circul_disk,xmin_circul_disk + integer ibiret1,ibiret2,igvtret,azimuth_ct,igiret,npts + integer igibret + integer circ_diff_ct,ir,nhalf,bskip1,bskip2,iskip,nlev + integer ilonfix,jlatfix,ibeg,iend,jbeg,jend,i,j,k,iix,jix + logical(1) cflag, valid_pt(imax,jmax) + +c---------------- +c + + print *,' ' + print *,'top of get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' cmodel_type= ',cmodel_type + print *,' maxmin= ',maxmin + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' cflag= ',cflag + print *,' ctlon= ',ctlon,' ctlat= ',ctlat + print *,' fxval= ',fxval + print *,' igwcret= ',igwcret + + igwcret = 0 + + grid_maxlat = glatmax + grid_minlat = glatmin + grid_maxlon = glonmax + grid_minlon = glonmin + + rads = rads_wind_circ + ri = ri_wind_circ + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of get_wind_circulation, rads= ',rads + & ,' ri= ',ri,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+15; fmin = 1.0e+15 + ctlon = 0.0; ctlat = 0.0 + +c Distances checked and the radial intervals are a function of +c the grid resolution.... + + if (dell > 0.50) then + rdist(1) = 50. + rdist(2) = 85. + rdist(3) = 120. + rdist(4) = 155. + rdist(5) = 190. + rdist(6) = 225. + rdist(7) = 260. + else + rdist(1) = 35. + rdist(2) = 65. + rdist(3) = 95. + rdist(4) = 125. + rdist(5) = 155. + rdist(6) = 185. + rdist(7) = 215. + endif + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + print *,' in get_wind_circulation, nlev= ',nlev + + if (uvgeslat >= 0.0) then + hemisphere = 1.0 + else + hemisphere = -1.0 + endif + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend + & ,igibret) + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (uvgeslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = uvgeslon - 360. + else + temp_guesslon = uvgeslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = uvgeslon + endif + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + +c For the wind circulation analysis, we will want to speed things +c up for finer resolution grids. We can do this by skipping some +c of the points in the wind circulation analysis. + + if (dell > 0.20) then + bskip1 = 1 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 3 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 5 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 8 + bskip2 = 3 + else if (dell <= 0.03) then + bskip1 = 10 + bskip2 = 4 + endif + +c bskip1 = 1 +c bskip2 = 1 + + jix = 0 + +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to first loop, ' + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop1: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = uvgeslat + dell*float(j) + + iix = 0 + + iloop1: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop1 + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT from call in ' + print *,'!!! get_wind_circulation: icvpret= ',icvpret + endif + cycle iloop1 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist,degrees) + if (dist .gt. rads) cycle iloop1 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop1: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop1: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + ! These calls to bilin_int_uneven pass a variable "level" + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop1 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop1 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop1 +cc and radiusloop1). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,' ' +cc print *,'1st run, wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'1st run, ir= ',ir,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +c print *,'1st run, circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'1st run, circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'1st run, xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif + + enddo iloop1 + + enddo jloop1 + + if (uvgeslat >= 0.0) then + write (6,61) 360.-ctlon,ctlat,xmax_circul_disk + else + write (6,63) 360.-ctlon,ctlat,xmin_circul_disk + endif + + 61 format (' After first run, Wind Circulation (NHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmax_circul_disk = ',f15.1) + 63 format (' After first run, Wind Circulation (SHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmin_circul_disk = ',f15.1) + +c If nhalf is specified as 0, then don't go through any more +c iterations of this routine, just exit with the value that we +c already got the first time through the loop, above. + + if (dell > 0.50) then + nhalf = 4 + else if (dell > 0.20 .and. dell <= 0.50) then + nhalf = 3 + else if (dell > 0.10 .and. dell <= 0.20) then + nhalf = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + nhalf = 1 + else if (dell <= 0.05) then +c nhalf = 0 + nhalf = 1 +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'In get_wind_circulation, dell is < 0.05 deg, so ' +c print *,'nhalf is set to 0 and only the first iteration of' +c print *,'the search loop is done.' +c print *,' dell= ',dell,' nhalf= ',nhalf +c endif + endif + + if (nhalf < 1) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +c npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only do this once +c for this grid-refinement (even though the grid is redefined +c nhalf times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). Cut the value of +c rads in half (only do this once) so that any points beyond +c rads/2 are not considered as potential centers. + + rads = 0.5 * rads + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igibret) + + if (igibret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_wind_circulation from call to ' + print *,'!!! get_ij_bounds just before nhalf loop. ' + print *,'!!! Stopping processing for storm number ',ist + endif + igwcret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + kloop: do k = 1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: get_wind_circ kloop, k= ',i2,' ' + & ,i2.2,':',i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'get_wind_circ nhalf loop, k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip,' rads= ',rads + endif + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to loop k= ',k + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist + & ,degrees) + if (dist .gt. rads) cycle iloop2 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop2: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop2: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop2 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop2 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop2 +cc and radiusloop2). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,'kloop k= ',k,' wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'kloop k= ',k,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +cc print *,'kloop k= ',k,' circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'kloop k=',k,' circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0.0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'kloop k= ',k,' xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean + + enddo iloop2 + + enddo jloop2 + + if ( verb .ge. 3 ) then + if (uvgeslat >= 0.0) then + print *,'---> xmax_circul_disk= ',xmax_circul_disk + write (6,71) k,360.-ctlon,ctlat,xmax_circul_disk + else + print *,'---> xmin_circul_disk= ',xmin_circul_disk + write (6,73) k,360.-ctlon,ctlat,xmin_circul_disk + endif + endif + + enddo kloop + + 71 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (NHEM: Max) = ' + & ,f15.1) + 73 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (SHEM: Min) = ' + & ,f15.1) + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,xval,trkrinfo,igucret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the minimum in the wind speed near the storm center. This center +c fix is done differently than for the other parms. With this fix, +c we severely limit the area that is searched, because we do not +c want to confuse a wind minimum out on the periphery of a storm +c with the center wind minimum. Therefore, this subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the guess +c position for this time and the 5 other parm fixes. That modified +c guess position is passed into this subroutine as uvgeslon and +c uvgeslat, and that's where the searching for the wind minimum +c is done. To get the wind minimum, the u and v data are first +c interpolated down to a fine grid (see details below for exact +c figures), and then a single-pass barnes analysis is done on that +c fine grid. The reason that we first interpolate the data (which +c is different from how we do the other parms) is that if we just +c use the original grid resolution, we may not be able to +c accurately pick out a minimum in the wind field at the center. +c + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real, allocatable :: uold(:,:),vold(:,:),unew(:,:),vnew(:,:) + real, allocatable :: rlonold(:),rlatold(:),rlonnew(:),rlatnew(:) + real, allocatable :: vmag(:,:) + real :: dx,dy + real :: grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + character*1 :: gotlat + logical(1) cflag, valid_pt(imax,jmax) + logical(1), allocatable :: lbi(:,:) +c + gotlat = 'n' +c +c ----------------------------------------------------------------- +c INTERPOLATE INPUT GRID TO SMALLER GRID +c ----------------------------------------------------------------- +c +c Get beginning and ending j points (on the input grid) for a +c smaller array that surrounds the storm. It is this smaller array +c that we will interpolate to a finer grid. +c +c Calculate number of pts to either side of this j to search +c + npts = ceiling(rads_vmag/(dtk*((dx+dy)/2.))) +c + call get_ij_bounds (npts,0,ritrk_vmag,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij D, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij D, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center from call to ' + print *,'!!! get_ij_bounds, stopping processing for ' + print *,'!!! storm number ',ist + endif + + igucret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, and our gridtype is NOT' + print *,'!!! global, so we are going to redefine ibeg to 1.' + print *,' ' + endif + + ibeg = 1 + endif + endif + + if (jbeg < 1) jbeg = 1 + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center calculating ibeg, iend, jbeg' + print *,'or jend. ibeg= ',ibeg,' iend= ',iend,' jbeg= ',jbeg + print *,'jend= ',jend + print *,'uv center will not be calculated for this time.' + endif + + igrret = 99 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, and our gridtype is' + print *,'!!! NOT global, so we will redefine iend to imax.' + print *,' ' + endif + + iend = imax + endif + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif +c + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the grid sizes for +c some of the typical grids that will be used: +c +c Original grid size # of interps Final grid size +c -------------------- ------------ --------------------- +c 1.00 deg (111.19 km) 3 0.125 deg (13.9 km) +c 1.25 deg (138.99 km) 3 0.156 deg (17.4 km) +c 2.50 deg (277.99 km) 4 0.156 deg (17.4 km) + + if ((dx+dy)/2. > 1.2) then + numinterp = 4 + else if ((dx+dy)/2. > 0.50 .and. (dx+dy)/2. <= 1.2) then + numinterp = 3 + else if ((dx+dy)/2. > 0.25 .and. (dx+dy)/2. <= 0.50) then + numinterp = 2 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.25) then + numinterp = 1 + else if ((dx+dy)/2. <= 0.10) then + numinterp = 0 + endif + + dell = (dx+dy)/2. + imxold = iend - ibeg + 1 + jmxold = jend - jbeg + 1 + +c -------------------------------------------------------------- +c Before interpolating, make sure that all the original +c points have valid data. If they don't then exit the +c subroutine. NOTE: This is NOT checking to see if ALL the pts +c on the complete & full input grid have valid data; it only +c checks those points that are within the box returned from +c get_ij_bounds. + + do i=ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_uv_center, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! PROCESSING WILL STOP. ' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + stop 94 + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_uv_center' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + do j=jbeg,jend + if (.not. valid_pt(ip,j)) goto 975 + enddo + + enddo + +c ------------------------------------ +c Now begin the interpolation process + + allocate (uold(imxold,jmxold),stat=iuo) + allocate (vold(imxold,jmxold),stat=ivo) + allocate (rlonold(imxold),stat=iloo) + allocate (rlatold(jmxold),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. iloo /= 0 .or. ilao /= 0) goto 970 + + do intnum = 1,numinterp + + if (intnum == 1) then + + do i=ibeg,iend + + ik = i + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ik = i + imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i < 1' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i + endif + + igucret = 92 + return + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ik = i - imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i > imax' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i,' imax= ',imax + endif + + igucret = 92 + return + endif + endif + + rlonold(i-ibeg+1) = glon(ik) + do j=jbeg,jend + uold(i-ibeg+1,j-jbeg+1) = u(ik,j,nlev) + vold(i-ibeg+1,j-jbeg+1) = v(ik,j,nlev) + if (gotlat == 'n') then + rlatold(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatold once + enddo + + else + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate (rlatold) + allocate (uold(imxnew,jmxnew),stat=iuo) + allocate (vold(imxnew,jmxnew),stat=ivo) + allocate (rlonold(imxnew),stat=iloo) + allocate (rlatold(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 970 + + gotlat = 'n' + do i=1,imxnew + rlonold(i) = rlonnew(i) + do j=1,jmxnew + uold(i,j) = unew(i,j) + vold(i,j) = vnew(i,j) + if (gotlat == 'n') then + rlatold(j) = rlatnew(j) + endif + enddo + gotlat = 'y' + enddo + + imxold = imxnew + jmxold = jmxnew + deallocate (unew); deallocate (vnew) + deallocate (rlonnew); deallocate (rlatnew) + + endif + + dell = 0.5 * dell + imxnew = 2 * imxold - 1 + jmxnew = 2 * jmxold - 1 + + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + + call bilin_int_even (imxold,jmxold,uold + & ,imxnew,jmxnew,unew,ibiret) + call bilin_int_even (imxold,jmxold,vold + & ,imxnew,jmxnew,vnew,ibiret) +c call lin_int (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int_lon (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int (jmxold,jmxnew,rlatold,rlatnew,iliret) + + chk_lonspc_old = rlonold(imxold) - rlonold(imxold - 1) + chk_latspc_old = rlatold(jmxold) - rlatold(jmxold - 1) + chk_lonspc_new = rlonnew(imxnew) - rlonnew(imxnew - 1) + chk_latspc_new = rlatnew(jmxnew) - rlatnew(jmxnew - 1) + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, intnum= ',intnum + print *,'imxold= ',imxold,' imxnew= ',imxnew + print *,'jmxold= ',jmxold,' jmxnew= ',jmxnew + print *,'Grid boundaries of modified uv grid: ' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ' + & ,grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ' + & ,grid_minlon + endif + + enddo + +c ------------------ + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate(rlatold) + + if (numinterp == 0) then + + ! No interpolations were done for this fine mesh grid, but we + ! need to fill some of these arrays and define variables for + ! subsequent subroutine calls just below here that require + ! the variables imxnew, jmxnew, and the arrays unew and vnew. + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional ' + print *,'grid; iend should not > imax here !!!' + endif + + igucret = 99 + return + endif + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional' + print *,'grid; ibeg should not < 1 here !!!' + endif + + igucret = 99 + return + endif + endif + + imxnew = iend - ibeg + 1 + jmxnew = jend - jbeg + 1 + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + gotlat = 'n' + + do i=ibeg,iend + + ip = i + + if (i > imax) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i - imax ! Wrapping past GM + endif + + if (i < 1) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i + imax ! Wrapping past GM + endif + + rlonnew(i-ibeg+1) = glon(ip) + do j=jbeg,jend + unew(i-ibeg+1,j-jbeg+1) = u(i,j,nlev) + vnew(i-ibeg+1,j-jbeg+1) = v(i,j,nlev) + if (gotlat == 'n') then + rlatnew(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatnew once + enddo + + endif + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,'Grid boundaries of modified uv grid in get_uv_center:' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ',grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ',grid_minlon + endif + + allocate (vmag(imxnew,jmxnew),stat=ivm) + allocate (lbi(imxnew,jmxnew),stat=ilb) + if (ivm /= 0 .or. ilb /= 0) goto 972 + call calc_vmag (unew,vnew,imxnew,jmxnew,vmag,icvret) + deallocate (unew); deallocate (vnew) + + lbi = .TRUE. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to find_maxmin, imxnew= ',imxnew + & ,'jmxnew= ',jmxnew,' ist= ',ist + write (6,171) dell,uvgeslon,360.-uvgeslon,uvgeslat + 171 format (' dell= ',f7.3,' uvgeslon= ',f8.3,'E (',f8.3,'W)' + & ,' uvgeslat= ',f8.3) + endif + +c Note that in the next call, I pass the 'global' argument to +c find_maxmin. This defines what type of grid it is, so that the +c proper grid_buffer can be chosen. This grid_buffer is designed +c to avoid having a center be chosen too close to the grid +c boundary. However, in the case of vmag here, we are only using +c a small subgrid, and we want to make sure we use *all* points +c in that subgrid for searching, and that will occur if we set that +c calling argument to 'global' as opposed to 'regional'. + + call find_maxmin (imxnew,jmxnew,dell,dell,'vmag' + & ,vmag,'min',ist,uvgeslon,uvgeslat,rlonnew,rlatnew,lbi + & ,trkrinfo,cflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,'global',ifmret) + deallocate (vmag); deallocate (lbi) + deallocate (rlonnew); deallocate (rlatnew) +c + if (ifmret == 0) then + goto 995 + else + igucret = ifmret + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center in call to find_maxmin' + print *,'!!! storm num = ',ist,' igucret = ',igucret + endif + + goto 998 + endif +c + 970 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either uold, vold,' + print *,'!!! rlonold or rlatold in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 971 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either unew, vnew,' + print *,'!!! rlonnew or rlatnew in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 972 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either vmag or lbi in ' + print *,'!!! subroutine get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! ivm= ',ivm,' ilb= ',ilb + endif + + igucret = 97 + goto 998 + + 975 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Inside get_uv_center, at least one of the points' + print *,'!!! is not a valid data point. This point may be ' + print *,'!!! outside the valid data bounds of a regional grid' + print *,'!!! i= ',i,' j= ',j + print *,'!!! Storm number = ',ist + endif + + igucret = 98 + goto 998 +c + 995 continue + igucret = 0 +c + 998 continue + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_guess (guesslon,guesslat,clon,clat + & ,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) +c +c ABSTRACT: The purpose of this subroutine is to get a modified +c first guess lat/lon position before searching for the +c minimum in the wind field. The reason for doing this is +c to better refine the guess and avoid picking up a wind +c wind minimum far away from the center. So, use the +c first guess position (and give it strong weighting), and +c then also use the fix positions for the current time +c (give the vorticity centers stronger weighting as well), +c and then take the average of these positions. +c +c INPUT: +c guesslon guess longitude for this forecast time +c guesslat guess latitude for this forecast time +c clon array with center longitude fixes for the various parms +c clat array with center latitude fixes for the various parms +c calcparm logical; tells whether or not a parm has a valid fix +c at this forecast hour +c ist index for current storm +c ifh index for current forecast hour +c maxstorm max # of storms that can be handled +c +c OUTPUT: +c uvgeslon contains modified guess longitude position at which to +c look for the wind minimum +c uvgeslat contains modified guess latitude position at which to +c look for the wind minimum +c igugret return code for this subroutine (0=normal) +c---- +c + USE set_max_parms; USE level_parms; USE error_parms + USE verbose_output + + logical(1) calcparm(maxtp,maxstorm) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real uvgeslon, uvgeslat + real guesslon,guesslat,degrees + integer gt345_ct,lt15_ct + + sumlon = 0.0 + sumlat = 0.0 + ict = 0 + gt345_ct = 0 + lt15_ct = 0 + +c NOTE: We need to be careful in this routine when averaging +c the longitudes together, in case we cross the greenwich +c meridian, because then we may be averaging 345+ lons with +c lons that are less than 15, giving incorrect results. +c Therefore, check for this, and if it occurs, add 360 onto +c any of the <15 lons (add it twice for those lons being +c counted twice (guesslon and the vorticity centers)). + +c Weight the uv guess position by counting the storm's guess +c position twice. + + sumlon = sumlon + 2.*guesslon + sumlat = sumlat + 2.*guesslat + ict = ict + 2 + + if (guesslon > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (guesslon < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct.... + endif + + do ip = 1,maxtp + if ((ip > 2 .and. ip < 7) .or. ip == 10) then + cycle ! because 3-6 are for 850 & 700 u & v and 10 is + ! for surface wind magnitude. + else + if (calcparm(ip,ist)) then + call calcdist (guesslon,guesslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + + if (dist < uverrmax) then +c +c Give the vorticity centers 2x weighting as well +c + if (ip == 1 .or. ip == 2 .or. ip == 11) then + sumlon = sumlon + 2.*clon(ist,ifh,ip) + sumlat = sumlat + 2.*clat(ist,ifh,ip) + ict = ict + 2 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct... + endif + else + sumlon = sumlon + clon(ist,ifh,ip) + sumlat = sumlat + clat(ist,ifh,ip) + ict = ict + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 ! Only 1 for non-zeta parms + endif + endif + + endif + + endif + endif + enddo +c + if (ict > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (sumlon) + uvgeslon = (sumlon + (360.*float(lt15_ct)))/ ict + else + uvgeslon = sumlon / ict + endif + if (uvgeslon >= 360.0) then + uvgeslon = uvgeslon - 360. + endif + uvgeslat = sumlat / ict + igugret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_guess, ict not > 0, ict= ',ict + print *,'!!! vmag center will not be calculated for this' + print *,'!!! storm -- at least not at this level' + print *,'!!! Storm number = ',ist + endif + + igugret = 91 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calc_vmag (xu,xv,imx,jmx,wspeed,icvret) +c +c ABSTRACT: This subroutine calculates the magnitude of the wind +c speed for an array of points, given real u and real v arrays. +c + real xu(imx,jmx),xv(imx,jmx),wspeed(imx,jmx) +c + do i=1,imx + do j=1,jmx + wspeed(i,j) = sqrt( xu(i,j)*xu(i,j) + xv(i,j)*xv(i,j) ) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_even (imxold,jmxold,xold + & ,imxnew,jmxnew,xnew,ibiret) +c +c ABSTRACT: This subroutine does a bilinear interpolation on a +c grid of evenly spaced data. Do NOT attempt to use this subroutine +c with data that are not evenly spaced or you will get unpredictable +c results. +c + real xold(imxold,jmxold), xnew(imxnew,jmxnew) +c +c +c --------------------------------------------------------------------- +c Latitude ----> | +c | +c L O e O e O e O e O | O: original point from input array +c o | +c n e 1 2 1 2 1 2 1 e | 1: interpolated, primary inter. pt +c g | +c i O 2 O 2 O 2 O 2 O | e: interpolated edge point +c t | +c u e 1 2 1 2 1 2 1 e | 2: interpolated, secondary inter. pt +c d | +c e O 2 O 2 O 2 O 2 O | Interpolations are done in the order +c | as indicated above; First, the input +c | e 1 2 1 2 1 2 1 e | 'O' pts are placed onto the new, +c | | larger grid. From that, the '1' pts +c | O 2 O 2 O 2 O 2 O | can be interpolated. Next, the edge +c | | (e) pts are interpolated using an +c v e 1 2 1 2 1 2 1 e | interpolation of two 'O' pts and one +c | '1' pt. Finally, the '2' pts are +c O e O e O e O e O | done using the 2 surrounding '0' and +c | '1' pts. Bilinear interpolation is +c | made incredibly easier by the fact +c | that the grid is evenly spaced. +c --------------------------------------------------------------------- +c NOTE: Remember that the arrays that are read in are indexed as +c (lon,lat), so that in the diagram above, pt (1,1) is at the upper +c left and pt (imax,jmax) is at the lower right, and each column is +c a new latitude and each row is a new longitude. +c +c ----------------------------------------------------------------- +c Put original (O) values from input array into new, expanded array +c ----------------------------------------------------------------- +c + do i=1,imxold + do j=1,jmxold + xnew(2*i-1,2*j-1) = xold(i,j) + enddo + enddo +c +c ---------------------------------------------- +c Interpolate to get primary interior (1) points +c ---------------------------------------------- +c + do i=1,imxold-1 + do j=1,jmxold-1 + xnew(2*i,2*j) = 0.25 * (xnew(2*i-1,2*j-1) + xnew(2*i+1,2*j-1) + & + xnew(2*i+1,2*j+1) + xnew(2*i-1,2*j+1)) + enddo + enddo +c +c --------------------------- +c Interpolate edge (e) points +c --------------------------- +c +c ... Northernmost 'e' points ... +c + j=1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,2)) + enddo +c +c ... Southernmost 'e' points ... +c + j = 2*jmxold - 1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,j-1)) + enddo +c +c ... Westernmost 'e' points ... +c + i=1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(2,2*j)) + enddo +c +c ... Easternmost 'e' points ... +c + i = 2*imxold - 1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(i-1,2*j)) + enddo +c +c ------------------------------------------------ +c Interpolate to get secondary interior (2) points +c ------------------------------------------------ +c + do j=2,2*jmxold-2 + istep = mod(j+1,2) + do i=istep+2,2*imxold-2,2 + xnew(i,j) = 0.25 * (xnew(i-1,j) + xnew(i,j-1) + xnew(i+1,j) + & + xnew(i,j+1)) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points +c + do i=1,ioldmax-1 + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int_lon (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. This particular +c routine is specifically used for interpolating +c longitudes, and it factors in the possibility of +c interpolating across the greenwich meridian. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points, and make the +c necessary adjustment when interpolating a longitude between, +c for example, 359.5 and 0.0. +c + do i=1,ioldmax-1 + if (xnew(2*i-1) > 350. .and. xnew(2*i+1) < 10.) then + xnew(2*i) = 0.5 * (xnew(2*i-1) + (360. + xnew(2*i+1))) + else + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + endif + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) +c +c ABSTRACT: This subroutine finds the maximum and mean zeta values +c at 850 & 700 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms + USE trkrparms; USE level_parms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07 ------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanzeta,dx,dy,re,ri,parmlon,parmlat + integer igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) + integer n,ix1,ix2,ilev,npts,imax,jmax,igzvret,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_zeta_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_zeta_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max zeta values at 850 and 700 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_zeta_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_zeta_loop: do n=1,2 + + gridpoint_maxmin = -99.0 + xmeanzeta = -99.0 + compflag = .true. + + select case (n) + case (1); ilev=850 ! For 850 mb + case (2); ilev=700 ! For 700 mb + end select + + if (zeta(ilonfix,jlatfix,n) > -9990.0) then + + ! ------------------------------------------- + ! We have valid zeta data for this level, so + ! we first call barnes now to get the mean zeta + ! surrounding our found center position. + ! ------------------------------------------- + + if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,n),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanzeta + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds + imeanzeta(n) = int ((xmeanzeta * 1e6) + 0.5) + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_zeta_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for zeta values will not be done.') + exit report_zeta_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + exit report_zeta_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) n,ilev,xmeanzeta,imeanzeta(n) + 621 format (1x,'+++ RPT_MEAN_ZETA: n= ',i2,' lev= ',i4 + & ,' xmeanzeta= ',f9.6,' imeanzeta (*1e6)= ',i8) + write (6,*) ' --- mean zeta raw = ',xmeanzeta + endif + + ! ----------------------------------------------- + ! Call fix_latlon_to_ij to get the nearest actual + ! raw (grid) zeta data value, not the mean value. + ! ----------------------------------------------- + + call fix_latlon_to_ij (imax,jmax,dx,dy + & ,zeta(1,1,n),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanzeta,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + igridzeta(n) = int ((gridpoint_maxmin * 1e6) + 0.5) + else + igridzeta(n) = -99 + endif + + if ( verb .ge. 3 ) then + write (6,623) n,ilev,gridpoint_maxmin,igridzeta(n),ifilret + 623 format (1x,'+++ RPT_GRID_ZETA: n= ',i2,' lev= ',i4 + & ,' grid zeta= ',f9.6,' igrid zeta (*1e6)= ',i8 + & ,' ifilret= ',i3) + write (6,*) ' --- grid zeta raw= ',gridpoint_maxmin + endif + + enddo report_zeta_loop + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get 850 & 700 zeta for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine find_maxmin (imax,jmax,dx,dy,cparm,fxy,maxmin,ist + & ,guesslon,guesslat,rlonv,rlatv,valid_pt,trkrinfo + & ,compflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,cmodel_type,ifmret) +c +c This routine finds the location (clon,clat) of and value of the +c the max or min of fxy in the vicinity of slon,slat. The value of +c the input flag maxmin determines whether to look for a max or a +c min value. The max/min is determined by finding the point which +c gives the max/min value of a single point barnes analysis of fxy +c with e-folding radius re (km) and influence radius ri (km). The +c initial search is restricted to a radius rads around the point +c (slon,slat) on a grid with lon,lat spacing dx and dy. The location +c is refined by reducing the spacing of the search grid by a factor +c of two, nhalf times. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c maxmin Char string indicating whether to search for a max or min +c ist Number of the storm being processed +c guesslon Guess longitude of the storm +c guesslat Guess latitude of the storm +c rlonv Array containing longitude values of input grid points +c rlatv Array containing latitude values of input grid points +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c trkrinfo derived type detailing user-specified grid info +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c +c INPUT/OUTPUT: +c compflag Logical; continue processing this storm or not (would be +c set to FALSE if, for example, the guess position is +c outside the domain of a regional grid) +c +c OUTPUT: +c ctlon Center longitude of storm found for this parameter +c ctlat Center latitude of storm found for this parameter +c xval Max or Min value found at the (ctlon,ctlat) +c ifmret Return code from this subroutine +c +c UPDATE DEC 2009: For the HFIP HRH testing, it was found that +c due to the very limited domain size of some of the models, the +c barnes scheme was allowing points close to the grid boundaries +c to erroneously be selected as the center point. We add in a +c buffer (grid_buffer) here to prevent this from occurring. + + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + character(*) maxmin,cparm,cmodel_type + logical(1) compflag, valid_pt(imax,jmax) + real fxy(imax,jmax),rlonv(imax),rlatv(jmax) + real ctlon,ctlat,degrees,dx,dy,guesslon,guesslat,xval + real rads,re,ri,dell,fmax,fmin,rlatt,rlont,dist,ftemp,ritmp + real vmag_latmax,vmag_latmin,vmag_lonmax,vmag_lonmin,retmp + real tlon,tlat,grid_buffer,temp_grid_minlon,temp_guesslon + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + integer imax,jmax,ist,bskip1,bskip2,iskip,ifmret,npts,maxvgrid + integer ibeg,iend,jbeg,jend,ilonfix,jlatfix,igiret,icount,iret + integer ibct,ibarnes_loopct,i,j,k,iix,jix,jvlatfix,ivlonfix + integer nhalf,icvpret + integer date_time(8) + character (len=10) big_ben(3) +c + ifmret = 0 + nhalf = 5 +c +c ----------------------------------------------------------- +c Set initial parms for use in find_maxmin. +c Different radii used for V magnitude than for other parms, +c see discussion in module radii for more details. +c + if (cparm == 'vmag') then + +c NOTE: The maxvgrid variable determines what size grid to send +c to subroutine barnes. e.g., maxvgrid = 8 means send an +c 8x8 grid; maxvgrid = 12 means send a 12x12 grid. For +c ultra-fine mesh grids (finer than 0.04 deg, or 1/25 deg), +c we expand to 12 in order to sample a few more points +c around each grid point. + + if ((dx+dy)/2. > 0.04) then + maxvgrid = 8 + else + maxvgrid = 12 + endif + + rads = rads_vmag; re = retrk_vmag; ri = ritrk_vmag + re = (float(maxvgrid)/4) * ((dx+dy)/2. * dtk) ! Basically, this +c sets re equal to half the distance from the gridpoint +c in question to the farthest point that will be +c sampled when the (maxvgrid x maxvgrid) grid is passed +c on to subroutine barnes. Thus, just ignore the +c parameter retrk_vmag, and use this instead. + else if ((dx+dy)/2. < 1.26 .and. (dx+dy)/2. >= 0.40) then + rads = rads_most; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.40 .and. (dx+dy)/2. >= 0.10) then + rads = rads_fine; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.10) then + rads = rads_hres; re = retrk_hres; ri = ritrk_most + else + rads = rads_coarse; re = retrk_coarse; ri = ritrk_coarse + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of find_maxmin, rads= ',rads,' re= ',re + & ,' ri= ',ri,' cparm= ',cparm,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+12; fmin = 1.0e+12 + ctlon = 0.0; ctlat = 0.0 + + if (npts == 0) npts = 1 + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if (dell > 0.20) then + bskip1 = 2 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 4 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 6 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 10 + bskip2 = 5 + else if (dell <= 0.03) then + bskip1 = 15 + bskip2 = 5 + endif + + if (cparm == 'vmag') then + bskip1 = 1 + bskip2 = 1 + endif + +c If input parm is vmag, we've already done the minimizing by +c interpolating to the fine mesh grid, so we'll simply send the +c bounds that were input to this subroutine to barnes +c as boundaries for the array to search. For all other parms, +c however, no minimizing has been done yet, so we need to call +c get_ij_bounds to set the boundaries for a much smaller grid that +c surrounds the storm (as opposed to having subroutine barnes +c search the entire global grid). + + if (cparm == 'vmag') then + + if ( verb .ge. 3 ) then + print *,'In find_maxmin, jmax= ',jmax,' imax= ',imax + endif + + ibeg=1; jbeg=1; iend=imax; jend=jmax + vmag_latmax = rlatv(1) ! N-most lat of vmag subgrid + vmag_latmin = rlatv(jmax) ! S-most lat of vmag subgrid + vmag_lonmin = rlonv(1) ! W-most lon of vmag subgrid + vmag_lonmax = rlonv(imax) ! E-most lon of vmag subgrid + + if ( verb .ge. 3 ) then + write (6,44) vmag_latmax,vmag_lonmin,360.-vmag_lonmin + & ,imax,jmax + write (6,46) vmag_latmin,vmag_lonmax,360.-vmag_lonmax + endif + + 44 format (' vmag_latmax= ',f8.3,' vmag_lonmin= ',f8.3 + & ,'E (',f8.3,'W) imax= ',i4,' jmax= ',i4) + 46 format (' vmag_latmin= ',f8.3,' vmag_lonmax= ',f8.3 + & ,'E (',f8.3,'W)') + + if (vmag_lonmin > 330. .and. vmag_lonmax < 30.) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: For a case of find_maxmin, our vmag' + print *,'!!! subgrid is straddling the GM. The code should' + print *,'!!! be able to handle this, but if strange errors' + print *,'!!! are occurring, check into the code either here' + print *,'!!! in find_maxmin or get_uv_ctr.' + print *,' ' + endif + endif + + npts = ceiling(rads/(dtk*dell)) + + else + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,guesslon,guesslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to ' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + ifmret = 92 + return + endif + + endif + +c +c --------------------------------------------------------------- +c + if ( verb .ge. 3 ) then + print *,' ' + write (6,39) guesslon,360.-guesslon,guesslat + 39 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + if (cparm == 'vmag') then + print *,'ilonfix= (unused) jlatfix= (unused)' + & ,' npts= ',npts + print *,'ilonfix and jlatfix are meaningless for computing' + print *,'vmag, so ignore the large values you see for them.' + else + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + endif + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + + if ( verb .ge. 3 ) then + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: find_maxmin 1 ',i2.2,':',i2.2,':',i2.2) + endif + + ibct=0 + ibarnes_loopct = 0 + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (guesslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = guesslon - 360. + else + temp_guesslon = guesslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = guesslon + endif + + jix = 0 + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + jloop: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = guesslat + dell*float(j) + + iix = 0 + +c vlat(jix) = rlatt + + iloop: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c if (cparm == 'vmag') then +c print *,' ' +c print '(a16,i6,a4,i6,2(a8,f8.3),a12,f8.3)' +c & ,'in find_max, i= ',i +c & ,' j= ',j,' rlatt= ',rlatt,' rlont= ',rlont +c & ,' 360-rlont= ',360.-rlont +c endif + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT: icvpret= ',icvpret + endif + + cycle iloop + endif + + call calcdist(rlont,rlatt,temp_guesslon,guesslat,dist,degrees) + if (dist .gt. rads) cycle iloop + + if (cparm == 'vmag') then + +c This next bit of code gets the ij coordinates for an 8x8 +c box around the current point under consideration. These ij +c coordinates are sent to barnes so that barnes only loops +c 64 times, as opposed to nearly 10,000 if the whole 97x97 +c array were sent. So, fix rlatt to the grid point just +c northward of rlatt and fix rlont to the grid point just +c eastward of rlont. Note that this makes for a modified +c barnes analysis in that we're sort of specifying ahead of +c time exactly which grid points will be included and we'll +c be excluding some points that would be near the periphery +c of each (rlont,rlatt)'s range, but as long as we're consis- +c tent and do it this way for each point, it's well worth the +c trade-off in cpu time. Parameter maxvgrid determines what +c size array to send to barnes (maxvgrid=8 means 8x8) + + jvlatfix = int((vmag_latmax - rlatt)/dy + 1.) + ivlonfix = int((rlont - temp_grid_minlon)/dx + 2.) +c ivlonfix = int((rlont - vmag_lonmin)/dx + 2.) + + ibeg = ivlonfix - (maxvgrid/2) + iend = ivlonfix + (maxvgrid/2 - 1) + jbeg = jvlatfix - (maxvgrid/2 - 1) + jend = jvlatfix + (maxvgrid/2) + + if (ibeg < 1 .or. jbeg < 1 .or. + & iend > imax .or. jend > jmax) then + + ! DO NOT quit if we find a boundary outside the grid + ! bounds. Rather, just set the J violating bound(s) to + ! the min or max limit, and for I bounds, allow the + ! program to continue down to subsequent code below, + ! provided it's a global grid. + +c print *,'!!! ' +c print *,'!!! Before vmag adjustments, boundaries are: ' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt,' dx= ',dx +c print *,'!!! temp_grid_minlon= ',temp_grid_minlon +c print *,'!!! vmag_latmax= ',vmag_latmax +c print *,'!!! ivlonfix = ',ivlonfix,' jvlatfix = ',jvlatfix +c print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax +c print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Vmag will not be computed for' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Vmag will not be computed for ' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,'!!! ' + print *,'!!! *AFTER* vmag adjustments, boundaries are: ' + print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax + print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + endif + + endif + + endif + + if (cparm == 'vmag') then + ri = re * 3 +c print '(a36,f10.4,a6,f10.4)' +c & ,' + before call to vmag barnes, re= ',re,' ri= ',ri + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,bskip1,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes...' + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After 1st findmax loop, # calls to barnes = ',ibct + print *,'Total # of barnes loop iterations = ',ibarnes_loopct + endif + +c + 55 format ('i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ',f7.3 + & ,' barnval= ',f11.5) + 56 format ('k= ',i3,' i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ' + & ,f7.3,' barnval= ',f11.5) + + if (ctlon < 0.) then + ! We have grid-wrapped to find the ctlon, which was found to be + ! < 0, so for reporting purposes and for the start of the next + ! loop, set ctlon to positive degress east. + ctlon = ctlon + 360. + endif + + if (cparm == 'zeta') then + + if ( verb .ge. 3 ) then + print *,'!!! Zeta check, fmax= ',fmax,' fmin= ',fmin + write (6,61) 360.-ctlon,ctlat,fmax*100000.,fmin*100000. + endif + + else + + if ( verb .ge. 3 ) then + write (6,63) 360.-ctlon,ctlat,fmax,fmin + endif + + endif + 61 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 63 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax = ',e16.3,' fmin = ',e16.3) + 111 format (i2,' rlont= ',f7.2,'W rlatt= ',f7.2,' zeta= ',f13.8) + +c Through interpolation, the grid for vmag has already been +c minimized considerably, we don't need to go through the 2nd part +c of this subroutine, which halves the grid spacing. + + if (nhalf < 1 .or. cparm == 'vmag') then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c ------------------------------------------------------------- +c If the grid spacing is +c fine enough (I've chosen 0.2-deg as a min threshold), there is +c no need to halve the grid more than 3 times, as halving a +c 0.2-deg grid 3 times gives a resolution of 0.025-deg (2.7 km), +c or a max error in the position estimate of 2.7/2 = 1.35 km. + + if ((dx+dy)/2. <= 0.2) then + if ((dx+dy)/2. <= 0.05) then + nhalf = 1 + else + nhalf = 2 + endif + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +ctpm npts = 3 + npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only +c do this once for this grid-refinement (even though the grid is +c redefined 3 times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to get_ij_bounds' + print *,'!!! just before nhalf loop. Stopping processing' + print *,'!!! for storm number ',ist + endif + + ifmret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + + if ( verb .ge. 3 ) then + print *,' ' + endif + + if ((dx+dy)/2. <= 1.25 .and. ri >= 300 .and. re >= 150) then + retmp = re + ritmp = ri + re = re * 0.5 + ri = ri * 0.5 + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re has been reduced' + print *,'from ',retmp,' to ',re,', and ri has been reduced ' + print *,'from ',ritmp,' to ',ri + endif + + else + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re and ri have NOT ' + print *,'been changed. re = ',re,' ri = ',ri + endif + + endif + + ibct=0 + ibarnes_loopct = 0 + do k=1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: find_maxmin kloop, k= ',i2,' ',i2.2,':' + & ,i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat + fmax = -1.0e+15; fmin = 1.0e+15 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'find_maxmin nhalf loop, cparm= ',cparm,' k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,iskip,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes, k= ',k + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop2 + enddo jloop2 + + if ( verb .ge. 3 ) then + if (cparm == 'zeta') then + write (6,71) k,360.-ctlon,ctlat,fmax*100000.,fmin*100000. + else + write (6,73) k,360.-ctlon,ctlat,fmax,fmin + endif + endif + + enddo + + 71 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 73 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax = ',e16.3,' fmin = ',e16.3) + + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ppp after 2nd findmax loop, # calls to barnes = ' + & ,ibct + print *,'ppp Total # of barnes loop iterations = ' + & ,ibarnes_loopct + endif + + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine barnes(flon,flat,rlon,rlat,iimax,jjmax,iibeg,jjbeg + & ,iiend,jjend,fxy,defined_pt,bskip,re,ri,favg,icount,ctype + & ,trkrinfo,iret) +c +c ABSTRACT: This routine performs a single-pass barnes anaylsis +c of fxy at the point (flon,flat). The e-folding radius (km) +c and influence radius (km) are re and ri, respectively. +c +c NOTE: The input grid that is searched in this subroutine is most +c likely NOT the model's full, original grid. Instead, a smaller +c subgrid of the original grid is searched. The upper left and +c lower right grid point indices are passed into this subroutine +c (iibeg, jjbeg, iiend, jjend) for this subgrid. These indices are +c determined in the subroutine get_ij_bounds, and the purpose of +c doing it this way is to limit the number of points for which the +c subroutine has to calculate distances (for a global 1 deg grid, +c the number of loop iterations is reduced from 65160 to somewhere +c around 600). +c +c NOTE: This subroutine will immediately exit with a non-zero +c return code if it tries to access a grid point that does not have +c valid data. This would happen in the case of a regional grid, if +c you try to access a point near the edge of the grid (remember that +c because of the interpolation for the regional grids, there will be +c areas around the edges that have no valid data). +c +c INPUT: +c flon Lon value for center point about which barnes anl is done +c flat Lat value for center point about which barnes anl is done +c rlon Array of lon values for each grid point +c rlat Array of lat values for each grid point +c iimax Max number of pts in x-direction on input grid +c jjmax Max number of pts in y-direction on input grid +c iibeg i index for grid point to start barnes anlysis (upp left) +c jjbeg j index for grid point to start barnes anlysis (upp left) +c iiend i index for last grid point in barnes anlysis (low right) +c jjend j index for last grid point in barnes anlysis (low right) +c fxy Real array of data on which to perform barnes analysis +c defined_pt Logical; bitmap array used for regional grids +c bskip integer to indicate number of grid points to skip during +c a barnes loop, in order to speed processing +c re input e-folding radius for barnes analysis +c ri input influence radius for searching for min/max +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, in +c this barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c favg Average value about the point (flon,flat) +c iret Return code from this subroutine +c + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real fxy(iimax,jjmax), rlon(iimax), rlat(jjmax) + real degrees + integer bskip + logical(1) defined_pt(iimax,jjmax) + character(*) ctype + +c -------------------------- + + res = re*re + wts = 0.0 + favg = 0.0 + + icount = 0 + + do jix=jjbeg,jjend,bskip + do iix=iibeg,iiend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > iimax) then + if (trkrinfo%gridtype == 'global') then + i = iix - iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i,' imax= ',iimax + print *,' ' + endif + + stop 97 + endif + endif + + icount = icount + 1 + + call calcdist(flon,flat,rlon(i),rlat(j),dist,degrees) + + if (dist .gt. ri) cycle + + if (defined_pt(i,j)) then + if (fxy(i,j) >-999.01 .and. fxy(i,j) <-998.99) then + ! This is a patch. Even though this (i,j) is a valid + ! point, its zeta value has been set to -999 because a + ! neighboring point in subroutine rvcal was found + ! to be out of the grid boundaries. + cycle + endif + wt = exp(-1.0*dist*dist/res) + wts = wts + wt + favg = favg + wt*fxy(i,j) + else + if (ctype == 'vitals') then + continue + else +carw print *,' ' +carw print *,'!!! UNDEFINED PT OUTSIDE OF GRID IN BARNES....' +carw print *,'!!! i= ',i,' j= ',j +carw print *,'!!! flon= ',flon,' flat= ',flat +carw print *,'!!! rlon= ',rlon(i),' rlat= ',rlat(j) +carw print *,'!!! re= ',re,' ri= ',ri +carw print *,'!!! EXITING BARNES....' +carw print *,' ' +carw iret = 95 +carw return + endif + endif + + enddo + enddo + + if (wts > 1.0E-5) then + favg = favg/wts + else + favg = 0.0 + endif + iret = 0 +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,rglatmax,rglatmin,rglonmax,rglonmin,geslon,geslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) +c +c ----------------------------------------------------------- +c ABSTRACT: This subroutine figures out, based on ri, dx and dy and +c the guess latitude and longitude positions, the farthest reaching +c grid points that are searchable by an analysis subroutine. The +c purpose is to return indices for a subgrid that is much smaller +c than the original, full grid. This smaller subgrid can then be +c passed to a subsequent analysis or interpolation subroutine, and +c work can be done on this smaller array. This can help save time, +c especially in the barnes analysis subroutine, as work will only +c be done on, say, a 20 x 20 array (400 pts) instead of on a +c 360 x 181 array (65160 pts). It's crucial, however, to make sure +c that the ibeg, jbeg, iend and jend are extended far enough out to +c fully encompass any search that would be done. Below is a +c diagram showing the different grids.... +c +c Full Global or Regional Model Grid (Grid F) -----------> +c ---------------------------------------------------------------- +c | | (ibeg,jbeg) | +c | | x = ij position that the | (Grid R) | +c | | geslat/geslon is fixed to. ._______________. | +c | | | | | +c | | Even though only the points | (Grid B) | | +c | | within Grid B will be checked | . . . . k | | +c v | later on for a max/min (in the | . . . . . | | +c | case of a subsequent call to | . . x . e | | +c | find_maxmin), the barnes anal- | . . . . . | | +c | ysis will include all pts sur- | . . . . . | | +c | rounding these Grid B points | | | +c | that are within a radius of ri. ._______________. | +c | So in the case of pt. k, that ri | +c | radius may extend all the way to the Grid R | | +c | boundary, thus we need to include those (iend,jend) | +c | points within our ibeg-jbeg-iend-jend bounds. | +c | | +c ---------------------------------------------------------------- +c +c Remember that the grids we deal with start north and increase +c south, so the northernmost latitude on the input grid will have +c a j index of 1. +c +c INPUT: +c npts Num pts from x to edge of max/min search grid (Grid B) +c (i.e., You define the size of Grid B by the value of +c npts that you pass into this subroutine). +c nhalf Number of times the grid spacing will be halved +c ri Radius of influence (for use in barnes analysis) +c imax Number of points in x-direction on original grid +c jmax Number of points in y-direction on original grid +c dx Input grid spacing in i-direction on original grid +c dy Input grid spacing in j-direction on original grid +c rglatmax Value of northern-most latitude on original grid +c rglatmin Value of southern-most latitude on original grid +c rglonmax Value of eastern-most longitude on original grid +c rglonmin Value of western-most longitude on original grid +c geslat Value of latitude of guess position of storm +c geslon Value of longitude of guess position of storm +c +c OUTPUT: +c ilonfix i index on full, input grid that storm is fixed to +c jlatfix j index on full, input grid that storm is fixed to +c ibeg i index for top left of sub-array (Grid R) of input grid +c iend i index for bot right of sub-array (Grid R) of input grid +c jbeg j index for top left of sub-array (Grid R) of input grid +c jend j index for bot right of sub-array (Grid R) of input grid +c igiret Return code from this subroutine +c + USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + real tmpangle +c + igiret = 0 +c +c -------------------------------------- +c GET BEGINNING AND ENDING J POINTS.... +c +c (1) Calculate number of searchable, max/min pts, that is, the pts +c from x to the edge of Grid B. +c (2) Calculate number of pts beyond the last search point in Grid +c B, but are within the bounds of Grid R and thus can be +c included in the barnes analysis. +c (3) Add (1) and (2) to get the max number of pts to subtract/add +c to x to get jbeg and jend. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Beginning of get_ij_bounds...' + print *,' geslat= ',geslat,' geslon= ',geslon + print *,' ' + endif + + +c If nhalf > 0: This occurs in the case of a call from fmax, when +c the grid spacing is halved nhalf times. In this case, we have to +c do extra work to figure out the maximum possible grid point. For +c this case: +c jhlatpts = # of grid pts to last possible search pt (from x to +c edge of Grid B in above diagram), plus a cushion. +c jripts = # of searchable grid points within radius ri of last +c possible search pt (num pts between edge of Grid B +c and edge of Grid R in above diagram), plus a cushion +c jbmaxlatpts = # of pts (in j direction) from x to the edge of +c Grid R to include in this subgrid. +c +c If nhalf = 0: In this case, the grid spacing will not be reduced, +c so the number of pts in j direction from x to the edge of Grid +c B will be the input parameter npts, and just multiply it by 2, +c and add 2 for a cushion to get jmaxlatpts. Typically, this sub +c is called from find_maxmin, and in that sub, the first time that +c this sub is called, nhalf will = 0. Then, after a first-shot +c center is found, the grid spacing will be cut in order to rerun +c barnes on a smaller grid, and that's when nhalf will be sent +c here as 3. +c + if (nhalf > 0) then + rdeg = 0.0 + do i = 1,nhalf + rdeg = rdeg + float(npts) * (1./(float(i)*2)) * (dx+dy)/2 + enddo + jhlatpts = ceiling(rdeg/dy) + 1 + jripts = ceiling((ri + 1.)/(dtk*dx)) + 1 + jbmaxlatpts = jhlatpts + jripts + else + jbmaxlatpts = npts * 2 + 2 + endif +c +c +c Roughly fix geslat to the grid point just poleward of geslat. +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' +++ Near top of get_ij_bounds, ' + print *,' +++ geslat= ',geslat,' geslon= ',geslon + print *,' +++ rglatmax= ',rglatmax,' rglatmin= ',rglatmin + print *,' +++ rglonmax= ',rglonmax,' rglonmin= ',rglonmin + print *,' +++ imax= ',imax,' jmax= ',jmax + print *,' +++ dx= ',dx,' dy= ',dy,' nhalf= ',nhalf + print *,' +++ npts= ',npts + if(nhalf>0) then + print *,' +++ jhlatpts= ',jhlatpts,' jripts= ',jripts + else + print *,' +++ nhalf<=0 so jhlatpts and jripts unused' + endif + print *,' +++ jbmaxlatpts= ',jbmaxlatpts + endif + + if (geslat >= 0.0) then + jlatfix = int((rglatmax - geslat)/dy + 1.) + else + jlatfix = ceiling((rglatmax - geslat)/dy + 1.) + endif + + if ( verb .ge. 3 ) then + print *,' +++ jlatfix= ',jlatfix + endif + + jbeg = jlatfix - jbmaxlatpts + jend = jlatfix + jbmaxlatpts + if (jbeg > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jbeg > jmax' + print *,'!!! jbeg = ',jbeg,' jmax= ',jmax + endif + + igiret = igiret + 1 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jend < 1, jend = ',jend + endif + + igiret = igiret + 1 + return + endif + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' +++ jbeg= ',jbeg,' jend= ',jend + endif + + ! If using a global grid, avoid using the pole points, or else + ! you'll get a cosfac = 0 and then a divide by zero!!! + + if (jend == jmax .and. rglatmin == -90.0) then + jend = jmax - 2 + endif + if (jbeg == 1 .and. rglatmax == 90.0) then + jbeg = 3 + endif + +c ----------------------------------------- +c NOW GET BEGINNING AND ENDING I POINTS.... +c +c Using the map factor (cos lat), figure out, based on ri, the +c max distance beyond the last search point in x-direction (in +c degrees) that could be searched at this guess latitude (geslat) +c (i.e., in the diagram above, the max num pts from pt. e eastward +c to the edge of Grid R). Calculate how many grid points that is, +c add 2 to it for a cushion, & add the number of points (npts) +c within the defined search grid (Grid B) to get ibmaxlonpts. +c +c April, 2007: A min statement was put on the calculation to +c derive dlon, since with that cosine in there, the values of +c of dlon could get pretty ridiculous as you approach the poles. +c Also, the cosine factor (cosfac) used to be computed at the +c most poleward latitude possible given the jend here. For +c similar concerns with cosines near the poles, I've scrapped +c this to instead compute the cosine factor at the input +c guess latitude. - tpm + + cosfac = cos (geslat * dtr) + tmpangle = cosfac * dtk + dlon = min((ri /tmpangle ),20.0) +c dlon = min((ri / (cosfac * dtk)),20.0) +c + if (nhalf > 0) then + ihlonpts = ceiling(rdeg/dx) + 1 + ibmaxlonpts = ihlonpts + ceiling(dlon/dx) + 2 + else + ibmaxlonpts = npts + ceiling(dlon/dx) + 2 + endif + + if ( verb .ge. 3 ) then + if(nhalf>0) then + print *,' +++ rdeg= ',rdeg,' ri= ',ri,' cosfac= ',cosfac + print *,' +++ dtr= ',dtr,' dtk= ',dtk,' dlon= ',dlon + else + print*,' +++ nhalf<=0 so rdeg,ri,cosfac,dtr,dtk,dlon unused' + endif + print *,' +++ ibmaxlonpts= ',ibmaxlonpts,' dx= ',dx,' dy= ',dy + endif + +c Roughly fix geslon to the grid point just EASTward of geslon. + + ilonfix = int((geslon - rglonmin)/dx + 2.) + + ibeg = ilonfix - ibmaxlonpts + iend = ilonfix + ibmaxlonpts + + if ( verb .ge. 3 ) then + print *,' +++ (orig) ilonfix= ',ilonfix + print *,' +++ (orig) ibeg= ',ibeg,' iend= ',iend + print *,' +++ ' + endif + + if (ibeg > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 1 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, ibeg > imax' + print *,'!!! for a non-global grid' + print *,'!!! ibeg = ',ibeg,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + ! For a regional grid, just set iend to be imax + iend = imax + endif + endif + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + ! For a regional grid, just set ibeg to be 1 + ibeg = 1 + endif + endif + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + + if ( verb .ge. 3 ) then + print *,'!!! ERROR in get_ij_bounds, iend < 1' + print *,'!!! for a non-global grid' + print *,'!!! iend = ',iend,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine check_bounds (guesslon,guesslat,ist,ifh,trkrinfo + & ,icbret) +c +c ABSTRACT: This subroutine checks to make sure that the requested +c storm is in fact within the model's grid boundaries; +c this is only a concern for the regional models. +c + USE def_vitals; USE grid_bounds; USE set_max_parms + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + if (trkrinfo%gridtype == 'regional') then + if (guesslon > glonmax .or. guesslon < glonmin .or. + & guesslat > glatmax .or. guesslat < glatmin) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is outside of grid' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + goto 125 + else + icbret = 0 + endif + endif + + ! We have encountered problems with global grids where we + ! continue tracking almost the whole way to the pole. While + ! that's nice to do that, it creates problems for array + ! indices, especially in subroutine getradii. So we will cut + ! our losses and eliminate tracking of storms within + ! 5 degrees of the pole for global grids. + + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'global')then + if (guesslat > 85.0 .or. guesslat < -85.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is too close to the' + print *,'!!! N or S Pole for global tracking.' + print *,'!!! STOPPING TRACKING FOR THIS STORM DUE TO POLE' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + else + icbret = 0 + endif + endif + + 125 continue +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calcdist(rlonb,rlatb,rlonc,rlatc,xdist,degrees) +c +c ABSTRACT: This subroutine computes the distance between two +c lat/lon points by using spherical coordinates to +c calculate the great circle distance between the points. +c Figure out the angle (a) between pt.B and pt.C, +c N. Pole then figure out how much of a % of a great +c x circle distance that angle represents. +c / \ +c b/ \ cos(a) = (cos b)(cos c) + (sin b)(sin c)(cos A) +c / \ +c pt./<--A-->\c NOTE: The latitude arguments passed to the +c B / \ subr are the actual lat vals, but in +c \ the calculation we use 90-lat. +c a \ +c \pt. NOTE: You may get strange results if you: +c C (1) use positive values for SH lats AND +c you try computing distances across the +c equator, or (2) use lon values of 0 to +c -180 for WH lons AND you try computing +c distances across the 180E meridian. +c +c NOTE: In the diagram above, (a) is the angle between pt. B and +c pt. C (with pt. x as the vertex), and (A) is the difference in +c longitude (in degrees, absolute value) between pt. B and pt. C. +c +c !!! NOTE !!! -- THE PARAMETER ecircum IS DEFINED (AS OF THE +c ORIGINAL WRITING OF THIS SYSTEM) IN KM, NOT M, SO BE AWARE THAT +c THE DISTANCE RETURNED FROM THIS SUBROUTINE IS ALSO IN KM. +c + USE trig_vals + + real degrees +c + if (rlatb < 0.0 .or. rlatc < 0.0) then + pole = -90. + else + pole = 90. + endif +c + distlatb = (pole - rlatb) * dtr + distlatc = (pole - rlatc) * dtr + difflon = abs( (rlonb - rlonc)*dtr ) +c + cosanga = ( cos(distlatb) * cos(distlatc) + + & sin(distlatb) * sin(distlatc) * cos(difflon)) + +c This next check of cosanga is needed since I have had ACOS crash +c when calculating the distance between 2 identical points (should +c = 0), but the input for ACOS was just slightly over 1 +c (e.g., 1.00000000007), due to (I'm guessing) rounding errors. + + if (cosanga > 1.0) then + cosanga = 1.0 + endif +cPENG added bug fixed on 04/28/2016-------------------------- + if (cosanga < -1.0) then + cosanga = -1.0 + endif +cPENG added on 04/28/2016-------------------------- + + degrees = acos(cosanga) / dtr + circ_fract = degrees / 360. + xdist = circ_fract * ecircum +c +c NOTE: whether this subroutine returns the value of the distance +c in km or m depends on the scale of the parameter ecircum. +c At the original writing of this subroutine (7/97), ecircum +c was given in km. +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine subtract_cor (imax,jmax,dy,level) +c +c ABSTRACT: This subroutine subtracts out the coriolis parameter +c from the vorticity values. It is needed because at the original +c writing of this system, all of the forecast centers who included +c vorticity included only absolute vorticity. +c + USE tracked_parms; USE trig_vals; USE grid_bounds + + implicit none + + integer :: i,j,imax,jmax,level + real :: dy,coriolis,rlat +c + do j=1,jmax + rlat = glatmax - ((j-1) * dy) + coriolis = 2. * omega * sin(rlat*dtr) + do i=1,imax + zeta(i,j,level) = zeta(i,j,level) - coriolis + enddo + enddo +c + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_grib_file_name (ifh,gfilename,ifilename) + +c ABSTRACT: This subroutine uses various input regarding the model +c and forecast hour and generates the name of the input grib file +c for this particular forecast hour. Remember that the lead time +c is in minutes and that 5 spaces must be reserved for the lead +c time (e.g., f00360). File name should be something that looks +c like either, e.g., "gfdl.6thdeg.katrina12l.2005082818.f00720", +c or "gfdl.6thdeg.2005082818.f00720" (the part in there with the +c storm name & ID is optional). The grib index file name should +c be exactly the same as the grib data file itself, but with the +c character string ".ix" added onto the end of the name. +c +c NOTE: Array iftotalmins is brought in via module tracked_parms. +c +C INPUT: +c ifh integer array index for current lead time +c +c OUTPUT: +c gfilename GRIB file name +c ifilename GRIB index file name + + USE gfilename_info; USE tracked_parms; USE atcf + USE verbose_output + + implicit none + + character(*) gfilename,ifilename + character cfmin*5,cymdh*10 + integer ifh,nlen1,nlen2,nlen3,nlen4,nlen5 + +c Convert integer minutes to 5-position character, with +c leading zeroes, and convert 10-digit integer date into +c 10-position character. Then trim the various input variables +c and combine all into the file name. + + write (cfmin,'(i5.5)') iftotalmins(ifh) + write (cymdh,'(i10.10)') atcfymdh + + nlen1 = len_trim(gmodname) + gfilename = trim(gmodname(1:nlen1)) + + nlen2 = len_trim(rundescr) + + gfilename = trim(gfilename(1:nlen1))//'.'//trim(rundescr(1:nlen2)) + + nlen3 = len_trim(atcfdescr) + nlen4 = len_trim(gfilename) + +c If an extension to the name with the ATCF or storm name descriptor +c was included, then add it to the name now. Otherwise, just add +c the starting date and the lead time in minutes. + + if (nlen3 > 0) then + gfilename = trim(gfilename(1:nlen4))//'.' + & //trim(atcfdescr(1:nlen3))//'.'//cymdh//'.f'//cfmin + else + gfilename = trim(gfilename(1:nlen4))//'.'//cymdh//'.f'//cfmin + endif + +c Create the name for the grib index file, which is just the name of +c the grib file, with "ix" added to the end of it. + + nlen5 = len_trim(gfilename) + ifilename = trim(gfilename(1:nlen5))//'.ix' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,72) 'gfilename',gfilename + write (6,72) 'ifilename',ifilename + endif + + 72 format (1x,'In get_grib_file_name, file name for ',a9 + & ,' is ',a120) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) +c +c ABSTRACT: This subroutine reads the input GRIB file for the +c tracked parameters. It then calls subroutines to convert the +c data from a 1-d array into a 2-d array if the read was successful. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature (I jerry-rigged this by storing +c the data as being at the 401 mb level.) +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +cPENG----2018-06-07 ------------------------ +c 18. Ushear 200-850hPa (501hPa level) +c 19. 500hPa relative humidity +c +c INPUT: +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c inp of a derived type, contains user-input info +c lugb integer unit number of input grib file +c lugi integer unit number of input grib index file +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE verbose_output; USE params; USE grib_mod; USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + type (datecard) inp + type (gribfield) :: gfld,prevfld,holdgfld +c + integer, parameter :: jf=40000000 +c integer, parameter :: nreadparms=17 +cPENG----2018-06-07 ------------------------ + integer, parameter :: nreadparms=19 + + real, allocatable :: f(:) + real :: dmin,dmax,firstval,lastval + logical(1), allocatable :: lb(:) + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1) file_open + logical :: unpack=.true. + logical :: open_grb=.false. + character*1 :: lbrdflag + character*8 :: chparm(nreadparms) + CHARACTER(len=8) :: pabbrev + character (len=10) big_ben(3) + integer date_time(8) + integer,dimension(200) :: jids,jpdt,jgdt + integer :: listsec1(13), enable_timing + integer, intent(in) :: imax,jmax + integer igparm(nreadparms),iglev(nreadparms) + integer iglevtyp(nreadparms) + integer ig2_parm_cat(nreadparms),ig2_parm_num(nreadparms) +cPENG----2018-06-07 ------------------------ + integer ig2_parm_cat_cmcd(nreadparms) + & ,ig2_parm_num_cmcd(nreadparms) + +c integer ig2_lev_val(nreadparms),ig2_lev_typ(nreadparms) +cPENG----2018-06-07 ------------------------ + integer ig2_lev_10(nreadparms) + integer ig2_lev_11(nreadparms),ig2_lev_12(nreadparms) + +cPENG 04/18/2018 for CMC Det. and CMC ensemble data +cPENG----2018-06-07 ------------------------ +c integer ig2_lev_11_cmc(nreadparms),ig2_lev_val_cmc(nreadparms) +c integer ig2_lev_11_cmcd(nreadparms),ig2_lev_val_cmcd(nreadparms) + integer ig2_lev_11_cmc(nreadparms),ig2_lev_12_cmc(nreadparms) + integer ig2_lev_11_cmcd(nreadparms),ig2_lev_12_cmcd(nreadparms) + + integer cpsig2_parm_cat(nlevs_cps),cpsig2_parm_num(nlevs_cps) +c integer cpsig2_lev_typ(nlevs_cps),cpsig2_lev_val(nlevs_cps) +cPENG-04/18/2018 for CMC Det. and CMC ensemble data + integer cpsig2_lev_10(nlevs_cps) + integer cpsig2_lev_11(nlevs_cps),cpsig2_lev_12(nlevs_cps) + + integer ec_igparm(nreadparms),ec_iglev(nreadparms) + integer ec_iglevtyp(nreadparms) + integer cpsgparm(nlevs_cps),cpsglev(nlevs_cps) + integer cpsglevtyp(nlevs_cps) + integer ec_cpsgparm(nlevs_cps) + integer jpds(200),jgds(200),kpds(200),kgds(200) + integer igvret,ifa,ila,ip,ifh,i,j,k,kj,iret,kf,lugb,lugi + integer jskp,jdisc,np + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer pdt_4p0_vert_level,pdt_4p0_vtime + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 +c + lbrdflag = 'n' + enable_timing=trkrinfo%enable_timing +c The following data statements contain the parameters that will be +c used to search the grib files. The first 9 parameters will all be +c used to locate the storm position. The last 4 parameters (500 mb +c u- and v-components and 10 m u- and v- components) will not be +c used for tracking, but only for helping to estimate the next first +c guess position (500 mb winds) and for estimating the max near- +c surface wind speeds in the vicinity of the storm (10 m winds). +c +c ** NOTE: iglevtyp(12 & 13) and iglev(12 & 13) are initialized to +c 0 just to satisfy the IBM xlf compiler, which barks about +c there being too few initial values in the list when I +c only had 11 values there -- even though the real +c initialization for these variables is done just about +c 10 lines below. +c +c ** NOTE: The new ECMWF hi-res data uses the ECMWF GRIB parameter +c ID table, which has different values than the NCEP +c table. Therefore, we needed to add the variables and +c data values for ec_igparm, ec_iglevtyp and ec_iglev. +c +c July 2007: Read statements added for GP height for cyclone +c phase space (CPS) algorithm. + +c data igparm /41,41,33,34,33,34,7,7,1,33,34,33,34,11,7,7,81/ +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7,81/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 +c & ,100,100,100,1/ +c data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500,401 +c & ,500,200,0/ +cPENG----2018-06-07 ------------------------ + data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7,81 + & ,33,52/ + data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 + & ,100,100,100,1,100,100/ + data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500,401 + & ,500,200,0,501,500/ + + data cpsgparm /13*7/ + data ec_cpsgparm /13*156/ + data cpsglevtyp /13*100/ + data cpsglev /900,850,800,750,700,650,600,550,500,450,400 + & ,350,300/ + +c data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 +c & ,131,132,130,156,156,999/ +c data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 +c & ,100,100,100,999/ +c data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 +c & ,401,500,200,999/ +c +c data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' +c & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' +c & ,'vgrid','temp','gphgt','gphgt','lmask'/ +cPENG----2018-06-07 ------------------------ + data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 + & ,131,132,130,156,156,999,131,157/ + data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 + & ,100,100,100,999,100,100/ + data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 + & ,401,500,200,999,501,500/ + + data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' + & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' + & ,'vgrid','temp','gphgt','gphgt','lmask' + & ,'ushe','rhum'/ + +c data ig2_parm_cat /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2/ +c data ig2_parm_num /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8/ +c data ig2_lev_typ /100,100,100,100,100,100,100,100,101,103,103 +c & ,100,100,100,100,100,-9999/ +c data ig2_lev_val /850,700,850,850,700,700,850,700,0,10,10,500,500 +c & ,401,500,200,-9999/ +cPENG----2018-06-07 ------------------------ + data ig2_parm_cat /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2,2,1/ + data ig2_parm_num /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8,2,1/ + data ig2_parm_cat_cmcd /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2,2,1/ + data ig2_parm_num_cmcd /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8,2,1/ + + data ig2_lev_10 /100,100,100,100,100,100,100,100,101,103,103 + & ,100,100,100,100,100,-9999,100,100/ + +cPENG 04/18/2018 for CMC Det. and CMC ensemble data +c data ig2_lev_11_cmc /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 +c & ,-4,-4,0/ +c data ig2_lev_val_cmc/85,7,85,85,7,7,85,7,0,10,10,5,5,40100 +c & ,5,2,-9999/ +c data ig2_lev_11_cmcd /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 +c & ,-4,-4,0/ +c data ig2_lev_val_cmcd /85,7,85,85,7,7,85,7,0,10,10,5,5,40100 +c & ,5,2,-9999/ +cPENG----2018-06-07 ------------------------ +cPENG---for gfs/gefs NEVGEM/FENS ensemble---------- + data ig2_lev_11 /0,0,0,0,0,0,0,0,0,0,0 + & ,0,0,0,0,0,0,0,0/ + data ig2_lev_12 /85000,70000,85000,85000,70000,70000,85000,70000 + & ,0,10,10,50000,50000 + & ,40100,50000,20000,-9999,50100,50000/ +cPENG---for CMC ensemble--------------------- + data ig2_lev_11_cmc /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0,0,-4/ + data ig2_lev_12_cmc/85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999,50100,5/ +cPENG---for CMC deterministic--------- + data ig2_lev_11_cmcd /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0,0,0/ + data ig2_lev_12_cmcd /85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999,50100,50000/ + + + data cpsig2_parm_cat /13*3/ + data cpsig2_parm_num /13*5/ +c data cpsig2_lev_typ /13*100/ +c data cpsig2_lev_val /900,850,800,750,700,650,600,550,500,450,400 +c & ,350,300/ +cPENG---------------------------------- + data cpsig2_lev_10 /13*100/ + data cpsig2_lev_11 /13*0/ + data cpsig2_lev_12 /90000,85000,80000,75000,70000,65000 + & ,60000,55000,50000,45000,40000 + & ,35000,30000/ + +c Model numbers used: (1) AVN, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) Early Eta, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble, +c (13) SREF Ensemble, +c (14) NCEP Ensemble (from ensstat mean fields), +c (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) NCEP Ensemble RELOCATION +c (21) UKMET hi-res (from NHC) +c (23) FNMOC Ensemble +c (24) HWRF Basin-scale + + if (trkrinfo%gribver == 2) then + +c For GRIB2, we will check to see if the MSLP being searched for +c is the standard MSLP (MSLP parm ID = 1) or if it is the +c so-called "Eta" or "Membrane" MSLP reduction that is included +c in the output for some models (like GFS and GDAS). Note that +c for 10m winds, with GRIB2, so far with all of the GRIB2 model +c data we've seen to this point, they all have the same IDs for +c 10m winds for all models, so no need to break out by model +c like we do for GRIB v1 in the else portion of this if statement. + + ig2_parm_num(9) = trkrinfo%g2_mslp_parm_id ! 1 = standard MSLP + ! reduction, 192 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB2 read, MSLP ID = ig2_parm_num(9) = ' + & ,ig2_parm_num(9) + + endif + + else + +c For GRIB1, do the same check as done just above in the IF part +c of this IF statement, but note that we need to also check to +c see what the GRIB1 parm IDs are for the sfc wind level type +c and value. Most models list the level type as 105 (which means +c height above the ground) and then a level value of 10. But +c ECMWF and UKMET use a level type of 1 (which means ground or +c water surface) and a level value of 0. + + igparm(9) = trkrinfo%g1_mslp_parm_id ! 102 = standard MSLP + ! reduction, 130 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglev(10) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + iglev(11) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + + ec_iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglev(10) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + ec_iglev(11) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB1 read, MSLP ID = igparm(9) = ' + & ,igparm(9) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev type = ' + & ,iglevtyp(10) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev value = ' + & ,iglev(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev type = ' + & ,ec_iglevtyp(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev value = ' + & ,ec_iglev(10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata. A return' + print *,'code (iret) not equal to zero indicates that ' + print *,'subroutine getgb was unable to find the requested ' + print *,'parameter. This could be simply because the parm is ' + print *,'not included in the grib file (this is likely for ' + print *,'ECMWF data, as they limit what they send us), or it ' + print *,'could indicate a problem with the grib index file.' + endif + + + if (allocated(f)) deallocate(f) + if (allocated(lb)) deallocate(lb) + allocate (f(imax*jmax),stat=ifa) + allocate (lb(imax*jmax),stat=ila) + if (ifa /= 0 .or. ila /= 0) then + print *,' ' + print *,'!!! ERROR in getdata allocating f or lb array.' + print *,'!!! ifa = ',ifa,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + if (trkrinfo%gribver == 2) then + + ! Reading from a GRIB v2 file.... + + grib2_standard_parm_read_loop: do ip = 1,nreadparms + + if (ip == 17) then + if (trkrinfo%use_land_mask == 'y' .or. + & trkrinfo%use_land_mask == 'Y') then + continue + else + if (verb .ge. 3) then + print *,' ' + print *,'The use_land_mask flag has not been set to ' + print *,'y or Y, so we will not try to read it in... ' + print *,' ' + cycle grib2_standard_parm_read_loop + endif + endif + endif + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + if (ip == 17) then + jdisc=1 ! hydrological products. At this point, used only + ! for the land-sea mask. + else + jdisc=0 ! meteorological products + endif + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for input parameter by production template 4.0. This +c tave program is used primarily for temperature, but still we +c will leave that as a variable and not-hard wire it in case we +c choose to average something else in the future. + + ! We are looking for Temperature or GP Height here. This + ! block of code, or even the smaller subset block of code that + ! contains the JPDT(1) and JPDT(2) assignments, can of course + ! be modified if this program is to be used for interpolating + ! other variables.... + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = ig2_parm_cat(ip) + JPDT(2) = ig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + +c JPDT(10) = ig2_lev_typ(ip) +cPENG----2018-06-07 ------------------------ + JPDT(10) = ig2_lev_10(ip) + +cPENG 04/18/2018 CMC Det. and CMC ensemble data +c if (inp%model == 16 ) then +c JPDT(11) = ig2_lev_11_cmc(ip) +c JPDT(12) = ig2_lev_val_cmc(ip) +c elseif (inp%model == 15 ) then +c JPDT(11) = ig2_lev_11_cmcd(ip) +c JPDT(12) = ig2_lev_val_cmcd(ip) +c else +c JPDT(11) = 0 +c if (JPDT(10) == 100) then ! isobaric surface +c JPDT(12) = ig2_lev_val(ip) * 100 ! GRIB2 levels are in Pa +c else +c JPDT(12) = ig2_lev_val(ip) ! This is going to be either mslp +c & ! or 10m winds. +c endif +c endif +cPENG----2018-06-07 ------------------------ + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 8 ) then + JPDT(11) = ig2_lev_11(ip) + JPDT(12) = ig2_lev_12(ip) + else if(inp%model == 16) then + JPDT(11) = ig2_lev_11_cmc(ip) + JPDT(12) = ig2_lev_12_cmc(ip) + else if(inp%model == 15) then + JPDT(11) = ig2_lev_11_cmcd(ip) + JPDT(12) = ig2_lev_12_cmcd(ip) + endif + + if ( verb_g2 .ge. 1 ) then + print *,'before getgb2 call, value of unpack = ',unpack + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is CLOSED' + endif + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is CLOSED' + endif + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,531) date_time(5),date_time(6),date_time(7) + 531 format (1x,'TIMING: before getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,532) date_time(5),date_time(6),date_time(7) + 532 format (1x,'TIMING: after getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call, value of unpacked = ' + & ,gfld%unpacked + print *,'after getgb2 call, gfld%ngrdpts = ',gfld%ngrdpts + print *,'after getgb2 call, gfld%ibmap = ',gfld%ibmap + endif + + if ( iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb2 found parm: ',chparm(ip) +c print *,'+++ at level = ',ig2_lev_val(ip) +cPENG---2018-06-07------------------------------------------- + print *,'+++ at level = ',ig2_lev_12(ip) + + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + select case (chparm(ip)) + case ('absv') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpdt(12) == 20000 .or. jpdt(12) == 2) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) +cPENG----2018-06-07 ------------------------ + case ('ushe') + call conv1d2d_real (imax,jmax,f,ushear + & ,need_to_flip_lats) + case ('rhum') + call conv1d2d_real (imax,jmax,f,rhumid + & ,need_to_flip_lats) + + + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb2 could not find parm: ' + & ,chparm(ip) +c print *,'!!! at level = ',ig2_lev_val(ip) +cPENG---2018-06-07------------------------------------------- + print *,'+++ at level = ',ig2_lev_12(ip) + + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib2_standard_parm_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c This is the GRIB2 reading section. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + grib2_cps_parm_lev_loop: do ip = 1,nlevs_cps + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + + jpds = -1 + jgds = -1 + j=0 + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = cpsig2_parm_cat(ip) + JPDT(2) = cpsig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + +c JPDT(10) = cpsig2_lev_typ(ip) + JPDT(10) = cpsig2_lev_10(ip) +cPENG-------------------------------------------- + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + JPDT(11) = cpsig2_lev_11(ip) + JPDT(12) = cpsig2_lev_12(ip) + endif + +c if (JPDT(10) == 100) then ! isobaric surface +c JPDT(12) = cpsig2_lev_val(ip) * 100 ! GRIB2 levels +c ! are in Pa +c else +c if (verb .ge. 3) then +c print *,' ' +c print *,'ERROR in getdata: JPDT(10) array value' +c print *,'should only be 100 in this CPS section' +c print *,'for GRIB2 data.' +c endif +c endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,731) date_time(5),date_time(6),date_time(7) + 731 format (1x,'TIMING: before getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn + & ,jgdt,unpack,krec,gfld,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,732) date_time(5),date_time(6),date_time(7) + 732 format (1x,'TIMING: after getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 (PHASE) in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call(PHASE),' + & ,' value of unpacked = ',gfld%unpacked + print *,'after getgb2 (PHASE) call, gfld%ngrdpts = ' + & ,gfld%ngrdpts + print *,'after getgb2 (PHASE) call, gfld%ibmap = ' + & ,gfld%ibmap + endif + + if (verb .ge. 3) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + +c Determine packing information from GRIB2 file. +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) + & then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ' + & ,gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ' + & ,gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned + ! from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do +c this once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) + & then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.' + & ,gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ' + & ,gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ' + & ,gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.' + & ,gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline + & ,gfld%ipdtmpl(1),gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,231) + 231 format (' rec# param level byy bmm bdd ' + & ,'bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin + & ,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo grib2_cps_parm_lev_loop + + endif + + endif + + else + + !---------------------------------- + ! Reading from a GRIB v1 file.... + !---------------------------------- + + grib1_read_loop: do ip = 1,nreadparms + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then ! ECMWF hi-res data uses ECMWF table + print *,' ' + print *,'WARNING: From the namelist, inp%model is set to a' + print *,' value of 4, which is for ECMWF, so in routine' + print *,' getdata_grib, the input jpds(5,6,7) parms are' + print *,' going to have values that are specific for' + print *,' ECMWF GRIB1 data.' + print *,' ' + jpds(5) = ec_igparm(ip) + jpds(6) = ec_iglevtyp(ip) + jpds(7) = ec_iglev(ip) + else ! All other models use NCEP-standard GRIB table + jpds(5) = igparm(ip) + jpds(6) = iglevtyp(ip) + jpds(7) = iglev(ip) + endif + + print *,' ' + print *,' --- Before getgb, jpds(5)= ',jpds(5) + print *,' --- , jpds(6)= ',jpds(6) + print *,' --- , jpds(7)= ',jpds(7) + + if (jpds(5) == 999) then + cycle + endif + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,831) date_time(5),date_time(6),date_time(7) + 831 format (1x,'TIMING: before getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + + if (enable_timing /= 0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,832) date_time(5),date_time(6),date_time(7) + 832 format (1x,'TIMING: after getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb call, j= ',j,' k= ',k + & ,' iftotalmins= ' + & ,iftotalmins(ifh),' parm # (ip) = ',ip,' iret= ',iret + else + print *,'After getgb call, j= ',j,' k= ',k,' ifhours= ' + & ,ifhours(ifh),' parm # (ip) = ',ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb found parm: ',chparm(ip) + print *,'+++ at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,29) + else + write (6,31) + endif + 29 format (' rec# parm# levt lev byy bmm bdd bhh fmin' + & ,' npts minval maxval') + 31 format (' rec# parm# levt lev byy bmm bdd bhh fhr ' + & ,' npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + + select case (chparm(ip)) + case ('absv') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpds(7) == 200) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb could not find parm: ',chparm(ip) + print *,'!!! at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib1_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + cps_grib1_lev_loop: do ip = 1,nlevs_cps + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then + ! Use different grib parm id for ECMWF GP height + jpds(5) = ec_cpsgparm(ip) + else + jpds(5) = cpsgparm(ip) + endif + jpds(6) = cpsglevtyp(ip) + jpds(7) = cpsglev(ip) + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,841) date_time(5),date_time(6),date_time(7) + 841 format (1x,'TIMING: before getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,842) date_time(5),date_time(6),date_time(7) + 842 format (1x,'TIMING: after getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,39) + else + write (6,41) + endif + 39 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fmin npts minval maxval') + 41 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fhr npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo cps_grib1_lev_loop + + endif + + endif + + endif +c + deallocate (f) + deallocate (lb) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) +c +c ABSTRACT: This subroutine reads the input NetCDF file for the +c tracked parameters for one lead time. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +c +c If the user has requested to check the cyclone phase space for +c this run (phaseflag set to 'y' and phasescheme set to 'cps'), +c then we need to have gp height data for 900-300 mb at every 50 +c mb. Some of those levels for gp height data were already read +c in with the read of the initial 17 parameters, but we will be +c sure to read in the others, if requested. +c +c INPUT: +c ncfile_id integer ID associated with the NetCDF file +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file +c itself in subroutine read_netcdf_fhours. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE netcdf_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo +c integer, parameter :: nreadparms=17,nreadparms_cps=13 +cPENG---2018-06-07-------------------------------- + integer, parameter :: nreadparms=19,nreadparms_cps=13 + + real, allocatable :: f(:) + real :: dmin,dmax,xmissing_value,xfill_value + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + character*1 :: lbrdflag,match_check + character*30 :: chparm(nreadparms) + character*30 :: chparm_cps(nreadparms_cps) + integer, intent(in) :: ncfile_id,imax,jmax + integer :: igvret,ifa,ip,ifh,i,j,k,m,n,ncfile_tmax,nf_get_att_real + integer :: nf_get_att_double,nf_inq_attlen,imvlen,ifvlen + integer :: usertime,ncix,missing_val_length,nf_status + integer :: nf_inq_varid,varid +c + lbrdflag = 'n' + +cnc data cpsgparm /13*7/ +cnc data cpsglevtyp /13*100/ +cnc data cpsglev /900,850,800,750,700,650,600,550,500,450,400 +cnc & ,350,300/ + +c data chparm /'vort850','vort700' +c & ,'u850','v850','u700','v700' +c & ,'h850','h700','slp','u_ref','v_ref' +c & ,'u500','v500','tm'/ + +c Load the names of the NetCDF variables for the standard +c variables into the chparm array... + + chparm(1) = netcdfinfo%rv850name + chparm(2) = netcdfinfo%rv700name + chparm(3) = netcdfinfo%u850name + chparm(4) = netcdfinfo%v850name + chparm(5) = netcdfinfo%u700name + chparm(6) = netcdfinfo%v700name + chparm(7) = netcdfinfo%z850name + chparm(8) = netcdfinfo%z700name + chparm(9) = netcdfinfo%mslpname + chparm(10) = netcdfinfo%usfcname + chparm(11) = netcdfinfo%vsfcname + chparm(12) = netcdfinfo%u500name + chparm(13) = netcdfinfo%v500name + chparm(14) = netcdfinfo%tmean_300_500_name + chparm(15) = netcdfinfo%z500name + chparm(16) = netcdfinfo%z200name + chparm(17) = netcdfinfo%lmaskname + + + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata_netcdf.' + endif + + if (allocated(f)) deallocate(f) + allocate (f(imax*jmax),stat=ifa) + if (ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getdata_netcdf allocating f data array.' + print *,'!!! ifa = ',ifa + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + !--------------------------------------------------------------- + ! First go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match up + ! the lead times that were read in with the lead times that + ! we read in directly from the NetCDF file. Get the index from + ! the NetCDF file for that lead time and use that in the call to + ! the read routine (get_var3_tlev_double). + !--------------------------------------------------------------- + + usertime = iftotalmins(ifh) + + match_check = 'n' + + find_index_loop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + ncix = m + if (verb .ge. 1) then + print *,'+++ Time match in getdata_netcdf for usertime= ' + & ,usertime,' netcdf file index= ncix= ',ncix + endif + match_check = 'y' + exit find_index_loop + endif + + enddo find_index_loop + + if (match_check == 'n') then + print *,' ' + print *,'!!! ERROR in getdata_netcdf: ' + print *,' For a NetCDF file, the user has ' + print *,' requested to process a lead time, and that lead' + print *,' time does not exist in the NetCDF list of time' + print *,' values. ' + print *,' ifh= ',ifh + print *,' usertime= iftotalmins(ifh)= ',iftotalmins(ifh) + print *,' STOPPING....' + stop 99 + endif + + !--------------------------------------------------------------- + ! Now go through the read loop for the list of parameters + !--------------------------------------------------------------- + + netcdf_standard_parm_read_loop: do ip = 1,nreadparms + + if (chparm(ip) == 'X' .or. chparm(ip) == 'x') then + if (verb .ge. 3) then + print *,' ' + print *,'!!! NetCDF read NOT requested for parm # ',ip + endif + cycle netcdf_standard_parm_read_loop + else + if (verb .ge. 3) then + print *,' ' + print *,'+++ NetCDF read requested for parm # ',ip + & ,' ... parm= ',chparm(ip) + endif + endif + + ! Note that I am sending a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which we + ! want), depending on the model & grid, we may need to flip the + ! grid in the north-south direction. I already have a routine + ! for converting data from a 1-d to a 2-d array, and it has + ! the functionality for flipping a grid, so I programmed it as + ! getting a 1-d array from the netcdf read routine and send that + ! 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm(ip),imax,jmax,ncix + & ,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + + ! Need to get the value of the "missing_value" attribute for + ! this variable from the list of attributes in the NetCDF + ! file. Only do this for the first lead time, since the + ! value of the "missing_value" obviously will not change + ! with lead time. + +c nf_status = nf_inq_attlen (ncfile_id,varid,"missing_value" +c & ,imvlen) +c nf_status = nf_inq_attlen (ncfile_id,varid,"_FillValue" +c & ,ifvlen) + + ! These next two nf function calls retrieve the value of the + ! "missing_value" attribute from the list of attributes for + ! the given variable being read in. This is needed in order + ! to know if a non-valid point is being accessed, as for a + ! regional grid, like the nested fvGFS. In GRIB1/GRIB2 files, + ! such regions would be bitmapped out, but in a NetCDF file, + ! no such bitmap exists, so we have to check for missing + ! values. In case it's a moving grid, we need to do this + ! for every lead time, since the "map of missing values" + ! will shift with lead time. Once we have those missing + ! values, we can loop through them and fill the valid_pt + ! logical array so that, in the end, we will have the same + ! logical bitmap for masking out missing data that we have + ! with GRIB1/GRIB2 data. + + nf_status = nf_inq_varid (ncfile_id,chparm(ip),varid) + + print *,'nf_status from nf_inq_varid call = ',nf_status + + nf_status = nf_get_att_real (ncfile_id,varid,"missing_value" + & ,xmissing_value) + + print *,'nf_status from nf_get_att_real call = ',nf_status + +c nf_status = nf_get_att_real (ncfile_id,varid,"_FillValue" +c & ,xfill_value) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"missing_value",len=imvlen) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"_FillValue",len=ifvlen) +c +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + if (verb .ge. 3) then + write (6,31) + 31 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,33) ifhours(ifh),ifclockmins(ifh),ip,chparm(ip) + & ,dmin,dmax + 33 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,35) chparm(ip),xmissing_value + 35 format (' --- ',a30,' missing value = ',g12.4) + endif + + ! This call to conv1d2d_logic_netcdf creates + ! a logical bitmap, so that in case we have + ! regional (non-global) data and an irregular grid (e.g., + ! the FV3 nested grid), we can mask out grid points that + ! have missing values as their data values. There is not + ! actually a native logical bitmap in NetCDF, so we will + ! create one by examining the real data values and masking + ! out grid points that have missing values. + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic_netcdf (imax,jmax,f,valid_pt + & ,xmissing_value,need_to_flip_lats) + lbrdflag = 'y' + endif + + if (ip == 1) then ! 850 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else if (ip == 2) then ! 700 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + else if (ip == 3) then ! 850 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (ip == 4) then ! 850 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (ip == 5) then ! 700 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (ip == 6) then ! 700 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (ip == 7) then ! 850 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (ip == 8) then ! 700 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (ip == 9) then ! MSLP + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + else if (ip == 10) then ! Near-sfc (10m) u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + else if (ip == 11) then ! Near-sfc (10m) v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + else if (ip == 12) then ! 500 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else if (ip == 13) then ! 500 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else if (ip == 14) then ! 300-500 mb mean Temp + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + else if (ip == 15) then ! 500 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (ip == 16) then ! 200 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + else if (ip == 17) then ! Land-sea mask + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + else + + print *,'!!! NOTE: Parm not recognized. ' + print *,'!!! ip is > 17.... ip= ',ip + print *,'!!! Forecast time level = ',ifh + + endif + + endif + + enddo netcdf_standard_parm_read_loop + +c *--------------------------------------------------------------* +c If we are attempting to determine the cyclone structure using +c Hart's cyclone phase space, then read in data now that will +c allow us to do that. If we are instead just using the +c mid-level (300-500 mb) mean temperature to do that with a +c simple warm-core check, then that mean temperature field was +c already read in above in the read loop for the standard +c variables. The variables needed here for CPS are pretty +c straightforward: gp height every 50 mb from 300 to 900 mb. +c keep in mind that we have already read in a few of these +c gp height records for selected levels above. +c *--------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + chparm_cps(1) = netcdfinfo%z900name + chparm_cps(2) = netcdfinfo%z850name + chparm_cps(3) = netcdfinfo%z800name + chparm_cps(4) = netcdfinfo%z750name + chparm_cps(5) = netcdfinfo%z700name + chparm_cps(6) = netcdfinfo%z650name + chparm_cps(7) = netcdfinfo%z600name + chparm_cps(8) = netcdfinfo%z550name + chparm_cps(9) = netcdfinfo%z500name + chparm_cps(10) = netcdfinfo%z450name + chparm_cps(11) = netcdfinfo%z400name + chparm_cps(12) = netcdfinfo%z350name + chparm_cps(13) = netcdfinfo%z300name + + ! Read in GP Height levels for cyclone phase space... + + if (verb .ge. 3) then + print *,' ' + print *,'--- Reads for CPS parms follow...' + print *,' ' + endif + + netcdf_cps_parm_read_loop: do ip = 1,nreadparms_cps + + if (chparm_cps(ip) == 'X' .or. chparm_cps(ip) == 'x') then + if (verb .ge. 3) then + print *,'!!! ERROR: NetCDF read NOT requested for' + print *,'!!! CPS parm # ',ip + print *,'!!! You must have an error in your namelist.' + print *,'!!! You have requested to do cyclone phase' + print *,'!!! checking, so you need to include the ' + print *,'!!! NetCDF names for ALL requested gp height' + print *,'!!! variables from 900 to 300 mb, every 50 ' + print *,'!!! mb,in the namelist.' + print *,'!!! phaseflag is being set to NO (n), and ' + print *,'!!! phase-checking will NOT take place.' + print *,'!!! If you want to run again and just do ' + print *,'!!! phase-checking with a simple warm-core' + print *,'!!! check, then in the namelist set phaseflag' + print *,'!!! to y and set phasescheme to vtt.' + phaseflag = 'n' + endif + exit netcdf_cps_parm_read_loop + else + if (verb .ge. 3) then + print *,'+++ NetCDF read requested for cps parm # ',ip + & ,' ... parm= ',chparm_cps(ip) + endif + endif + + ! As above, we send a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which + ! we want), depending on the model & grid, we may need to + ! flip the grid in the north-south direction. I already + ! have a routine for converting data from a 1-d to a 2-d + ! array, and it has the functionality for flipping a grid, + ! so I programmed it as getting a 1-d array from the netcdf + ! read routine and send that 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm_cps(ip),imax + & ,jmax,ncix,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm_cps(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + nf_status = nf_inq_varid (ncfile_id,chparm_cps(ip),varid) + nf_status = nf_get_att_real (ncfile_id,varid + & ,"missing_value",xmissing_value) + + if (verb .ge. 3) then + write (6,231) + 231 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,233) ifhours(ifh),ifclockmins(ifh),ip + & ,chparm_cps(ip),dmin,dmax + 233 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,235) chparm_cps(ip),xmissing_value + 235 format (' --- ',a30,' missing value = ',g12.4) + endif + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo netcdf_cps_parm_read_loop + + endif + + endif + + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_ncdim1 (ncid,var1_name,nmax) +c +c ABSTRACT: This routine queries a netcdf file to get the +c value of a requested file dimension (e.g., imax, jmax) +c + implicit none + + include "netcdf.inc" + + integer, intent(in) :: ncid + character*(*), intent(in) :: var1_name + integer, intent(out) :: nmax + integer :: status, var1id + + status = nf_inq_dimid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + status = nf_inq_dimlen (ncid,var1id,nmax) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_ncdim1 +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_var1_double (ncid,var1_name,nmax,var1) +c +c ABSTRACT: This routine reads a netcdf file in order to return +c a 1-dimensional array of data. + + implicit none + + include "netcdf.inc" + + integer, intent(in):: ncid + character*(*), intent(in):: var1_name + integer, intent(in):: nmax + real, intent(out):: var1(nmax) + + integer :: status, var1id + + status = nf_inq_varid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) +! write(*,*) 'Got var1id', var1id + + status = nf_get_var_real (ncid,var1id,var1) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var1_double +c +c--------------------------------------------------------- +c +c--------------------------------------------------------- + subroutine get_var3_tlev_double (ncid,var3_name,imax,jmax,ncix + & ,var3,igvret) +c +c ABSTRACT: This routine reads a netcdf file and returns a +c 2-dimensional synoptic variable at a particular lead time. +c The lead time is specified by the ltix array, which is +c included in module tracked_parms and defined in subroutine +c read_fhours. +c +c PARAMETERS +c +c INPUT: +c ncid integer that contains the NetCDF file ID +c var3_name character name of NetCDF input file +c imax integer x-dimension of input data +c jmax integer y-dimension of input data +c ncix integer index of time level for where this time level +c actually is inside the NetCDF data. Do NOT confuse this +c with the index of where this forecast hour is in the +c user's list of input forecast hours, as they may be +c different. For example, the user may request times that +c are every 6 hours, but the NetCDF file might have times +c that are every hour, so the indices for those two arrays +c will be different. Be sure to use the one (ncix) that +c indicates where the data actually starts in the +c NetCDF file. +c +c OUTPUT: +c var3 real array with real values returned from NetCDF read +c igvret integer return code from this routine + + USE tracked_parms; USE verbose_output; USE netcdf_parms + + implicit none + + include "netcdf.inc" +c + integer, intent(in) :: ncid,ncix + character*(*), intent(in) :: var3_name + integer, intent(in) :: imax,jmax + real, intent(out) :: var3(imax,jmax) + integer :: istart(3),ilength(3) + integer :: status,var3id,igvret + + if (verb .ge. 3) then + print *,' ' + print *,'In get_var3_tlev_double, ncix= ',ncix + print *,' nctotalmins(ncix)= ',nctotalmins(ncix) + endif + + istart(1) = 1 + istart(2) = 1 + istart(3) = ncix + + ilength(1) = imax + ilength(2) = jmax + ilength(3) = 1 + + igvret = 0 + + status = nf_inq_varid (ncid,var3_name,var3id) + + if (status /= NF_NOERR) then + print *,' ' + print *,'NOTE: Could not find variable ',var3_name,' at time' + & ,' index ncix= ',ncix + & ,' nctotalmins(ncix)= ',nctotalmins(ncix) + + igvret = 92 + return + endif + + status = nf_get_vara_real (ncid,var3id,istart,ilength,var3) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var3_tlev_double +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine handle_netcdf_err (status) +c +c ABSTRACT: This subroutine is an error handling routine for NetCDF- +c related functions. + + implicit none + + include "netcdf.inc" + + integer status +c + if (status /= nf_noerr) then + print *,' ' + print *,'Tracker NetCDF error: ' + print *, nf_strerror(status) + stop 'Stopped' + endif + + end subroutine handle_netcdf_err +c +c------------------------------------------------------------------- +c +c------------------------------------------------------------------- + subroutine bitmapchk (n,ld,d,dmin,dmax) +c +c This subroutine checks the bitmap for non-existent data values. +c Since the data from the regional models have been interpolated +c from either a polar stereographic or lambert conformal grid +c onto a lat/lon grid, there will be some gridpoints around the +c edges of this lat/lon grid that have no data; these grid +c points have been bitmapped out by Mark Iredell's interpolater. +c To provide another means of checking for invalid data points +c later in the program, set these bitmapped data values to a +c value of -999.0. The min and max of this array are also +c returned if a user wants to check for reasonable values. +c + logical(1) ld + dimension ld(n),d(n) +c + dmin=1.E15 + dmax=-1.E15 +c + do i=1,n + if (ld(i)) then + dmin=min(dmin,d(i)) + dmax=max(dmax,d(i)) + else + d(i) = -999.0 + endif + enddo +c + return + end +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic (imax,jmax,lb1d,lb2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of logical data (lb1d) into a 2-dimensional output +c array (dimension imax,jmax) of logical data (lb2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c lb1d 1-d array containing logical bitmap values +c iscanflag This is kgds(11), an integer value in the GDS, +c which holds the scanning mode for the data values +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + logical(1) lb1d(imax*jmax),lb2d(imax,jmax) + logical(1) :: need_to_flip_lats + integer :: ilat,ilatix,ilon,imax,jmax +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + lb2d(ilon,ilatix) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + lb2d(ilon,ilat) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic_netcdf (imax,jmax,dat1d,lb2d + & ,xmissing_val,need_to_flip_lats) +c +c ABSTRACT: The purpose of this routine is to create a 2-d logical +c bitmap to be used for masking out regions with missing data, +c such as for a regional grid with irregular boundaries (such as +c we've seen for the regional / nested FV3). This bitmap will +c have the same functionality as a GRIB1/GRIB2 bitmap. The trick +c is that NetCDF does not have a logical bitmap within its +c definition, so we need to make one. We do this by reading in +c the "missing_value" attribute for any variable, then here we +c scan through all the data values retrieved from the NetCDF read, +c and then for all grid points with missing values we set the +c valid_pt flag to .false. +c +c Note the use of the need_to_flip_lats flag. This is in order to +c handle grids that are flipped. Most grids -- NCEP, UKMET, ECMWF +c -- have point (1,1) as the uppermost left point on the grid, and +c the data goes from north to south. Some grids -- GFDL and the +c new NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the need_to_flip_lats flag was set to TRUE in getgridinfo, meaning +c that we have northward scanning data, we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d array containing floating point data values +c xmissing_val real value of missing value for the given variable +c that was read in for the calling routine +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + USE verbose_output + + implicit none + + logical(1) lb2d(imax,jmax) + logical(1) need_to_flip_lats + integer ilat,ilatix,ilon,imax,jmax,tct,fct,mct + real dat1d(imax*jmax) + real xmissing_val +c + tct = 0 + fct = 0 + mct = 0 + + if (verb >= 3) then + print *,' ' + print *,'TOP of conv1d2d_logic_netcdf, xmissing_val= ' + & ,xmissing_val + print *,' ' + endif +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then + lb2d(ilon,ilatix) = .false. +c print *,'LBSF FLIP: ilon= ',ilon,' ilatix= ',ilatix +c fct = fct + 1 + else + lb2d(ilon,ilatix) = .true. +c tct = tct + 1 + endif + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then +c print *,'LBSF no-flip: ilon= ',ilon,' ilat= ',ilat + lb2d(ilon,ilat) = .false. +c fct = fct + 1 + else + lb2d(ilon,ilat) = .true. +c tct = tct + 1 + endif + enddo + enddo + + endif + +c print *,' ' +c print *,' LB STATS: tct= ',tct,' fct= ',fct,' mct= ',mct +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine conv1d2d_real (imax,jmax,dat1d,dat2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of real data (dat1d) into a 2-dimensional output +c array (dimension imax,jmax) of real data (dat2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d real array of data +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c dat2d 2-d real array of data +c + logical(1) :: need_to_flip_lats + real dat1d(imax*jmax),dat2d(imax,jmax) +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + dat2d(ilon,ilatix) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + dat2d(ilon,ilat) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_nlists (inp,trkrinfo,netcdfinfo) +c +c ABSTRACT: This subroutine simply reads in the namelists that are +c created in the shell script. Namelist datein contains the +c starting date information, plus the model identifier. Namelist +c stswitch contains the flags for processing for each storm. +c + USE inparms; USE set_max_parms; USE atcf; USE trkrparms; USE phase + USE structure; USE gfilename_info + USE verbose_output; USE waitfor_parms; USE netcdf_parms + USE tracking_parm_prefs + + implicit none + + integer ifh + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo +c + namelist/datein/inp + namelist/atcfinfo/atcfnum,atcfname,atcfymdh,atcffreq + namelist/trackerinfo/trkrinfo + namelist/phaseinfo/phaseflag,phasescheme,wcore_depth + namelist/structinfo/structflag,ikeflag + namelist/fnameinfo/gmodname,rundescr,atcfdescr + namelist/verbose/verb,verb_g2 + namelist/waitinfo/use_waitfor,wait_min_age,wait_min_size + & ,wait_max_wait,wait_sleeptime + & ,use_per_fcst_command,per_fcst_command + namelist/netcdflist/netcdfinfo + namelist/parmpreflist/user_wants_to_track_zeta850 + & ,user_wants_to_track_zeta700,user_wants_to_track_wcirc850 + & ,user_wants_to_track_wcirc700,user_wants_to_track_gph850 + & ,user_wants_to_track_gph700,user_wants_to_track_mslp + & ,user_wants_to_track_wcircsfc,user_wants_to_track_zetasfc + & ,user_wants_to_track_thick500850 + & ,user_wants_to_track_thick200500 + & ,user_wants_to_track_thick200850 + +c Set namelist default values: + use_per_fcst_command='t' + per_fcst_command=' ' + atcffreq=600 + trkrinfo%enable_timing=1 + trkrinfo%want_oci=.false. + trkrinfo%gribver=1 ! Set to GRIB1 as default, can be set to + ! something else in the namelist input. + + read (5,NML=datein,END=801) + 801 continue + read (5,NML=atcfinfo,END=807) + 807 continue + print *,'just before trackerinfo read namelist' + read (5,NML=trackerinfo,END=809) + 809 continue + print *,'just after trackerinfo read namelist' + read (5,NML=phaseinfo,END=811) + 811 continue + read (5,NML=structinfo,END=815) + 815 continue + read (5,NML=fnameinfo,END=817) + 817 continue + read (5,NML=waitinfo,END=821) + 821 continue + read (5,NML=netcdflist,END=823) + 823 continue + read (5,NML=parmpreflist,END=825) + 825 continue + read (5,NML=verbose,END=819,ERR=833) + 819 continue + goto 837 + 833 continue + verb = 1 + 837 continue + + print *,'in read_nlists, verb= ',verb + + if ( verb .ge. 0 ) then + print *,' ' + print *,'After datein namelist in trak.f, namelist ' + & ,'parms follow:' + print *,'Forecast initial year = byy = ',inp%byy + print *,'Forecast initial month = bmm = ',inp%bmm + print *,'Forecast initial day = bdd = ',inp%bdd + print *,'Forecast initial hour = bhh = ',inp%bhh + print *,'Forecast model identifier = model= ',inp%model + print *,'Forecast model type = modtyp= ',inp%modtyp + print *,'Forecast model data lead time units= lt_units= ' + & ,inp%lt_units + print *,'Forecast model data sequencing setup= file_seq= ' + & ,inp%file_seq + print *,'Forecast model nest type = ',inp%nesttyp +c + print *,' ' + print *,'Values read in from atcfinfo namelist: ' + write (6,89) atcfnum,atcfname + write (6,90) atcfymdh + write (6,92) atcffreq + 89 format ('ATCF ID = ',i2,' ATCF Name = ',a4) + 90 format ('ATCF date (initial date on output atcf records) = ' + & ,i10) + 92 format ('ATCF output frequency (in hours*100) = atcffreq = ',i6) +c + print *,' ' + print *,'Values read in from trackerinfo namelist follow: ' + write (6,101) ' western boundary = westbd = ',trkrinfo%westbd + write (6,101) ' eastern boundary = eastbd = ',trkrinfo%eastbd + write (6,101) ' northern boundary = northbd = ',trkrinfo%northbd + write (6,101) ' southern boundary = southbd = ',trkrinfo%southbd + write (6,102) ' tracker type = ',trkrinfo%type + write (6,103) ' mslp threshold = mslpthresh = ' + & ,trkrinfo%mslpthresh + write (6,120) ' Flag for using backup mslp gradient check= ' + & ,'use_backup_mslp_grad_check = ' + & ,trkrinfo%use_backup_mslp_grad_check + write (6,103) ' v850 threshold = v850thresh = ' + & ,trkrinfo%v850thresh + write (6,122) ' Flag for using backup 850 mb Vt check= ' + & ,'use_backup_850_vt_check = ' + & ,trkrinfo%use_backup_850_vt_check + write (6,123) ' Max allowable distance between the ' + & ,'tracker-found fixes for mslp and 850 zeta = ' + & ,trkrinfo%max_mslp_850 + write (6,104) ' model grid type = ',trkrinfo%gridtype + write (6,101) ' Contour interval to be used = ',trkrinfo%contint + write (6,106) ' Flag for whether or not roci will be computed' + & ,' and written out for tracker-type case = ' + & ,trkrinfo%want_oci + write (6,105) ' Flag for whether or not vitals will be written ' + & ,'out = ',trkrinfo%out_vit + write (6,109) ' Flag for whether or not a land mask will be ' + & ,'used for tcgen candidate low filtering = ' + & ,trkrinfo%use_land_mask + write (6,110) ' Flag for input data type (grib or netcdf) = ' + & ,trkrinfo%inp_data_type + write (6,107) ' Flag for which GRIB version (1 or 2) the input' + & ,' data will be in = ',trkrinfo%gribver + write (6,108) ' Flag for input GRIB2 JPDTN (0 or 1) = ' + & ,trkrinfo%g2_jpdtn + write (6,112) ' Flag for input GRIB2 MSLP ID (1 or 192) = ' + & ,trkrinfo%g2_mslp_parm_id + write (6,114) ' Flag for input GRIB1 MSLP ID (102 or 130) = ' + & ,trkrinfo%g1_mslp_parm_id + write (6,116) ' Flag for input GRIB1 sfcwind level type ' + & ,'(PDS Octet 10... should be 1 or 105) = ' + & ,trkrinfo%g1_sfcwind_lev_typ + write (6,118) ' Flag for input GRIB1 sfcwind level value ' + & ,'(PDS Octets 11 & 12... usually 0 or 10) = ' + & ,trkrinfo%g1_sfcwind_lev_val + + 101 format (a31,f7.2) + 102 format (a16,a7) + 103 format (a31,f7.4) + 104 format (a19,a8) + 106 format (a46,a41,L1) + 105 format (a48,a6,a1) + 109 format (a45,a41,a1) + 110 format (a45,a6) + 107 format (a47,a19,i1) + 108 format (a39,i2) + 112 format (a43,i4) + 114 format (a45,i4) + 116 format (a41,a39,i4) + 118 format (a42,a43,i4) + 120 format (a44,a29,a1) + 122 format (a40,a26,a1) + 123 format (a36,a44,f7.1) + + print *,' ' + print *,' ' + print *,'Values read in from netcdflist namelist: ' + print *,' ' + write (6,300) netcdfinfo%num_netcdf_vars ! Total *possible* + ! number of input NetCDF variables, + ! including those that are included + ! in the input file and those that + ! are not. + write (6,370) netcdfinfo%netcdf_filename ! full path filename + write (6,301) + write (6,302) netcdfinfo%rv850name ! 850 mb rel vort + write (6,304) netcdfinfo%rv700name ! 700 mb rel vort + write (6,306) netcdfinfo%u850name ! 850 mb u-comp + write (6,308) netcdfinfo%v850name ! 850 mb v-comp + write (6,310) netcdfinfo%u700name ! 700 mb u-comp + write (6,312) netcdfinfo%v700name ! 700 mb v-comp + write (6,314) netcdfinfo%z850name ! 850 mb gp height + write (6,316) netcdfinfo%z700name ! 700 mb gp height + write (6,318) netcdfinfo%mslpname ! mslp + write (6,320) netcdfinfo%usfcname ! near-sfc u-comp + write (6,322) netcdfinfo%vsfcname ! near-sfc v-comp + write (6,324) netcdfinfo%u500name ! 500 mb u-comp + write (6,326) netcdfinfo%v500name ! 500 mb v-comp + write (6,328) netcdfinfo%tmean_300_500_name !Mean T @ 300-500 mb + write (6,330) netcdfinfo%z500name ! 500 mb gp height + write (6,332) netcdfinfo%z200name ! 200 mb gp height + write (6,334) netcdfinfo%lmaskname ! Land mask + write (6,336) netcdfinfo%z900name ! 900 mb gp height + write (6,338) netcdfinfo%z800name ! 800 mb gp height + write (6,340) netcdfinfo%z750name ! 750 mb gp height + write (6,342) netcdfinfo%z650name ! 650 mb gp height + write (6,344) netcdfinfo%z600name ! 600 mb gp height + write (6,346) netcdfinfo%z550name ! 550 mb gp height + write (6,348) netcdfinfo%z450name ! 450 mb gp height + write (6,350) netcdfinfo%z400name ! 400 mb gp height + write (6,352) netcdfinfo%z350name ! 350 mb gp height + write (6,354) netcdfinfo%z300name ! 300 mb gp height + write (6,355) netcdfinfo%time_name ! Name of time variable + ! (usually it is "time") + write (6,356) netcdfinfo%lon_name ! longitudes + write (6,358) netcdfinfo%lat_name ! latitudes + write (6,359) netcdfinfo%time_units ! This will be either "days" + ! or "hours". If it's "hours", + ! then all the time data values + ! are for hours since the initial + ! time. Same thing for "days", + ! however if it is "days", then + ! know that a value of 0.25 will + ! be the same as a 6-hour lead + ! time. + + 300 format ('Total *possible* number of input NetCDF variables,' + & ,/,' including those that are included in the input' + & ,/,' NetCDF file and those that are not = ',i4) + 370 format ('Input NetCDF filename = ',a180) + 301 format (' ',/ + & ,'List of NetCDF variables follows. A value of X ',/ + & ,'indicates the variable is not included in the ',/ + & ,'input file and no attempt will be made to read in ',/ + & ,'that variable: ',/,' ') + 302 format ('NetCDF variable name for 850 mb vort = ',a30) + 304 format ('NetCDF variable name for 700 mb vort = ',a30) + 306 format ('NetCDF variable name for 850 mb u-comp = ',a30) + 308 format ('NetCDF variable name for 850 mb v-comp = ',a30) + 310 format ('NetCDF variable name for 700 mb u-comp = ',a30) + 312 format ('NetCDF variable name for 700 mb v-comp = ',a30) + 314 format ('NetCDF variable name for 850 mb gp height = ',a30) + 316 format ('NetCDF variable name for 700 mb gp height = ',a30) + 318 format ('NetCDF variable name for MSLP = ',a30) + 320 format ('NetCDF variable name for near-sfc u-comp = ',a30) + 322 format ('NetCDF variable name for near-sfc v-comp = ',a30) + 324 format ('NetCDF variable name for 500 mb u-comp = ',a30) + 326 format ('NetCDF variable name for 500 mb v-comp = ',a30) + 328 format ('NetCDF variable name for 300-500 mb Mean T = ',a30) + 330 format ('NetCDF variable name for 500 mb gp height = ',a30) + 332 format ('NetCDF variable name for 200 mb gp height = ',a30) + 334 format ('NetCDF variable name for land-sea mask = ',a30) + 336 format ('NetCDF variable name for 900 mb gp height = ',a30) + 338 format ('NetCDF variable name for 800 mb gp height = ',a30) + 340 format ('NetCDF variable name for 750 mb gp height = ',a30) + 342 format ('NetCDF variable name for 650 mb gp height = ',a30) + 344 format ('NetCDF variable name for 600 mb gp height = ',a30) + 346 format ('NetCDF variable name for 550 mb gp height = ',a30) + 348 format ('NetCDF variable name for 450 mb gp height = ',a30) + 350 format ('NetCDF variable name for 400 mb gp height = ',a30) + 352 format ('NetCDF variable name for 350 mb gp height = ',a30) + 354 format ('NetCDF variable name for 300 mb gp height = ',a30) + 355 format ('NetCDF variable name for time = ',a30) + 356 format ('NetCDF variable name for longitudes = ',a30) + 358 format ('NetCDF variable name for latitudes = ',a30) + 359 format ('NetCDF time value (hours|days) = ',a30) + + print *,' ' + print *,' ' + print *,'Values read in from parmpreflist namelist: ' + print *,' ' + write (6,402) user_wants_to_track_zeta850 + write (6,404) user_wants_to_track_zeta700 + write (6,406) user_wants_to_track_wcirc850 + write (6,408) user_wants_to_track_wcirc700 + write (6,410) user_wants_to_track_gph850 + write (6,412) user_wants_to_track_gph700 + write (6,414) user_wants_to_track_mslp + write (6,416) user_wants_to_track_wcircsfc + write (6,418) user_wants_to_track_zetasfc + write (6,420) user_wants_to_track_thick500850 + write (6,422) user_wants_to_track_thick200500 + write (6,424) user_wants_to_track_thick200850 + + 402 format ('user_wants_to_track_zeta850= ',a2) + 404 format ('user_wants_to_track_zeta700= ',a2) + 406 format ('user_wants_to_track_wcirc850= ',a2) + 408 format ('user_wants_to_track_wcirc700= ',a2) + 410 format ('user_wants_to_track_gph850= ',a2) + 412 format ('user_wants_to_track_gph700= ',a2) + 414 format ('user_wants_to_track_mslp= ',a2) + 416 format ('user_wants_to_track_wcircsfc= ',a2) + 418 format ('user_wants_to_track_zetasfc= ',a2) + 420 format ('user_wants_to_track_thick500850= ',a2) + 422 format ('user_wants_to_track_thick200500= ',a2) + 424 format ('user_wants_to_track_thick200850= ',a2) + + print *,' ' + print *,'Values read in from phaseinfo namelist: ' + write (6,211) phaseflag,phasescheme + write (6,212) wcore_depth + 211 format ('Storm phase flag = ',a1,' Phase scheme = ',a4) + 212 format ('Storm phase, warm core depth (wcore_depth) = ',f7.2) + + print *,' ' + print *,'Values read in from structinfo namelist: ' + write (6,93) structflag + write (6,95) ikeflag + 93 format ('Structure flag = ',a1) + 95 format ('IKE flag = ',a1) + + print *,' ' + print *,'Values read in for grib file name from fnameinfo' + & ,' namelist: ' + write (6,131) gmodname + write (6,133) rundescr + write (6,135) atcfdescr + 131 format ('Model name description = gmodname = ',a4) + 133 format ('Forecast run description = rundescr = ',a40) + 135 format ('Optional ATCF / Storm name description = atcfdescr = ' + & ,a40) + + print *,' ' + print *,'Value read in for verbose output for most output:' + write (6,141) verb + 141 format ('Value read in for verbose flag = verb = ',i2) + + print *,' ' + print *,'Value read in for verbose output for grib2 output:' + write (6,142) verb_g2 + 142 format ('Value read in for GRIB2 verbose flag = verb_g2 = ',i2) + + print *,' ' + print *,'Values read in from waitinfo namelist:' + write (6,151) use_waitfor + write (6,152) wait_min_age + write (6,153) wait_min_size + write (6,154) wait_max_wait + write (6,155) wait_sleeptime + if(len_trim(per_fcst_command)>0) then + write (6,156) trim(per_fcst_command) + else +c No command specified, so disable the feature + use_per_fcst_command='n' + endif + 151 format ('Flag for input file waiting = use_waitfor = ',a1) + 152 format ('min age (time in seconds since last mod) = ' + & ,'wait_min_age = ',i8) + 153 format ('min file size in bytes = wait_min_size = ',i12) + 154 format ('max number of seconds to wait for each file = ' + & ,'wait_max_wait = ',i6) + 155 format ('number of seconds to sleep between checks = ' + & ,'wait_sleeptime = ',i6) + 156 format ('command to run after every forecast time = "',A,'"') +c + if (use_waitfor == 'y') then + if (inp%file_seq == 'multi') then + continue + else + print *,' ' + print *,'!!! ERROR: The use_waitfor flag is set to "y".' + print *,' This requires that the inp%file_seq flag be' + print *,' set to "multi", but you have specified ' + print *,' something else. ' + print *,' inp%file_seq = ',inp%file_seq + print *,' STOPPING....' + print *,' ' + STOP 95 + endif + endif +c + endif + return + end +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_fhours (ifhmax) +c +c ABSTRACT: This subroutine reads in a text file that contains the +c forecast times that will be read in. The format of the file is +c in "MMMMM", i.e., minutes, for example, for a forecast going out +c to 120h, the file would look like this: +c +c For reference, here +c are the times that +c match up with the +c minutes on the left: +c +c 1 0 0:00 +c 2 240 4:00 +c 3 270 4:30 +c 4 300 5:00 +c 5 330 5:30 +c 6 360 6:00 +c 7 600 10:00 +c 8 630 10:30 +c 9 660 11:00 +c 10 690 11:30 +c 11 720 12:00 +c 12 960 16:00 +c 13 990 16:30 +c . . . +c . . . +c . . . +c 87 7200 120:00 +c +c Note that we are now allowing for sub-hourly time intervals. +c + USE tracked_parms + USE verbose_output + + implicit none +c + integer, parameter :: iunit_fh=15 + integer itmphrs(750),itmpmins(750),input_mins(750),itmpltix(750) + integer ifhmax,inphr,inpmin,ict,i,ifa,ifma,icma,ira,inpltix,ila + real xminfract + + itmphrs = -99 + itmpmins = -99 + + if (allocated(ifhours)) deallocate (ifhours) + if (allocated(iftotalmins)) deallocate (iftotalmins) + if (allocated(ifclockmins)) deallocate (ifclockmins) + if (allocated(fhreal)) deallocate (fhreal) + if (allocated(ltix)) deallocate (ltix) + + ict = 0 + do while (.true.) + + if ( verb .ge. 3 ) then + print *,'Top of while loop in read_fhours' + endif + + read (iunit_fh,85,end=130) inpltix,inpmin + write (6,85) inpltix,inpmin + + if (inpmin >= 0 .and. inpmin < 150000) then + ict = ict + 1 + itmpltix(ict) = inpltix + itmphrs(ict) = inpmin / 60 + itmpmins(ict) = mod(inpmin,60) + input_mins(ict) = inpmin + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Input minutes not between 0 and 150000' + print *,'!!! inpmin= ',inpmin + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + if ( verb .ge. 3 ) then + print *,'readloop, ict= ',ict,' inpmin= ',inpmin + endif + + enddo + + 130 continue + + ifhmax = ict + + 85 format (i4,1x,i5) + + if ( verb .ge. 3 ) then + print *,' ' + endif + + allocate (ifhours(ifhmax),stat=ifa) + allocate (iftotalmins(ifhmax),stat=ifma) + allocate (ifclockmins(ifhmax),stat=icma) + allocate (fhreal(ifhmax),stat=ira) + allocate (ltix(ifhmax),stat=ila) + if (ifa /= 0 .or. ifma /= 0 .or. icma /= 0 .or. ira /= 0 .or. + & ila /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_fhours allocating either ifhours,' + print *,'!!! iftotalmins, ifclockmins or fhreal.' + print *,'!!! ifa = ',ifa,' ifma= ',ifma,' ira= ',ira + print *,'!!! icma= ',icma,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + do i = 1,ifhmax + + ltix(i) = itmpltix(i) + xminfract = float(itmpmins(i)) / 60. + fhreal(i) = float(itmphrs(i)) + xminfract + ifhours(i) = itmphrs(i) + ifclockmins(i) = itmpmins(i) + iftotalmins(i) = input_mins(i) + + if (i > 1) then + if (fhreal(i) > fhreal(i-1)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: In read_fhours, the time read in ' + print *,'!!! is not greater than the previous time.' + print *,'!!! i= ',i + print *,'!!! fhreal(i)= ',fhreal(i) + print *,'!!! fhreal(i-1)= ',fhreal(i-1) + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + endif + + if ( verb .ge. 3 ) then + write (6,87) i,ltix(i),iftotalmins(i),fhreal(i),ifhours(i) + & ,ifclockmins(i) + endif + + enddo + + 87 format (1x,'i= ',i3,' input lead time index= ',i4,' minutes= ' + & ,i5,' real_lead_time= ',f6.2,' clock_lead_time= ',i3,':' + & ,i2) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_tcv_card1 (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + logical(1) :: vit_file_exists + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + + ! Check to see if the TC Vitals file exists. If so, then open it + ! using the unit specified in lucard. + + inquire (file="tcvit_rsmc_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for existing, RSMC-numbered' + & ,' storms exists and will be opened with ' + & ,' unit= lucard= ',lucard + endif + + open (unit=lucard,file="tcvit_rsmc_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_rsmc_storms.txt has ' + print *,' been opened with unit= lucard= ',lucard + endif + + else + + if (trkrinfo%type == 'tracker') then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card. The fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. This TC Vitals file is needed for' + print *,'!!! a tracker case. Check to see that the ' + print *,'!!! TC Vitals file exists in this directory and' + print *,'!!! is named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING....' + print *,'!!! ' + iret=99 + return + endif + + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! NOTE: In read_tcv_card, the fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. While this TC Vitals file is ' + print *,'!!! needed for tracker cases, you are running' + print *,'!!! either a midlat or tcgen case here, and so ' + print *,'!!! that file is not needed... although you can ' + print *,'!!! run with using tc vitals for those genesis' + print *,'!!! cases if you want to. You may want to check' + print *,'!!! and make sure this is what you intend. If ' + print *,'!!! you do want to use it, the TC Vitals file ' + print *,'!!! should be in this directory and it should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! ' + endif + + endif + + endif + + ii=1 + + if (vit_file_exists) then + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + 801 continue + endif + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + + maxstorm = numtcv + + if (maxstorm > 0) then + continue + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING...' + print *,'!!! ' + iret=99 + return + endif + endif + + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! Stopping....' + print *,' ' + endif + + iret = 99 + return + + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card opening rsmc TC vitals' + print *,'!!! file named tcvit_rsmc_storms.txt. A file with' + print *,'!!! that name needs to be in your working directory.' + print *,'!!! It should contain the input TC vitals for ' + print *,'!!! already-existing storms that have rsmc-issued' + print *,'!!! storm IDs.' + endif + + iret = 97 + return + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine read_gen_vitals1(lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + logical(1) :: vit_file_exists + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + + ! Check to see if the genesis TC Vitals file exists. If so, then + ! open it using the unit specified in lgvcard. + + inquire (file="tcvit_genesis_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for genesis' + & ,' storms exists and will be opened with ' + & ,' unit= lgvcard= ',lgvcard + endif + + open (unit=lgvcard,file="tcvit_genesis_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_genesis_storms.txt has ' + print *,' been opened with unit= lgvcard= ',lgvcard + endif + + endif + + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + + ii = numtcv + 1 + + if (vit_file_exists) then + + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1 + & ,1x,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + endif + + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals opening genesis vitals' + print *,'!!! file named tcvit_genesis_storms.txt' + endif + + iret = 97 + return + + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just read the +c grib file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE tracked_parms; USE inparms + USE verbose_output; USE params; USE grib_mod + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + + type(gribfield) :: gfld,prevfld,holdgfld + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1), allocatable :: lb(:) + logical :: unpack=.true. + logical :: open_grb=.false. + CHARACTER(len=8) :: pabbrev + integer,dimension(200) :: jids,jpdt,jgdt + integer, parameter :: jf=40000000 + integer :: listsec1(13) + integer pdt_4p0_vert_level,pdt_4p0_vtime + real xhold,xlondiff,xlatdiff,temp,firstval,lastval + real, allocatable :: f(:) + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer jpds(200),jgds(200),igetpds(200),igetgds(200) + integer, intent(in) :: ifh + integer, intent(out) :: imax,jmax + integer iia,ija,ila,midi,midj,i,j,iix,jix,ifa,iret + integer iscanflag,iggret,kf,k,lugb,lugi,jskp,jdisc + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 + + iggret = 0 + + allocate (lb(jf),stat=ila); allocate (f(jf),stat=ifa) + if (ila /= 0 .or. ifa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating either lb or f' + print *,'!!! ila = ',ila,' ifa= ',ifa + endif + iggret = 97 + return + endif + + if (trkrinfo%gribver == 2) then + + ! Search for a record from a GRIB2 file + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 ! meteorological products + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for Temperature or GP Height by production template.... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + ! Request a record on a lat/lon grid. + + jgdtn = 0 + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpdt(8) = 0 + jpdt(9) = iftotalmins(ifh) + else + jpdt(8) = 1 + jpdt(9) = ifhours(ifh) + endif + + if (verb >= 3) then + print *,'before getgb2 call, lugb= ',lugb,' lugi= ',lugi + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + if ( iret.ne.0) then + print *,' ' + print *,' ERROR: getgb2 error in getgridinfo = ',iret + print *,' FATAL ERROR: cannot proceed without info ' + print *,' from getgridinfo. STOPPING....' + stop 95 + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if ( verb_g2 .ge. 1 ) then + print *,' ' + print *,' -- BEGIN getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'PDT num= gfld%ipdtnum= ',gfld%ipdtnum + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + imax = gfld%igdtmpl(8) + jmax = gfld%igdtmpl(9) + dx = float(gfld%igdtmpl(17))/1.e6 + dy = float(gfld%igdtmpl(17))/1.e6 + kf = gfld%ngrdpts + + holdgfld = gfld + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + + endif + + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' -- END getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' ' + print *,' ' + + endif + + need_to_flip_lons = .false. + + iscanflag = gfld%igdtmpl(19) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(gfld%igdtmpl(12))/1.e6 + glatmax = float(gfld%igdtmpl(15))/1.e6 + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(gfld%igdtmpl(15))/1.e6 + glatmax = float(gfld%igdtmpl(12))/1.e6 + need_to_flip_lats = .false. + endif + + glonmin = float(gfld%igdtmpl(13))/1.e6 + glonmax = float(gfld%igdtmpl(16))/1.e6 + + if (verb .ge. 3) then + print *,'In getgridinfo: glatmin= ',glatmin + print *,' glatmax= ',glatmax + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + + else + + !------------------------------------------ + ! Search for a record from a GRIB1 file + !------------------------------------------ + + jpds = -1 + jgds = -1 + + jgds(1) = 0 ! Request a record that's on a lat/lon grid + + if ( verb .ge. 3 ) then + print *,'before getgb in getgridinfo, ifh= ',ifh + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* Forecast hour: ',i4,':',i2.2) + print *,' ifhours(ifh)= ',ifhours(ifh) + print *,' iftotalmins(ifh)= ',iftotalmins(ifh) + endif + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + j=0 + +c jpds(14) = 0 ! test +c + write(*,980) jpds(1),jpds(2) + write(*,981) jpds(3),jpds(4) + write(*,982) jpds(5),jpds(6) + write(*,983) jpds(7),jpds(8) + write(*,984) jpds(9),jpds(10) + write(*,985) jpds(11),jpds(12) + write(*,986) jpds(13),jpds(14) + write(*,987) jpds(15),jpds(16) + write(*,988) jpds(17),jpds(18) + write(*,989) jpds(19),jpds(20) + write(*,990) jpds(21),jpds(22) + write(*,991) jpds(23),jpds(24) + write(*,992) jpds(25) + write(*,880) jgds(1),jgds(2) + write(*,881) jgds(3),jgds(4) + write(*,882) jgds(5),jgds(6) + write(*,883) jgds(7),jgds(8) + write(*,884) jgds(9),jgds(10) + write(*,885) jgds(11),jgds(12) + write(*,886) jgds(13),jgds(14) + write(*,887) jgds(15),jgds(16) + write(*,888) jgds(17),jgds(18) + write(*,889) jgds(19),jgds(20) + write(*,890) jgds(21),jgds(22) + + 980 format(' jpds(1) = ',i7,' jpds(2) = ',i7) + 981 format(' jpds(3) = ',i7,' jpds(4) = ',i7) + 982 format(' jpds(5) = ',i7,' jpds(6) = ',i7) + 983 format(' jpds(7) = ',i7,' jpds(8) = ',i7) + 984 format(' jpds(9) = ',i7,' jpds(10) = ',i7) + 985 format(' jpds(11) = ',i7,' jpds(12) = ',i7) + 986 format(' jpds(13) = ',i7,' jpds(14) = ',i7) + 987 format(' jpds(15) = ',i7,' jpds(16) = ',i7) + 988 format(' jpds(17) = ',i7,' jpds(18) = ',i7) + 989 format(' jpds(19) = ',i7,' jpds(20) = ',i7) + 990 format(' jpds(21) = ',i7,' jpds(22) = ',i7) + 991 format(' jpds(23) = ',i7,' jpds(24) = ',i7) + 992 format(' jpds(25) = ',i7) + 880 format(' jgds(1) = ',i7,' jgds(2) = ',i7) + 881 format(' jgds(3) = ',i7,' jgds(4) = ',i7) + 882 format(' jgds(5) = ',i7,' jgds(6) = ',i7) + 883 format(' jgds(7) = ',i7,' jgds(8) = ',i7) + 884 format(' jgds(9) = ',i7,' jgds(10) = ',i7) + 885 format(' jgds(11) = ',i7,' jgds(12) = ',i7) + 886 format(' jgds(13) = ',i7,' jgds(14) = ',i7) + 887 format(' jgds(15) = ',i7,' jgds(16) = ',i7) + 888 format(' jgds(17) = ',i7,' jgds(18) = ',i7) + 889 format(' jgds(19) = ',i7,' jgds(20) = ',i7) + 890 format(' jgds(20) = ',i7,' jgds(22) = ',i7) + + print *,'lugb= ',lugb,' lugi= ',lugi + print *,'before ggi getgb jpds(14) = ',jpds(14) + print *,'before ggi getgb jgds(1) = ',jgds(1) + + call getgb(lugb,lugi,jf,j,jpds,jgds, + & kf,k,igetpds,igetgds,lb,f,iret) + + if (iret.ne.0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo calling getgb' + print *,'!!! Return code from getgb = iret = ',iret + endif + + iggret = iret + else + iggret=0 + imax = igetgds(2) + jmax = igetgds(3) + dx = float(igetgds(9))/1000. + dy = float(igetgds(10))/1000. + endif + +c write(*,780) igetpds(1),igetpds(2) +c write(*,781) igetpds(3),igetpds(4) +c write(*,782) igetpds(5),igetpds(6) +c write(*,783) igetpds(7),igetpds(8) +c write(*,784) igetpds(9),igetpds(10) +c write(*,785) igetpds(11),igetpds(12) +c write(*,786) igetpds(13),igetpds(14) +c write(*,787) igetpds(15),igetpds(16) +c write(*,788) igetpds(17),igetpds(18) +c write(*,789) igetpds(19),igetpds(20) +c write(*,790) igetpds(21),igetpds(22) +c write(*,791) igetpds(23),igetpds(24) +c write(*,792) igetpds(25) +c write(*,680) igetgds(1),igetgds(2) +c write(*,681) igetgds(3),igetgds(4) +c write(*,682) igetgds(5),igetgds(6) +c write(*,683) igetgds(7),igetgds(8) +c write(*,684) igetgds(9),igetgds(10) +c write(*,685) igetgds(11),igetgds(12) +c write(*,686) igetgds(13),igetgds(14) +c write(*,687) igetgds(15),igetgds(16) +c write(*,688) igetgds(17),igetgds(18) +c write(*,689) igetgds(19),igetgds(20) +c write(*,690) igetgds(21),igetgds(22) +c +c 780 format(' kpds(1) = ',i7,' kpds(2) = ',i7) +c 781 format(' kpds(3) = ',i7,' kpds(4) = ',i7) +c 782 format(' kpds(5) = ',i7,' kpds(6) = ',i7) +c 783 format(' kpds(7) = ',i7,' kpds(8) = ',i7) +c 784 format(' kpds(9) = ',i7,' kpds(10) = ',i7) +c 785 format(' kpds(11) = ',i7,' kpds(12) = ',i7) +c 786 format(' kpds(13) = ',i7,' kpds(14) = ',i7) +c 787 format(' kpds(15) = ',i7,' kpds(16) = ',i7) +c 788 format(' kpds(17) = ',i7,' kpds(18) = ',i7) +c 789 format(' kpds(19) = ',i7,' kpds(20) = ',i7) +c 790 format(' kpds(21) = ',i7,' kpds(22) = ',i7) +c 791 format(' kpds(23) = ',i7,' kpds(24) = ',i7) +c 792 format(' kpds(25) = ',i7) +c 680 format(' kgds(1) = ',i7,' kgds(2) = ',i7) +c 681 format(' kgds(3) = ',i7,' kgds(4) = ',i7) +c 682 format(' kgds(5) = ',i7,' kgds(6) = ',i7) +c 683 format(' kgds(7) = ',i7,' kgds(8) = ',i7) +c 684 format(' kgds(9) = ',i7,' kgds(10) = ',i7) +c 685 format(' kgds(11) = ',i7,' kgds(12) = ',i7) +c 686 format(' kgds(13) = ',i7,' kgds(14) = ',i7) +c 687 format(' kgds(15) = ',i7,' kgds(16) = ',i7) +c 688 format(' kgds(17) = ',i7,' kgds(18) = ',i7) +c 689 format(' kgds(19) = ',i7,' kgds(20) = ',i7) +c 690 format(' kgds(20) = ',i7,' kgds(22) = ',i7) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,' dx= ',dx,' dy= ',dy + endif + + +c ---------------------------------------------------------------- +c Get boundaries of the data grid. NOTE: gds(4) is referred to in +c GRIB documenatation as the "Latitude of origin", which might +c imply "minimum Latitude". However, for the grids that we'll be +c using in this program, the "Latitude of origin" will be listed +c under gds(4) as the northernmost point (eg., in MRF, +c gds(4) = 90), so for this program, use gds(4) as your max lat, +c and gds(7) as your min lat. However, in case NCEP, UKMET or +c ECMWF change their convention and begin flipping their grids, a +c check is made to make sure that the max lat is not less than the +c min lat. +c +c BUGFIX (August, 2001): It is possible to have an input grid +c which goes from south to north (such as NAVGEM). In this case, +c we flip the data in subroutine conv1d2d_real. However, the max +c and min latitudes listed in the GRIB GDS will be confused, so we +c need to check the value of the GRIB scanning mode flag here. + + need_to_flip_lons = .false. + + iscanflag = igetgds(11) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(igetgds(4))/1000. + glatmax = float(igetgds(7))/1000. + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(igetgds(7))/1000. + glatmax = float(igetgds(4))/1000. + need_to_flip_lats = .false. + endif + + glonmin = float(igetgds(5))/1000. + glonmax = float(igetgds(8))/1000. + + endif + +c After this point in this subroutine, nothing is GRIB1 / GRIB2 +c specific, so it does not need to be within the if/then +c statement above that differentiated between GRIB / GRIB2. + +c17Jul2014 if (glonmin < 0.0) glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax < 0.0) glonmax = 360. - abs(glonmax) + + if (glonmin >= 0.0 .and. glonmax >= 0.0) then + if (glonmin > glonmax) then + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Badly notated longitude boundaries in ' + print *,' GRIB PDS, because the min longitude ' + print *,' (glonmin) is greater than the max ' + print *,' longitude (glonmax) where both longitudes' + print *,' are greater than 0.' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + print *,' !!! STOPPING....' + stop 98 + endif + endif + elseif (glonmin < 0.0 .and. glonmax >= 0.0) then + ! An example of this is the MPAS data, which starts and ends + ! at the dateline and is specified as glonmin=-179.875, + ! glonmax=179.875. Convert to be positive and go from + ! 180.125 to 539.875. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0, glonmax > 0, so glonmin' + print *,' will be converted to be > 0 and 360 will' + print *,' be added to glonmax.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. + abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin < 0.0 .and. glonmax < 0.0) then + ! Examples of this are GFDL and HWRF. In this case, make + ! both glonmin and glonmax positive. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0 and glonmax < 0, so both' + print *,' will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin >= 0.0 .and. glonmax < 0.0) then + ! An example of this is the GFS data, which goes from + ! glonmin=0.0 to glonmax=-0.5. Convert it here to go + ! from glonmin=0.0 to glonmax=359.5 + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is >= 0 and glonmax < 0, so' + print *,' glonmax will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + endif + +c17Jul2014 if (glonmin < 0.0) then +c17Jul2014 glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax <= 0.0) then +c17Jul2014 glonmax = 360. - abs(glonmax) +c17Jul2014 else +c17Jul2014 glonmax = 360 + abs(glonmax) +c17Jul2014 endif +c17Jul2014 endif + + if (glatmax < glatmin) then + temp = glatmax + glatmax = glatmin + glatmin = temp + endif + + if (glonmin > 200.0 .and. glonmin <= 360.) then + if (glonmax < 50.) then + ! Likely GM-wrapping in current record + glonmax = glonmax + 360. + endif + endif +c + if ( verb .ge. 3 ) then + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + print *,' ' + print *,'NOTE: For regional grids, valid data points might' + print *,'NOT extend all the way to the gds-defined grid ' + print *,'boundary, due to the fact that data have been ' + print *,'interpolated from a NPS or Lamb-Conf grid onto a ' + print *,'lat/lon grid. This program checks the logical ' + print *,'bitmap for valid data points, but just keep this in' + print *,'mind if trying to debug errors that occur near the' + print *,'grid boundaries for regional models.' + endif + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glat)) deallocate(glat) + if (allocated(glon)) deallocate(glon) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + endif + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + + iggret = 96 + return + endif + + do j=1,jmax + glat(j) = glatmax - (j-1)*dy + enddo + do i=1,imax + glon(i) = glonmin + (i-1)*dx + enddo + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + +c -------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have +c forgotten to change the input grid bounds from a global grid +c run). Modify the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just query the +c netcdf file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE inparms + USE verbose_output; USE netcdf_parms + + implicit none +c + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (datecard) inp + + logical(1) :: need_to_flip_lats,need_to_flip_lons + real xhold,xlondiff,xlatdiff + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer iscanflag,iggret + integer, intent(in) :: ncfile_id + integer, intent(out) :: imax,jmax + integer :: iia,ija,midi,midj,i,j,iix,jix +c + + iggret = 0 + + call get_ncdim1(ncfile_id,netcdfinfo%lon_name,imax) + call get_ncdim1(ncfile_id,netcdfinfo%lat_name,jmax) + + if (allocated(tmplon)) deallocate (tmplon) + if (allocated(tmplat)) deallocate (tmplat) + allocate (tmplon(imax),stat=iia) + allocate (tmplat(jmax),stat=ija) + if (iia /= 0 .or. ija /= 0) then + print *,' ' + print *,'!!! ERROR in sub getgridinfo_netcdf allocating arrays.' + print *,'!!! iia = ',iia,' ija= ',ija + iggret = 94 + return + endif + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + endif + + call get_var1_double (ncfile_id,netcdfinfo%lon_name,imax,tmplon) + call get_var1_double (ncfile_id,netcdfinfo%lat_name,jmax,tmplat) + +c Compute the dx and dy by picking values out of the middle of +c the lat and lon arrays.... + + midi = imax/2 + midj = jmax/2 + + dx = abs(tmplon(midi) - tmplon(midi-1)) + dy = abs(tmplat(midj) - tmplat(midj-1)) + + if (verb .ge. 1) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,'dx= ',dx,' dy= ',dy + print *,' ' + write (6,112) midi,dx + write (6,113) midj,dy + + 112 format(1x,' DX: midi= ',i4,' dx= ',f8.4) + 113 format(1x,' DY: midj= ',i4,' dy= ',f8.4) + endif + + +c ------------------------------------------------------------------ +c Get boundaries of the data grid. Note that it is possible to have +c an input grid which goes from south to north (in fact, it appears +c that many NetCDF files are constructed this way). Keep in mind, +c however, that the tracker has been written such that point (1,1) +c should be the upper-leftmost point on the grid, while point +c (imax,jmax) should be the lower-rightmost point. If we check and +c find that we're dealing with data that instead starts from the +c south and increases northward, we flip the data in subroutine +c conv1d2d_real. Similarly here, we make sure to test so that when +c we are done in this routine, glatmax refers to the northernmost +c latitude and glatmin the southernmost latitude. + + if (tmplon(imax) > tmplon(1)) then + glonmin = tmplon(1) + glonmax = tmplon(imax) + else + glonmin = tmplon(imax) + glonmax = tmplon(1) + endif + + if (tmplat(1) > tmplon(jmax)) then + glatmax = tmplat(1) + glatmin = tmplat(jmax) + else + glatmax = tmplat(jmax) + glatmin = tmplat(1) + endif + + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glon)) deallocate (glon) + if (allocated(glat)) deallocate (glat) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + iggret = 96 + return + endif + + ! If the lat or lon grids are flipped (i.e., the lats increase + ! from south to north, or the lons increase westward), then we + ! will need to flip both the data arrays as well as the arrays + ! that are holding the values of the lats and lons.... + + need_to_flip_lats = .false. + need_to_flip_lons = .false. + + if (tmplat(1) > tmplon(jmax)) then + do j=1,jmax + glat(j) = tmplat(j) + enddo + else + do j=1,jmax + jix = jmax - j + 1 + glat(jix) = tmplat(j) + enddo + need_to_flip_lats = .true. + endif + + if (tmplon(imax) > tmplon(1)) then + do i=1,imax + glon(i) = tmplon(i) + enddo + else + do i=1,imax + iix = imax - i + 1 + glon(iix) = tmplon(i) + enddo + need_to_flip_lons = .true. + endif + +c do i = 1,imax +c print *,'i= ',i,' glon(i)= ',glon(i) +c enddo +c do j = 1,jmax +c print *,'j= ',j,' glat(j)= ',glat(j) +c enddo + +c --------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have forgotten +c to change the input grid bounds from a global grid run). Modify +c the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) +c +c ABSTRACT: The purpose of this subroutine is to read the "time" +c dimension and "time data" from the NetCDF file so that we know +c how many time levels there are and what those time levels are. +c One reason for doing this is that some models, like the GFDL +c FV3, do not output hour 0 data, so we need to check this first +c before running through the tracking processing for the various +c hours. We will take the list of hours read in here directly from +c the NetCDF file and compare that against the *requested* list of +c forecast hours that the user has entered. The user might not be +c aware that there is no hour 0 data for a given model. We compare +c these two lists of forecast hours and then write a message if +c there is a lead time that is not in the NetCDF file. +c +c INPUT: +c ncfile character name of NetCDF file +c ncfile_id integer id associated with NetCDF file after open +c ifhmax integer max number of lead times that the user has +c requested on the input lead times data file. This +c value was set in subroutine read_fhours. +c netcdfinfo variable of user-defined type netcdfstuff (from +c module netcdf_parms). +c +c OUTPUT: +c ncfile_tmax integer max number of lead times that are in the +c NetCDF file, as read in from this subroutine +c ncfile_has_hour0 character flag (y|n) that tells whether or not +c the input NetCDF data file actually has an hour0 +c record in it or not. +c + USE netcdf_parms; USE tracked_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo + + character :: ncfile*180,ncfile_has_hour0*1,match_check*1 + integer, intent(in) :: ncfile_id + integer, intent(out) :: ncfile_tmax + integer :: infta,k,m,n,ifhmax,irnhret,usertime +c + + irnhret = 0 + ncfile_has_hour0 = 'n' + + !----------------------------------------------------------- + ! First read the NetCDF file to get the number of time levels, + ! which will be returned in "ncfile_tmax".... + !----------------------------------------------------------- + + print *,' ' + print *,'in read_netcdf_hours...' + print *,'ncfile_id= ',ncfile_id + print *,'netcdfinfo%time_name= ',netcdfinfo%time_name + print *,'ncfile_tmax= ',ncfile_tmax + + call get_ncdim1(ncfile_id,netcdfinfo%time_name,ncfile_tmax) + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + print *,'Num netcdf time levs= ncfile_tmax= ',ncfile_tmax + endif + + if (allocated(netcdf_file_time_values)) then + deallocate (netcdf_file_time_values) + endif + + allocate (netcdf_file_time_values(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating' + print *,'!!! netcdf_file_time_values array. infta = ',infta + irnhret = 94 + return + endif + + + !----------------------------------------------------------- + ! Now read in the actual time values that are stored in the + ! NetCDF file.... + !----------------------------------------------------------- + + call get_var1_double (ncfile_id,netcdfinfo%time_name,ncfile_tmax + & ,netcdf_file_time_values) + + if (verb .ge. 1) then + do k = 1,ncfile_tmax + print *,'k= ',k,' netcdf_file_time_values(k)= ' + & ,netcdf_file_time_values(k) + enddo + endif + + !------------------------------------------------------------ + ! Now convert the NetCDF time values into minutes in order to + ! be able to compare with the user-requested list of lead + ! times. Remember that the NetCDF lead times will be listed + ! either as hours or as fractions of days. + !------------------------------------------------------------ + + if (allocated(nctotalmins)) then + deallocate (nctotalmins) + endif + + allocate (nctotalmins(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating ' + print *,'!!! nctotalmins array. infta = ',infta + irnhret = 94 + return + endif + + do k = 1,ncfile_tmax + + if (netcdfinfo%time_units == 'hours') then + nctotalmins(k) = int(netcdf_file_time_values(k)) * 60 + elseif (netcdfinfo%time_units == 'days') then + nctotalmins(k) = int(netcdf_file_time_values(k) * 60. * 24.) + else + print *,' ' + print *,'!!! ERROR: In read_netcdf_hours, the value of' + print *,' netcdfinfo%time_units is neither hours nor days.' + print *,' netcdfinfo%time_units= ',netcdfinfo%time_units + print *,' STOPPING....' + print *,' ' + stop 99 + endif + + if (verb .ge. 1) then + write (6,71) k,netcdf_file_time_values(k),nctotalmins(k) + endif + + enddo + + 71 format (1x,i5,' netcdf_file_time_values(k)= ',f8.4 + & ,' nctotalmins(k)= ',i10) + + !------------------------------------------------------------ + ! Now go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match + ! the two lists up. The big one to watch out for is whether + ! or not the NetCDF file actually has an hour 0 lead time. + !------------------------------------------------------------ + + userloop: do n = 1,ifhmax + + usertime = iftotalmins(n) + + match_check = 'n' + + netcdfloop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + if (verb .ge. 1) then + print *,'+++ Time match for usertime= ',usertime + endif + match_check = 'y' + endif + + enddo netcdfloop + + if (match_check == 'n') then + + if (usertime == 0) then + print *,' ' + print *,'Warning: For a NetCDF file, the user has requested' + print *,'to read in an hour 0 file, however a scan of the' + print *,'time data values in the NetCDF file indicates' + print *,'that there is no hour 0 data in this file. ' + print *,'We will substitute either missing values or ' + print *,'the values from the TC Vitals data in the ' + print *,'hour 0 record and then start searching at the ' + print *,'next lead time.' + ncfile_has_hour0 = 'n' + else + print *,' ' + print *,'!!! ERROR: For a NetCDF file, the user has' + print *,' requested to process a particular lead time that' + print *,' does not exist in the NetCDF list of time ' + print *,' values.' + print *,' n= ',n + print *,' usertime= iftotalmins(n)= ',iftotalmins(n) + print *,' STOPPING....' + stop 99 + endif + + elseif (match_check == 'y') then + + if (usertime == 0) then + if (verb .ge. 1) then + print *,' ' + print *,'+++ For the input NetCDF file, an hour0 data ' + print *,' record exists in the data file.' + endif + ncfile_has_hour0 = 'y' + endif + + endif + + enddo userloop +c + return + end +c +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_valid_point (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) +c +c ABSTRACT: This subroutine checks to see if the input lat/lon +c point is associated with four surrounding (i,j) locations that +c have valid data. The writing of this routine was prompted by the +c HFIP project in February, 2009. Some of their high resolution +c data for their inner nests contained grids that had been rotated +c from native map projections to regular lat/lon grids, but that +c rotation left "empty" spots on the lat/lon grid where there is +c no data. Then when searching in find_maxmin, we were running +c barnes iterations from these lat/lon locations where there was +c no data, which would give artificially low values at those +c lat/lon locations (because the barnes scheme would only include +c points that were relatively far away where there was valid data). +c So in this routine, we call subroutine fix_latlon_to_ij in order +c to get the nearest (i,j) coordinates, and then we check all of +c these points to make sure that valid data exist. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing in i-direction +c dy grid spacing in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c rlatt,rlont input lat/lon about which we will check the +c surrounding (i,j) locations for valid data. +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c icvpret return code from this routine. A value of 0 means that +c all is okay and the input point is surrounded by valid +c data. + + USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,ifix,jfix + integer ifilret,icvpret + character(*) cmaxmin + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real rlont,rlatt,xdum,gridpoint_maxmin + real dx,dy,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +c + call fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt + & ,xdum,ifix,jfix,gridpoint_maxmin,'checker' + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) + + if (ifilret /= 0) then + icvpret = 99 + return + endif + + if (valid_pt(ifix,jfix)) then + icvpret = 0 + else + icvpret = 99 + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.025) then + grfact = 20 + else if (grdspc > 0.025 .and. grdspc <= 0.05) then + grfact = 12 + else if (grdspc > 0.05 .and. grdspc <= 0.10) then + grfact = 6 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif + + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (1*grfact) + iend = ipfix + (2*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (2*grfact) + iend = ipfix + (1*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (1*grfact) + iend = ipfix + (1*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (2*grfact) + jend = jpfix + (1*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (1*grfact) + jend = jpfix + (2*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (1*grfact) + jend = jpfix + (1*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +c print *,' End of fix_latlon_to_ij, gridpoint_maxmin = ' +c & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine rvcal (imax,jmax,dlon,dlat,z,vp) +c +c ABSTRACT: This routine calculates the relative vorticity (zeta) +c from u,v on an evenly-spaced lat/lon grid. Centered finite +c differences are used on the interior points and one-sided +c differences are used on the boundaries. +c +c NOTE: There are 3 critical arrays in this subroutine, the first +c being zeta and the 2nd and 3rd being u and v. There is a +c critical difference in the array indexing for the levels. For +c zeta, the array is dimensioned with levels from 1 to 3, with +c 1 = 850, 2 = 700, 3 = sfc. However, there is an extra level +c for the winds, such that the level dimension goes 1 = 850, +c 2 = 700, 3 = 500, 4 = sfc. So we need to adjust for that in +c this routine. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE trig_vals; USE grid_bounds + USE verbose_output + + implicit none + + dimension cosfac(jmax),tanfac(jmax) + real tmpzeta(imax,jmax) + real xlondiff,xlatdiff,dlon,dlat,dfix + real dlat_edge,dlat_inter,dlon_edge,dlon_inter + real rlat(jmax),cosfac,tanfac + integer z,iscanflag,nlat,nlon,i,j,imax,jmax,w + integer ii,jj + logical(1) vp(imax,jmax) + +c -------------------------- + +c Figure out what level of data we have and what the array +c indices should be. + + if (z == 1) then + ! z = 1 for 850 mb zeta, w = 1 for 850 mb winds + w = 1 + else if (z == 2) then + ! z = 2 for 700 mb zeta, w = 2 for 700 mb winds + w = 2 + else if (z == 3) then + ! z = 3 for sfc zeta, w = 4 for sfc (10m) winds + w = 4 + endif + +c Calculate grid increments for interior and edge points. + +c IMPORTANT: If dtk is defined in module trig_vals in km, then +c we need to multiply by 1000 here to get meters. If it's defined +c as meters, just let it be. Since the wind values are given in +c meters, that's why we need the dlon values to be in meters. + + if (dtk < 750.) then ! chances are, dtk was defined as km + dfix = 1000.0 + else ! dtk was already defined as meters + dfix = 1.0 + endif + + dlon_edge = dtk * dfix * dlon ! Di dist over 1 grid pt + dlat_edge = dtk * dfix * dlat ! Dj dist over 1 grid pt + dlon_inter = dtk * dfix * 2.0 * dlon ! Di dist over 2 grid pts + dlat_inter = dtk * dfix * 2.0 * dlat ! Dj dist over 2 grid pts + + +c Calculate required trig functions. These are functions of +c latitude. Remember that the grid must go from north to south. +c This north-to-south requirement has +c already been checked in subroutine getgridinfo. If necessary, +c any flipping of the latitudes was done there, and flipping of +c the data, again if necessary, was done in subroutine getdata. + + do j=2,jmax-1 + rlat(j) = glatmax - ((j-1) * dlat) + cosfac(j) = cos(dtr*rlat(j)) + tanfac(j) = (tan(dtr*rlat(j)))/erad + enddo + +c Set trig factors at end points to closest interior point +c to avoid a singularity if the domain includes the poles, +c which it will for the global grids (MRF, GDAS, GFS, UKMET,NCE) + + cosfac(1) = cosfac(2) + tanfac(1) = tanfac(2) + cosfac(jmax) = cosfac(jmax-1) + tanfac(jmax) = tanfac(jmax-1) + +c NOTE: These next bits of vorticity calculation code assume that +c the input grid is oriented so that point (1,1) is the upper +c left-most (NW) and point (imax,jmax) is the lower right- +c most point. Any other grids will probably crash the +c program due to array out of bounds errors. +c NOTE: Before each calculation is done, the logical array is +c checked to make sure that all the data points in this +c calculation have valid data (ie., that the points are not +c outside a regional model's boundaries). +c +c !!! IMPORTANT NOTE: While testing this, I uncovered a bug, which was +c that I had the "j+1" and "j-1" reversed. Just from a physical +c understanding, the du/dy term at a point is calculated by taking +c the u value north of the point minus the u value south of the +c point. Intuitively, this is u(j+1) - u(j-1). However, we have +c designed this program to have the northernmost point as +c the beginning of the grid (i.e., for the global grids, j=1 at 90N, +c and j increases southward). Thus, if you would do u(j+1) - +c u(j-1), you would actually be taking the u value south of the +c point minus the u value north of the point, EXACTLY THE OPPOSITE +c OF WHAT YOU WANT. Therefore, the vorticity calculations have +c been changed so that we now have u(j-1) - u(j+1). +c +c UPDATE FEB 2009: With limited domain grids that have missing +c data on them (such as you would have for a grid that has been +c converted from a non-lat/lon grid to a lat/lon grid), we were +c running into problems below with the setting of zeta values to +c a missing value of -999. In place of this, the easiest thing to +c do is to simply assign a value of the background coriolis value +c to that point. No, this is not correct, but it is the easiest +c workaround for this right now. Setting it to zero would be too +c far off. Setting it to the coriolis component has a net effect +c of not having much impact on the barnes scheme result. +c +c --------------- +c Interior points +c --------------- + + if ( verb .ge. 3 ) then + print *,'Just before inter rvcalc, dlon_inter = ',dlon_inter + & ,' dlat_inter = ',dlat_inter + endif + + do j=2,jmax-1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1) .and. vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo + enddo +c +c ----------------------------- +c Bottom (Southernmost) points +c ----------------------------- +c + j=jmax + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------- +c Top (Northernmost) points +c -------------------------- +c + j=1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c ------------------------------- +c Left edge (Westernmost) points +c ------------------------------- +c + i=1 + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------------- +c Right edge (Easternmost) points +c -------------------------------- +c + i=imax + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c --------- +c SW corner +c --------- + i=1 + j=jmax + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i+1,j,w)-v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NW corner +c --------- + i=1 + j=1 + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NE corner +c --------- + i=imax + j=1 + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c SE corner +c --------- + i=imax + j=jmax + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i,j,w)-v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + do ii=1,imax + do jj=1,jmax + tmpzeta(ii,jj) = zeta(ii,jj,z) * 1.e5 + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine thickness_calc (imax,jmax,vp) +c +c ABSTRACT: This routine calculates the thicknesses for three +c different layers: 200-500, 500-850 and 200-850 mb. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE verbose_output + + implicit none + + integer i,j,layer,upper,lower,imax,jmax + logical(1) vp(imax,jmax) + +c -------------------------- + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 +c +c The array indices for the levels for the 4 different GP height +c arrays (as assigned in subroutine getdata) are as follows: +c 1: 850 mb +c 2: 700 mb +c 3: 500 mb +c 4: 200 mb + + + do layer = 1,3 + + select case (layer) + case (1); upper=3; lower=1; + case (2); upper=4; lower=3; + case (3); upper=4; lower=1; + end select + + do j = 1,jmax + do i = 1,imax + + if (vp(i,j)) then + thick(i,j,layer) = hgt(i,j,upper) - hgt(i,j,lower) + else + thick(i,j,layer) = -999.0 + endif + + enddo + enddo + + enddo +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine first_ges_center (imax,jmax,dx,dy,cparm,fxy + & ,cmaxmin,trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) +c +c ABSTRACT: This subroutine scans an array and picks out areas of +c max or min, then loads those center positions into the first- +c guess lat & lon arrays to be used by subroutine tracker for +c locating the very specific low center positions. +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c dx Grid spacing in i-direction for the input grid +c dy Grid spacing in j-direction for the input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c finf Logical. Field of influence. Dimension same as fxy +c cmaxmin Char string to indicate if search is for a max or a min +c trkrinfo Derived type that holds/describes various tracker parms, +c including the contour interval to be used +c ifh Index for the forecast hour +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c contour_info Type cint_stuff from module contours. Contains +c contour information +c +c OUTPUT: +c maxmini Integer array containing i-indeces of max/min locations +c maxminj Integer array containing j-indeces of max/min locations +c ifgcret return code from this subroutine +c +c OTHER: +c storm Contains the tcvitals for the storms (module def_vitals) + + USE trkrparms; USE grid_bounds; USE set_max_parms; USE def_vitals + USE contours; USE tracked_parms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,n,isstart,ifamret,ibeg,jbeg,iend,jend + integer ifh,maxstorm,imax,jmax,itemp,ifgcret + integer stormct,oldstormct,mm + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + character(*) cparm,cmaxmin + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real dmax,dmin,dx,dy,dbuffer,tmp + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of first_ges_center *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for new lows at hour ',i4,':',i2.2) + print *,'*-------------------------------------------------*' + endif + + +c First check the user-supplied grid boundaries to see if we will +c scan the entire array or just a portion of it. + + if (trkrinfo%northbd < -998.0 .or. trkrinfo%southbd < -998.0 .or. + & trkrinfo%westbd < -998.0 .or. trkrinfo%eastbd < -998.0) then + ! User did not specify a subgrid, so scan the whole domain + ibeg = 1 + iend = imax + jbeg = 1 + jend = jmax + else + +c if (trkrinfo%westbd > 360.0 .or. trkrinfo%eastbd < 0.0 .or. +c & trkrinfo%westbd < 0.0 .or. + + if (trkrinfo%westbd > 360.0 .or. + & trkrinfo%northbd > 90.0 .or. trkrinfo%northbd <-90.0 .or. + & trkrinfo%southbd > 90.0 .or. trkrinfo%southbd <-90.0 .or. + & trkrinfo%westbd >= trkrinfo%eastbd .or. + & trkrinfo%southbd >= trkrinfo%northbd) then + + if (trkrinfo%westbd > trkrinfo%eastbd) then + + if (trkrinfo%westbd < 360.0 .and. + & trkrinfo%eastbd >= 0.0)then + + ! In this special case, the user has specified that the + ! western boundary be to the west of the Greenwich + ! meridian and the eastern boundary be to the east of it. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE: The user supplied grid lon boundaries' + print *,'++ span across the Greenwich meridian.' + print *,'++ ' + print *,'++ Western boundary: ',trkrinfo%westbd + print *,'++ Eastern boundary: ',trkrinfo%eastbd + print *,'++ Northern boundary: ',trkrinfo%northbd + print *,'++ Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ! Calculate the beginning and ending i and j points for + ! this case of spanning the Greenwich meridian. The + ! beginning and ending j points are, obviously, the same + ! as for the regular case below in the else. The + ! i-beginning point will also be the same as for the + ! regular case. However, the i-ending point will be + ! modified for the meridian wrap; it will be > imax. + + jbeg = int(((glatmax + dy - trkrinfo%northbd) + & / dy) + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) + & / dy) + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) + & / dx) + 0.5) +c iend = int(((trkrinfo%eastbd - glonmin + dx) +c & / dx) + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) + & / dx) + 0.5) + imax + + goto 377 + + endif + endif + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. There is a' + print *,'!!! problem with the user-supplied grid ' + print *,'!!! boundaries. Please check them and ' + print *,'!!! resubmit the program.' + print *,'!!!' + print *,'!!! Western boundary: ',trkrinfo%westbd + print *,'!!! Eastern boundary: ',trkrinfo%eastbd + print *,'!!! Northern boundary: ',trkrinfo%northbd + print *,'!!! Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ifgcret = 91 + return + + 377 continue + + else + ! Calculate the beginning and ending i and j points.... + jbeg = int(((glatmax + dy - trkrinfo%northbd) / dy) + & + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) / dy) + & + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) / dx) + & + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) / dx) + & + 0.5) + endif + endif + +c Scan the requested portion of the grid and pick out the max and +c min data values, figure out what the max and min contour levels +c will be, and fill an array with the values of the various +c intermediate, incremental contour levels. + + if (trkrinfo%contint <= 0) then + + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. For a midlat' + print *,'!!! or tcgen run of the tracker, the contour' + print *,'!!! interval supplied by the user is not ' + print *,'!!! greater than 0.' + print *,'!!! ' + print *,'!!! User-supplied contint = ',trkrinfo%contint + print *,' ' + endif + + ifgcret = 91 + return + endif + + dmin = 9.99e20 + dmax = -9.99e20 + + do j = jbeg,jend + do i = ibeg,iend + if (i > imax) then + itemp = i - imax ! If wrapping past GM + else + itemp = i + endif + if (valid_pt(itemp,j)) then + if (fxy(itemp,j) < dmin) dmin = fxy(itemp,j) + if (fxy(itemp,j) > dmax) dmax = fxy(itemp,j) + endif + enddo + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*--------------------------------------------*' + print *,'In first_ges_center, dmin= ',dmin,' dmax= ',dmax + endif + + +c We want to allow for storms moving out of the sub-region, +c in which case we might hit slightly lower or higher +c contours than were found in the sub-region, so allow for +c an extra buffer and modify dmin and dmax.... + + dbuffer = (dmax - dmin) / 2.0 + dmax = dmax + dbuffer + dmin = dmin - dbuffer + + if ( verb .ge. 3 ) then + print *,'after adjustment, dmin= ',dmin,' dmax= ',dmax + endif + +c Next 2 lines changed for compiler compatibility on +c other platforms.... +c contour_info%xmaxcont = dmax - amod(dmax,trkrinfo%contint) +c contour_info%xmincont = dmin - amod(dmin,trkrinfo%contint) + + tmp = trkrinfo%contint + contour_info%xmaxcont = dmax - mod(dmax,tmp) + contour_info%xmincont = dmin - mod(dmin,tmp) + + if ( verb .ge. 3 ) then + print *,'A1 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A1 contour_info%xmincont= ',contour_info%xmincont + endif + + if (contour_info%xmincont > contour_info%xmaxcont) then + contour_info%xmincont = contour_info%xmaxcont + endif + +c if (dmin > contour_info%xmincont) then +c contour_info%xmincont=contour_info%xmincont + trkrinfo%contint +c endif +c if (dmax < contour_info%xmaxcont) then +c contour_info%xmaxcont=contour_info%xmaxcont - trkrinfo%contint +c endif + + if ( verb .ge. 3 ) then + print *,'A2 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A2 contour_info%xmincont= ',contour_info%xmincont + print *,'maxconts= ',maxconts + endif + +c NOTE: In the loop below, the contour_info%contvals array is now +c (5/2003) no longer used in subsequent subroutines. But we still +c need to figure out the value of the contvals as we iterate the +c loop so we can know when we've surpassed dmax and can stop +c incrementing contour_info%numcont, which we do need in subsequent +c subroutines. + + contour_info%numcont = 0 + do n = 1,maxconts + contour_info%numcont = contour_info%numcont + 1 + contour_info%contvals(n) = contour_info%xmincont + + & float(n-1)*trkrinfo%contint +c print *,'n= ',n,' contour_info%contvals(n)= ' +c & ,contour_info%contvals(n) + if (contour_info%contvals(n) >= dmax) exit + enddo + + oldstormct = stormct + call find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) + + if (stormct > 0) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' ' + print *,'!!! ************************************************' + print *,'!!! ' + print *,'!!! NOTE: In first_ges_center, the value of stormct' + print *,'!!! returned from find_all_maxmins is not greater' + print *,'!!! than 0. This means there are no new centers' + print *,'!!! to track, which is not likely. Perhaps you are' + print *,'!!! searching over too small of an area??' + print *,'!!! ' + print *,'!!! ************************************************' + print *,' ' + endif + + endif + + print *,'ifh= ',ifh,' oldstormct= ',oldstormct + print *, ' stormct= ',stormct + + do mm = 1,300 + print *,'mm= ',mm,' maxmini(mm)= ',maxmini(mm) + & ,' maxminj(mm)= ',maxminj(mm) + enddo + + if (stormct > oldstormct .and. stormct > 0) then + isstart = oldstormct + 1 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,*) 'New search: ' + write (6,*) 'Possible new max/min locations at ifh= ',ifh + write (6,*) '--------------------------------------------' + endif + + do n = isstart,stormct + if (trkrinfo%type == 'midlat') then + storm(n)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(n)%tcv_center = 'TCG ' + endif + slonfg(n,ifh) = glonmin + (maxmini(n)-1)*dx + slatfg(n,ifh) = glatmax - (maxminj(n)-1)*dy + storm(n)%tcv_stspd = -99 + storm(n)%tcv_stdir = -99 + write (storm(n)%tcv_storm_id,'(i4.4)') n + write (storm(n)%tcv_storm_name,'(i4.4)') n + stormswitch(n) = 1 + if (cparm == 'mslp') then + + if ( verb .ge. 3 ) then + write (6,71) maxmini(n),maxminj(n),slonfg(n,ifh) + & ,360.-slonfg(n,ifh),slatfg(n,ifh) + & ,slp(maxmini(n),maxminj(n))/100.0 + endif + + endif + enddo + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' New search: ' + print *,'!!! NOTE: No new storms found in find_all_maxmins' + print *,'!!! at ifh = ',ifh,' stormct= ',stormct + print *,'!!! oldstormct= ',oldstormct + print *,' ' + endif + + endif + + 71 format (1x,'i= ',i4,' j= ',i4,' lon: ',f7.2,'E (',f6.2,'W)' + & ,2x,' lat: ',f6.2,' mslp: ',f6.1,' mb') +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) +c +c ABSTRACT: This subroutine will search an area delineated by +c input i and j indeces in order to find all local maxes or mins +c in that area. The (i,j) locations of the maxes/mins are returned +c in the maxmini and maxminj arrays. The input 3-character string +c cmaxmin will tell the subroutine to look for a "max" or a "min". +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c ibeg i-index for upper left location of grid to search +c iend i-index for lower right location of grid to search +c jbeg j-index for upper left location of grid to search +c jend j-index for lower right location of grid to search +c fxy Real array of data values +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c contour_info Type cint_stuff from module contours containing the +c the following 4 variables: +c 1. xmincont Real value for min contour level in the fxy data array +c 2. xmaxcont Real value for max contour level in the fxy data array +c 3. contvals Real array holding values of cont levels at this time +c 4. numcont Number of contour intervals found at this time +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c trkrinfo derived type containing various user-input tracker parms +c cmaxmin String that declares if "min" or "max" is being searched +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c +c OUTPUT: +c maxmini integer array containing i-indeces of the max/min points +c maxminj integer array containing j-indeces of the max/min points +c ifamret return code from this subroutine + + USE trkrparms; USE set_max_parms; USE contours + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + integer stormct,i,j,ibeg,iend,jbeg,jend,ix,jx,ixp1,ixm1 + integer ip,jp,maxstorm,jxp1,jxm1,ifamret,isret,iaret,iclmret + integer isoiret,icccret,igicwret,imax,jmax + character ccflag*1,get_last_isobar_flag*1,point_is_over_water*1 + character(*) cmaxmin + logical(1) still_finding_valid_maxmins,rough_gradient_check_okay + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real xavg,stdv,search_cutoff,dmin,dmax,sphere_cutoff + real plastbar,rlastbar,fract_land,dx,dy + +c----- + still_finding_valid_maxmins = .true. + + +c print *,'ctm beg of find_all_maxmins, maxstorm= ',maxstorm + + +c First, we want to get the mean and standard deviation of the input +c field to be searched. We can use the standard deviation info as +c part of our guideline for when to stop searching for maxes & mins. +c We will set the search cut-off threshold at 1/2 standard deviation +c above the mean for min searches. So, for the example of mslp, if +c the mean pressure over the whole domain is 1010 mb and the +c standard deviation is 12 mb, then when we are searching, if the +c lowest available (i.e., hasn't been found in a previous iteration +c of this loop) pressure is 1016, then it's time to stop searching. + + call avgcalc (fxy,imax*jmax,valid_pt,xavg,iaret) + call stdevcalc (fxy,imax*jmax,valid_pt,xavg,stdv,isret) + if (iaret /= 0 .or. isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_all_maxmins, the calls to avgcalc' + print *,'!!! and/or stdevcalc returned an error.' + print *,'!!! iaret= ',iaret,' isret= ',iaret + print *,' ' + endif + + ifamret = 98 + return + endif + + if (cmaxmin == 'min') then + search_cutoff = xavg + stdv*0.5 + else + search_cutoff = xavg - stdv*0.5 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In find_all_maxmins, search_cutoff= ',search_cutoff + print *,' ' + endif + +c Now begin to search the domain. We do a simple gridpoint scan, +c and once we find the max/min value, we pass the (i,j) coordinates +c at that point to a routine to check for a closed contour. Then +c we mask out those points in the contour (or, if there is not a +c closed contour, just the 8 points immediately surrounding the low +c center) and we do another iteration of search_loop to look for +c more lows. We mask out points we've found so that on subsequent +c iterations of search_loop, we don't find the same old center +c again and again and again..... + + search_loop: do while (still_finding_valid_maxmins) + + dmin = 9.99e20 + dmax = -9.99e20 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + ip = i + jp = j + + if (ip > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: In find_all_maxmins, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. The search' + print *,'!!! will not extend to the user-requested' + print *,'!!! grid boundary.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',ip + print *,' ' + endif + + exit iloop + + endif + endif + + if (valid_pt(ip,jp) .and..not. masked_out(ip,jp)) then + if (cmaxmin == 'min') then + if (fxy(ip,jp) < dmin) then + dmin = fxy(ip,jp) + ix = ip + jx = jp + endif + else + if (fxy(ip,jp) > dmax) then + dmax = fxy(ip,jp) + ix = ip + jx = jp + endif + endif + endif + + enddo iloop + enddo jloop + + if (cmaxmin == 'min') then + if (dmin < search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + else + if (dmax > search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + endif + +c As a rough first check, see if the neighboring points on all +c 4 sides have a gradient sloping down into the found min point, +c or at least that there is a flat field not having a gradient +c sloping away from the center point. + + call get_ijplus1_check_wrap (imax,jmax,ix,jx,ixp1,jxp1,ixm1 + & ,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In find_all_maxmins, the center we found' + print *,'!!! is too close to the grid boundary and will' + print *,'!!! NOT be checked for a closed contour.' + print *,'!!! ix= ',ix,' jx= ',jx,' fxy= ',fxy(ix,jx) + print *,'!!! ' + print *,' ' + endif + + masked_out(ix,jx) = .true. + cycle search_loop + endif + + if (cmaxmin == 'min') then + if (fxy(ix,jx) <= fxy(ixp1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxm1) .and. + & fxy(ix,jx) <= fxy(ixm1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + else + if (fxy(ix,jx) >= fxy(ixp1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxm1) .and. + & fxy(ix,jx) >= fxy(ixm1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + endif + + if (rough_gradient_check_okay) then + + if ( verb .ge. 3 ) then + print *,'Found a possible max/min at ix= ',ix,' jx= ',jx + endif + + +c From this rough check, we appear to have a gradient sloping +c in towards the center point. Now call the subroutine to +c check whether or not there is in fact a closed contour +c surrounding this local maximum or minimum. + + get_last_isobar_flag = 'n' + ccflag = 'n' + call check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,ccflag,cmaxmin,trkrinfo + & ,1,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if (ccflag == 'y') then + if (stormct < maxstorm) then + stormct = stormct + 1 + + if ( verb .ge. 3 ) then + print *,'AAA stormct= ',stormct,' ix= ',ix,' jx= ',jx + endif + + ! For a tcgen case, we will add in one additional check, + ! and that is to ensure the point is (mostly) over water. + ! Only do this check if the user has requested it (some + ! of the global models do not have a land-sea mask + ! included in the grib data files). + + point_is_over_water = 'u' + + if (trkrinfo%use_land_mask == 'y') then + call check_land_mask (imax,jmax,ix,jx,fract_land + & ,valid_pt,dx,dy,point_is_over_water,iclmret) + if (iclmret /= 0) then + print *,' ' + print *,'!!! ERROR from check_land_mask for ix= ',ix + & ,' jx= ',jx + print *,'!!! STOPPING PROGRAM' + stop 95 + endif + endif + + if (point_is_over_water /= 'n') then + maxmini(stormct) = ix + maxminj(stormct) = jx + endif + + else + + if ( verb .ge. 3 ) then + print *,'---max stormct reached, stormct= ', stormct + endif + + endif + else + + if ( verb .ge. 3 ) then + print *,'!!! contour check negative, ccflag= ',ccflag + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*-----------------------------------------------*' + print *,' ' + endif + + endif + +c Regardless of whether or not the found point turns out to have +c a closed contour, we don't want to find this local minimum or +c its 8 surrounding points again in a search on a subsequent +c iteration of this loop. + + masked_out(ix,jx) = .true. + masked_out(ix,jxp1) = .true. + masked_out(ixp1,jxp1) = .true. + masked_out(ixp1,jx) = .true. + masked_out(ixp1,jxm1) = .true. + masked_out(ix,jxm1) = .true. + masked_out(ixm1,jxm1) = .true. + masked_out(ixm1,jx) = .true. + masked_out(ixm1,jxp1) = .true. + + enddo search_loop + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine mask_based_on_wind_circ (imax,jmax,dx,dy,level + & ,valid_pt,masked_outc,trkrinfo + & ,ctlon,ctlat,cmodel_type,imbowret) +c +c ABSTRACT: This subroutine masks out grid points for a storm that +c is currently being tracked. It is called after a fix has been +c made at the current forecast hour. It is only used as a backup, +c that is, if the mslp data were not there and/or a fix position +c for mslp could not be made, then that means that the mask would +c not be able to get updated using the routine in subroutine +c check_closed_contour. But we still do need to update that mask, +c so we will instead do it based on wind circulation. We will go +c out radially from the center, starting at 40 km, then every +c 40 km from there on out. When the mean cyclonic Vt drops below +c 3 m/s, stop searching, and then mask out all grid points within +c that last-searched radius. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_outc Logical. T = data point is already accounted for, +c under the influence of another nearby max or min +c center; F = data point is available to be scanned by +c this subroutine for max or min centers. +c ctlon Fix longitude for the input parameter to this routine +c ctlon Fix latitude for the input parameter to this routine +c cmodel_type character, 'global' or 'regional' + + USE set_max_parms; USE trkrparms; USE grid_bounds + USE verbose_output; USE level_parms + + implicit none + + type (trackstuff) trkrinfo + + character(*) cmodel_type + integer, parameter :: numazim=24 + integer imax,jmax,level,imbowret,nlev,iazim,i,j + integer ibiret1,ibiret2,azimuth_ct,igvtret + integer jnfix,jsfix,iefix,iwfix + real vr(numazim),vt(numazim) + real dx,dy,ctlon,ctlat,rdist,bear,targlat,targlon + real xintrp_u,xintrp_v,grid_buffer,xmax_rdist_reached + real vt_mean,vt_azim_sum,xbear,dist,degrees + logical(1) valid_pt(imax,jmax),masked_outc(imax,jmax) + logical(1) searching_valid_pts + + imbowret = 0 + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + searching_valid_pts = .true. + + rdist = 40.0 ! units in km + xmax_rdist_reached = rdist ! units in km + + radial_loop: do while (searching_valid_pts) + + azimuth_ct = 0 + vt_azim_sum = 0.0 + vt = -999.0 + vr = -999.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (ctlat,ctlon,rdist,bear,targlat,targlon) + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + xmax_rdist_reached = rdist + exit radial_loop + endif + + ! These calls to bilin_int_uneven pass a variable, level, + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (ctlon,ctlat,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim) + & ,vt(iazim),igvtret) + azimuth_ct = azimuth_ct + 1 + vt_azim_sum = vt_azim_sum + vt(iazim) + else + xmax_rdist_reached = rdist + exit radial_loop + endif + + enddo azimloop + + if (azimuth_ct > 0) then + ! Compute azimuthally-averaged Vt at this distance + vt_mean = vt_azim_sum / float(azimuth_ct) + else + vt_mean = -999.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: rdist= ',rdist,' azimuth_ct= ',azimuth_ct + & ,' vt_azim_sum= ',vt_azim_sum,' vt_mean= ',vt_mean + endif + + if (ctlat >= 0.0) then + if (vt_mean >= 3.0) then + ! For a NH storm, if the cyclonic mean Vt >= 3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + else + if (vt_mean <= -3.0 .and. vt_mean > -998.0) then + ! For a SH storm, if the cyclonic mean Vt <= -3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + enddo radial_loop + + if ( verb .ge. 3 ) then + print *,'mbow: After radial_loop, rdist= ',rdist + & ,' xmax_rdist_reached= ',xmax_rdist_reached + endif + +c ----------------------------------------------------------------- +c At this point, we are done searching radially outwards away from +c the storm center. The max radial distance we reached is called +c xmax_rdist_reached. By getting to this spot in the subroutine, +c that means that we bumped out of radial_loop above because the +c rdist being used in that loop got to a radius at which the mean +c cyclonic Vt no longer was strong enough to continue the search +c outward, so we need to reduce it by 40 km here (back to the value +c for the last successful search). At a minimum, we will mask to a +c radius of 80 km. + + if (xmax_rdist_reached > 80.0) then + xmax_rdist_reached = xmax_rdist_reached - 40.0 + else + xmax_rdist_reached = 80.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: After adjustment of xmax_rdist_reached, rdist= ' + & ,rdist,' xmax_rdist_reached= ',xmax_rdist_reached + endif + + bearloop: do i = 1,4 + + ! Now find the values of the longitude for the farthest west + ! and east points and find the values of the latitude for the + ! farthest north and south points. The i and j indices + ! associated with these lons and lats will be used to define + ! the bounds of the grid over which we scan to find points + ! that will update the mask. + + select case (i) + case (1); xbear = 0.0; + case (2); xbear = 90.0; + case (3); xbear = 180.0; + case (4); xbear = 270.0; + end select + + call distbear (ctlat,ctlon,xmax_rdist_reached,xbear + & ,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,'mbow: distbear for i= ',i,' targlon= ',targlon + & ,' targlat= ',targlat + endif + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon > glonmax for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmax= ',glonmax + imbowret = 95 + return + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon < glonmin for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmin= ',glonmin + imbowret = 95 + return + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'either targlat > glatmx or targlat < glatmin, so' + print *,'we cannot update the mask.' + print *,'targlat= ',targlat,' glatmin= ',glatmin + print *,' glatmin= ',glatmin + imbowret = 95 + return + cycle bearloop + endif + + ! Get the i & j starting and ending points for our loop where + ! we will update the mask.... + + if (i == 1) then + + ! Get j for northern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jnfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jnfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 2) then + + ! Get i for eastern longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iefix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + elseif (i == 3) then + + ! Get i for southern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jsfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jsfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 4) then + + ! Get i for western longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iwfix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + endif + + enddo bearloop + + if ( verb .ge. 3 ) then + print *,'mbow: iwfix= ',iwfix,' iefix= ',iefix + & ,' jnfix= ',jnfix,' jsfix= ',jsfix + endif + + do i = iwfix,iefix + do j = jnfix,jsfix + + call calcdist (glon(i),glat(j),ctlon,ctlat,dist,degrees) + + if (dist < xmax_rdist_reached) then + masked_outc(i,j) = .true. + endif + + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,closed_contour,cmaxmin,trkrinfo + & ,num_requested_contours,contour_info + & ,get_last_isobar_flag,plastbar,rlastbar,icccret) +c +c ABSTRACT: This subroutine checks a field of data around an input +c (ix,jx) data point to see if a closed contour exists around +c that data point. It can check for a closed contour on a max or a +c min field, depending on the value of the input variable 'cmaxmin'. +c The algorithm works by examining rings of the 8 data points +c surrounding a data point that is in the contour interval. For +c example, in the diagram below, the X represents the location of +c the local minimum value which was passed into this routine with +c the coordinates (ix,jx), let's say it's 985 mb. And let's assume +c that the data values at points A-I are all in the 4 mb contour +c interval of 985-989 mb, and that all the surrounding points have +c data values >= 989. To test for a closed contour, we first check +c the ring of 8 points immediately around point X to see what their +c data values are. If a data value is found that is below the +c lower limit of this contour interval (985 mb) or lower than the +c local minimum value at the X point that we initially targeted +c (985 mb), then we do NOT have a closed contour, and we exit this +c subroutine. But in our example, that's not the case, and we have +c 5 points (B,D,E,F,G) that are in the interval. So in our next +c iteration of the loop, we set up 5 rings, each one set up around +c the points found in the first iteration (B,D,E,F,G), and we check +c the 8 points around each of those points. A logical array is +c used so that as soon as a point is found, it is flagged as being +c found. In this way, when we look at the ring around point D, for +c example, we won't pick point X again and set up another ring +c around it in the next ring iteration and end up in an infinite +c loop, going back and forth between point X and point D. While +c checking the 8 points in a ring, if a found data value is above +c our contour interval (i.e., >= 989 mb), we just ignore the +c point; we only mark points that are in our contour interval, +c and again, if we find a point below our contour interval, we +c exit the subroutine with a flag indicating a closed contour was +c NOT found. So in this method, we keep spreading out from the +c initial local minimum and creating and checking new rings until +c we either: (a) Hit the edge of the regional grid, in which case +c we consider a closed contour NOT found, (b) Run into a data +c point that has been marked as being under the influence of +c another nearby low, in which case we consider a closed contour +c NOT found, (c) Run into a point which is below (above) our +c contour interval for a min (max) check, in which case we +c consider a closed contour NOT found, or (d) we run out of +c points to keep searching, we have no rings left to create and +c check because all of the surrounding points are above (below) +c our contour interval for a min (max) check, and by default we +c consider this a closed contour and return to the calling +c subroutine a flag indicating such. +c +c + + + + + + + + + + +c + + + + + + + + + + +c + + A B + + + + + + +c + + C D X E + + + + +c + + + + F G + + + + +c + + + + + H I + + + +c + + + + + + + + + + +c + + + + + + + + + + +c +c UPDATE: This subroutine was updated to keep searching for +c multiple closed contours until it can't find anymore. The +c input parameter num_requested_contours dictates how many +c contours to search for. In the case of just trying to roughly +c locate new centers and establish that there is a closed +c circulation, num_requested_contours will = 1, and we will exit +c after finding that 1 contour. But for a check after making a +c full center fix, we set num_requested_contours = 999 so that +c we can keep searching for all closed contours around the low. +c In this 999 case, you will eventually get to a point where +c there is no closed contour. In that case, in the standard +c output you will see a message telling you that you hit a point +c that is not in the contour and that there is no closed contour, +c but you will also notice that the ccflag = y, meaning there is +c a closed contour (because you have found at least 1 closed +c contour along the way). The reason to keep searching for more +c closed contours is that we can then return the value of the +c outermost closed isobar. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c cmaxmin character string ('max' or 'min') that tells this +c routine what we're looking for. +c trkrinfo derived type that holds/describes various tracker parms +c contour_info Type cint_stuff from module contours. Contains +c contour information +c num_requested_contours For the simple first_ges_center check, +c this will be 1 (we just want to know if there's at +c least 1 closed contour). For the verifying check after +c we've found a center, this will be 9999 (i.e., just keep +c searching for more contours) +c get_last_isobar_flag character ('y' or 'n') to indicate whether +c or not to report on the value of the last closed isobar +c and the radius of the last closed isobar. +c +c OUTPUT: +c closed_contour character; A returned value of 'y' indicates that +c this routine was able to find a closed contour. +c plastbar Contains the value of the last closed isobar (unrounded) +c rlastbar Contains the mean radius of the last closed isobar +c +c LOCAL: +c num_pts_in_all_contours Counter for the number of pts inside of +c the contour we're looking at +c next_ring_ct Counter for the number of points that have been +c tagged to be used as center points for the next +c iteration of multiple_ring_loop. +c next_contour_ct Counter for the number of points that have been +c tagged to be used as center points in the first iteration +c through single_contour_scan_loop as we begin to scan +c points in the *next* contour interval. This counter gets +c incremented when, for example, we are searching points +c around a current center point and we find one that is not +c in our current interval, but rather is in the next +c interval. We want to remember this point and store the +c location, so we increment this counter and store the +c location in next_contour_i and next_contour_j arrays. +c beyond_contour_ct Counter for the number of points that have been +c tagged to be used as center points for some subsequent +c iteration of successive_contours_loop. This is +c different from next_contour_ct, which is used to hold +c the locations of points that are definitely in the +c *next* contour interval. Here, we have points that we +c just store in a pool of potential points to be searched +c in future iterations. These points can come about in +c cases where there is a very intense, very compact low +c with a tight pressure gradient, such that multiple +c contour intervals could be spanned in between 2 adjacent +c gridpoints (this is especially the case if the contour +c interval you have chosen is small). You need to be +c careful with how you handle this array. Once you find +c that you have searchable points in next_contour_i or +c next_contour_j, do not just simply empty out this +c beyond_contour count and its i and j arrays. The +c reason being that some of these "beyond" points may end +c up being used and searched in subsequent iterations, but +c not if we just delete them now. + + + USE set_max_parms; USE trkrparms; USE contours; USE grid_bounds + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,ir,iria,irja,irx,jrx,ix,jx,imax,jmax + integer nb,ibx,jby,nct,iflip + integer mr,ringct,ixp1,ixm1,jxp1,jxm1,nring,iter + integer icenx,jcenx,icccret,next_ring_ct,igicwret + integer num_pts_in_all_contours,next_contour_ct + integer beyond_contour_ct + integer num_pts_in_one_contour + integer num_requested_contours,num_found_contours + integer nm,im,jm,inall,insingle,isc_count,rlast_distct + character found_a_point_in_our_contour*1,closed_contour*1 + character found_a_point_below_contour*1 + character found_a_point_above_contour*1,get_last_isobar_flag*1 + character(*) cmaxmin + logical(1) still_scanning + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + logical(1) point_is_already_in_our_contour(imax,jmax) + logical(1) point_is_already_in_next_contour(imax,jmax) + logical(1) point_is_already_in_beyond_pool(imax,jmax) + integer isni,isnj,inci,incj,ibci,ibcj,ihmi,ihmj,itmi,itmj + integer, allocatable :: search_next_i(:) + integer, allocatable :: search_next_j(:) + integer, allocatable :: next_contour_i(:) + integer, allocatable :: next_contour_j(:) + integer, allocatable :: beyond_contour_i(:) + integer, allocatable :: beyond_contour_j(:) + integer, allocatable :: hold_mask_i_loc(:) + integer, allocatable :: hold_mask_j_loc(:) + integer, allocatable :: temp_mask_i_loc(:) + integer, allocatable :: temp_mask_j_loc(:) + integer, allocatable :: ringposi(:),ringposj(:) + real,allocatable :: ringpos(:,:) + real fxy(imax,jmax),contvals(maxconts) + real contlo,conthi,xcentval,contlo_next,conthi_next + real dist,degrees,rlast_distsum,plastbar,rlastbar +c + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + allocate (search_next_i(imax*jmax),stat=isni) + allocate (search_next_j(imax*jmax),stat=isnj) + allocate (next_contour_i(imax*jmax),stat=inci) + allocate (next_contour_j(imax*jmax),stat=incj) + allocate (beyond_contour_i((imax*jmax)/2),stat=ibci) + allocate (beyond_contour_j((imax*jmax)/2),stat=ibcj) + allocate (hold_mask_i_loc(imax*jmax),stat=ihmi) + allocate (hold_mask_j_loc(imax*jmax),stat=ihmj) + allocate (temp_mask_i_loc(imax*jmax),stat=itmi) + allocate (temp_mask_j_loc(imax*jmax),stat=itmj) + if (isni /= 0 .or. isnj /= 0 .or. inci /= 0 .or. incj /= 0 .or. + & ibci /= 0 .or. ibcj /= 0 .or. ihmi /= 0 .or. ihmj /= 0 .or. + & itmi /= 0 .or. itmj /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various search, hold and temp arrays.' + print *,'!!! isni = ',isni,' isnj= ',isnj + print *,'!!! inci = ',inci,' incj= ',incj + print *,'!!! ibci = ',ibci,' ibcj= ',ibcj + print *,'!!! ihmi = ',ihmi,' ihmj= ',ihmj + print *,'!!! itmi = ',itmi,' itmj= ',itmj + print *,' ' + endif + + STOP 98 + endif + + closed_contour = 'n' + xcentval = fxy(ix,jx) + num_found_contours = 0 + next_contour_ct = 0 + beyond_contour_ct = 0 + num_pts_in_all_contours = 0 + hold_mask_i_loc = 0 + hold_mask_j_loc = 0 + beyond_contour_i = 0 + beyond_contour_j = 0 + point_is_already_in_our_contour = .false. + point_is_already_in_beyond_pool = .false. + icccret = 0 + isc_count = 0 + plastbar = -999.0 + rlastbar = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* Top of check_closed_contour, ix= ',ix,' jx= ',jx + print *,'*-----------------------------------------------*' + print *,' ' + print *,'fxy(ix,jx)= ',fxy(ix,jx),' xcentval= ',xcentval + endif + +c First, set up the contour intervals that will be used. In +c the original version of this code, we used preset +c standard intervals (984,988,992,996,1000,1004....). But upon +c further review, it was decided that this was too arbitrary. +c So instead, we consider the found min (max) value to be the +c bottom (top) of the list of contour intervals. In this way, +c we can clearly specify and screen storms based on the "depth" +c of the pressure field as compared to the surroundings. + + i = 1 + do while (i <= maxconts) + if (cmaxmin == 'min') then + contvals(i) = xcentval + float(i-1)*trkrinfo%contint + i = i + 1 + else + iflip = maxconts - i + 1 + contvals(iflip) = xcentval - float(i-1)*trkrinfo%contint + i = i + 1 + endif + enddo + +c This successive_contours loop is the master loop.... + + successive_contours_loop: do while (num_found_contours < + & num_requested_contours) + +c Find the contour interval in which the center value resides. +c Note that the lower bound is included for a min check, while +c the upper bound is included for a max check. Note also that +c this subroutine can be used to find the last closed contour, +c and part of that functionality shows up in the next while +c statement where we reference "num_found_contours" in the +c array indeces for the contour values. Basically, the way we +c do this is, for example, if our central value is 990.4 mb and +c our contour interval is 4 mb, then in the first run through +c successive_contours_loop we see if we have a closed contour in +c the interval 990.4-994.4. If yes, then the next time through +c this loop, we see if we have a closed contour in the interval +c 994.4-998.4. If yes, then the next loop check is for 998.4- +c 1002.4, and so on.... We stop searching if we find a value +c that is either below the xcentval input into this subroutine +c or below the lower value of the current contour interval (this +c would mean a change in the gradient and would indicate that, +c in the case of mslp, we are heading down towards another, +c different low). + + isc_count = isc_count + 1 + + point_is_already_in_next_contour = .false. + + i = 1 + do while (i < maxconts) + if (cmaxmin == 'min') then + if (contvals(i) <= xcentval .and. xcentval < contvals(i+1)) + & then + + if ( verb .ge. 3 ) then + print *,'At A, num_found_contours= ',num_found_contours + endif + + contlo = contvals(i+num_found_contours) + conthi = contvals(i+1+num_found_contours) + + if ( verb .ge. 3 ) then + print *,'At A, contlo= ',contlo,' conthi= ',conthi + endif + exit + + endif + else + if (contvals(i) < xcentval .and. xcentval <= contvals(i+1)) + & then + contlo = contvals(i-num_found_contours) + conthi = contvals(i-num_found_contours+1) + exit + endif + endif + i = i + 1 + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'num_found_contours= ',num_found_contours + print *,'contlo= ',contlo,' conthi= ',conthi + print *,'xcentval= ',xcentval + endif + + +c This single_contour_scan_loop is the main loop for searching +c for one individual contour. If it is determined that a contour +c exists, control is returned to the successive_contours_loop, +c and if more contours were requested to be found, then the +c search continues onward & outward.... + + temp_mask_i_loc = 0 + temp_mask_j_loc = 0 + + iter = 1 + num_pts_in_one_contour = 0 + still_scanning = .true. + + rlast_distsum = 0.0 + rlast_distct = 0 + + single_contour_scan_loop: do while (still_scanning) + +c print *,' ' +c print *,' top of single contour scan loop' +c print *,'+++ iter= ',iter +c print *,' N1: next_contour_ct= ',next_contour_ct + + if (iter == 1 .and. num_found_contours == 0) then + ! For the first iteration, we have only the first ring, + ! which is centered on the input minimum/maximum point. + ringct = 1 + search_next_i(1) = ix + search_next_j(1) = jx + +c point_is_already_in_our_contour(ix,jx) = .true. +c num_pts_in_one_contour = num_pts_in_one_contour + 1 +c temp_mask_i_loc(num_pts_in_one_contour) = ix +c temp_mask_j_loc(num_pts_in_one_contour) = jx + + else if (iter == 1 .and. num_found_contours > 0) then + ! This is the first iteration in a *new* contour. + ! That is, we have already found 1 or more previous + ! contours while in previous iterations of + ! successive_contours_loop and we are now beginning + ! to look for the next contour. + +c print *,' N2: next_contour_ct= ',next_contour_ct + + if (next_contour_ct == 0) then + ! This would be for the special case in which, for + ! example, you've got a very intense, compact storm + ! that "skips" a contour. That is, suppose the + ! min pressure of a storm is 982 mb, and we are + ! utilizing a 4-mb contour interval, but all + ! surrounding data points are, say, 987 mb or + ! higher. Then, next_contour_ct would be 0 since no + ! data points were found in the next contour interval + ! of 982-986 mb, but we can continue searching since the + ! gradient is still sloping the correct way. The code in + ! this if statement handles this special case. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ALERT: next_contour_ct = 0 ' + endif + + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + +c print *,'b4 ZZ, ringct= ',ringct +c print *,'at ZZ, bcc= ',beyond_contour_ct +c & ,'contlo_next= ',contlo_next +c & ,'conthi_next= ',conthi_next + + bey_con_min_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_min_loop + endif + +c print *,'-- ZZ, ibx= ',ibx,' jby= ',jby +c & ,' fxy(ibx,jby)= ',fxy(ibx,jby) + + if (fxy(ibx,jby) >= contlo_next .and. + & fxy(ibx,jby) < conthi_next) then + +c print *,'>> ZZ HIT!!, ibx= ',ibx,' jby= ',jby +c +c print *,' +++ BEYOND in NEXT: i= ',ibx,' j= ',jby +c & ,' fxy= ',fxy(ibx,jby) + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + +c print *,'.. ZZ, next_contour_ct= ',next_contour_ct + + enddo bey_con_min_loop + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + +c print *,'At A, beyond_contour_ct= ',beyond_contour_ct +c print *,' contlo_next = ',contlo_next +c print *,' conthi_next = ',conthi_next + + bey_con_max_loop: do nb = 1,beyond_contour_ct + +c print *,'in bey_con_max_loop, nb= ',nb + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_max_loop + endif + +c print *,'ibx= ',ibx,' jby= ',jby,' data= ' +c & ,fxy(ibx,jby) + + if (fxy(ibx,jby) > contlo_next .and. + & fxy(ibx,jby) <= conthi_next) then + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + +c print *,' ++ HIT! ibx= ',ibx,' jby= ',jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + enddo bey_con_max_loop + endif + + if (next_contour_ct > 0) then + ringct = next_contour_ct + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! XXX next_contour_ct not > 0 !!!' + print *,'next_contour_ct= ',next_contour_ct + print *,'beyond_contour_ct= ',beyond_contour_ct + print *,'ringct= ',ringct + print *,'next_ring_ct= ',next_ring_ct + print *,'cycling to top of successive_contours_loop..' + print *,' ' + endif + + ! The number of rings that we have available to search + ! in the next contour interval is 0, so cycle all the + ! way back to the top of the outer loop, which is + ! successive_contours_loop, so that we can increase the + ! contour bounds and search inside those new bounds. + ! Again, this is for the case in which we have an + ! intense, compact storm and we are using a small + ! contour interval, such that we are essentially + ! "skipping" over one of these intervals in one of the + ! loop iterations. We need to bump up the + ! num_found_contours by one in order to increase the + ! array index in the contvals array at the top of the + ! successive_contours_loop. It is kosher to do this + ! since the reason we are cycling back to the top of + ! that loop is that we are skipping over a contour + ! interval. + + num_found_contours = num_found_contours + 1 + cycle successive_contours_loop + + endif + + else + + ringct = next_contour_ct + + endif + + do nring = 1,ringct + search_next_i(nring) = next_contour_i(nring) + search_next_j(nring) = next_contour_j(nring) +c print *,'at A, nring= ',nring,' next_contour_i(nring)= ' +c & ,next_contour_i(nring),' next_contour_j(nring)= ' +c & ,next_contour_j(nring) + enddo + + next_contour_ct = 0 + + else + ringct = next_ring_ct + endif + + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + allocate (ringposi(ringct),stat=iria) + allocate (ringposj(ringct),stat=irja) + if (iria /= 0 .or. irja /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various ring arrays. iria = ',iria + print *,'!!! irja = ',irja + print *,' ' + endif + + STOP 98 + endif + +ctm +c print *,' ' +c print *,'ringct= ',ringct + + do nring = 1,ringct + ringposi(nring) = search_next_i(nring) + ringposj(nring) = search_next_j(nring) +ctm +c print *,'nring= ',nring,' ringposi= ',ringposi(nring) +c & ,' ringposj= ',ringposj(nring) + enddo + + next_ring_ct = 0 + + ! This next loop reviews the points that have been + ! labelled for the "beyond_contour" pool. As we get further + ! into successive iterations of successive_contours_loop, + ! some of these previously "beyond" points are now within + ! the contour interval range that we are checking, so we + ! need to go through the list of "beyond" points and remove + ! any that are no longer in that "beyond" category.... + + check_beyond_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! This point may have been removed already in a + ! previous iteration of successive_contours_loop. + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle check_beyond_loop + endif + + ! Check to see if any of the points being searched in the + ! upcoming multiple_ring_loop are points that had previously + ! been saved as "beyond_contour" points. If so, remove + ! their status as "beyond_contour" points by setting the + ! logical flag to false. + + do nring = 1,ringct + + if (ibx == ringposi(nring) .and. jby == ringposj(nring)) + & then +c print *,' ' +c print *,'!!! beyond remove: ibx= ',ibx,' jby= ',jby + point_is_already_in_beyond_pool(ibx,jby) = .false. + endif + + enddo + + enddo check_beyond_loop + + +c In each iteration of single_contour_scan_loop, we can have a +c different number of rings to analyze. In the first +c iteration, we only have 1 ring, the initial ring around the +c local max/min that was input to this subroutine. Subsequent +c iterations will have a variable number of rings, depending on +c how many new data points within our contour interval were +c found in the previous iteration. + + multiple_ring_loop: do mr = 1,ringct + + icenx = ringposi(mr) + jcenx = ringposj(mr) + +ctm +c print *,' --- iter= ',iter,' mr= ',mr,' icenx= ',icenx +c & ,' jcenx= ',jcenx,' imax= ',imax,' jmax= ',jmax + + call get_ijplus1_check_wrap (imax,jmax,icenx,jcenx,ixp1,jxp1 + & ,ixm1,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NO CLOSED CONTOUR: The call to ' + print *,'!!! get_ijplus1_check_wrap indicates the' + print *,'!!! max/min contour extends past the edge of' + print *,'!!! our regional grid. ' + print *,' ' + print *,' ' + endif + + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c For each individual ring, we check all 8 points surrounding +c the center point. The points are numbered for each ring as +c shown in the diagram to the right of the "select case" +c statement just below. REMEMBER: The j in our grids +c increases from north to south, so that for a global grid, +c j=1 is at 90N and j=jmax is at 90S. + + individual_ring_loop: do ir = 1,9 + + select case (ir) + case (1); irx=ixm1; jrx=jcenx;! 2 3 4 + case (2); irx=ixm1; jrx=jxm1; ! + case (3); irx=icenx;jrx=jxm1; ! + case (4); irx=ixp1; jrx=jxm1; ! 1 (icenx,jcenx) 5 + case (5); irx=ixp1; jrx=jcenx;! + case (6); irx=ixp1; jrx=jxp1; ! + case (7); irx=icenx;jrx=jxp1; ! 8 7 6 + case (8); irx=ixm1; jrx=jxp1; ! + case (9); irx=icenx; jrx=jcenx; ! = center pt of ring + end select + +c Make sure the point we are looking at has valid data. +c This is an issue only on regional grids, where we have a +c buffer of bitmapped (null) data points surrounding the +c real grid. + +c print *,'ind ring loop: ir= ',ir,' irx= ',irx,' jrx= ',jrx + + if (.not. valid_pt(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a non-' + print *,'!!! valid point, meaning we are near the' + print *,'!!! bounds of the grid, or at least the ' + print *,'!!! bounds of the valid data for this ' + print *,'!!! grid. We will skip the' + print *,'!!! search for this center.' + print *,'!!! ' + print *,'!!! (i,j) of non-valid pt = (' + & ,irx,',',jrx,')' + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c Check to make sure that the point we are looking at is +c not considered under the influence of another nearby low. + + if (masked_out(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a point' + print *,'!!! that has been masked out, meaning it' + print *,'!!! belongs under the influence of ' + print *,'!!! another nearby low, so we will skip' + print *,'!!! the search for this center....' + print *,'!!! ' + print *,'!!! Min central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Masked-out value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of masked value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we have already hit this point on a previous ring +c check, then just ignore this point and cycle past it. + + if (point_is_already_in_our_contour(irx,jrx)) then +ctm +c print *,' ' +c print *,'Pt. AAA, already-in-contour.....' +c print *,'irx= ',irx,' jrx= ',jrx + cycle individual_ring_loop + endif + +c For a MIN check, check to see if the data point is below +c the contour interval or is below the local minimum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c For a MAX check, check to see if the data point is above +c the contour interval or is above the local maximum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c +c For example, for mslp, this would be as we're moving +c outward away from lower pressures to higher pressures, +c and then all of a sudden we come upon a lower pressure. +c This probably means we're heading toward another low +c pressure area, so mark the point and return to the +c calling routine. + + found_a_point_below_contour = 'n' + found_a_point_above_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) < xcentval .or. fxy(irx,jrx) < contlo) + & then + found_a_point_below_contour = 'y' + endif + else + if (fxy(irx,jrx) > xcentval .or. fxy(irx,jrx) > conthi) + & then + found_a_point_above_contour = 'y' + endif + endif + + if (found_a_point_below_contour == 'y' .or. + & found_a_point_above_contour == 'y') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a data' + print *,'!!! value that is less (greater) than the' + print *,'!!! current contour interval bound for a' + print *,'!!! min (max) and/or is less (greater) ' + print *,'!!! than the minimum (maximum) central ' + print *,'!!! value that we are centering the ' + print *,'!!! search on.' + print *,'!!! ' + print *,'!!! Central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Flagged value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of flagged value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we've made it this far, then we at least know that the +c gradient is still heading in the right direction. Do the +c check now to see if the value at this point is within our +c specific contour interval (there is the possibility that +c the value is beyond our interval, which will be checked +c for just below, and if that's the case, then that point +c will be processed in a subsequent iteration of this loop +c that encompasses that correct contour interval). + + found_a_point_in_our_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) >= contlo .and. fxy(irx,jrx) < conthi) + & then + found_a_point_in_our_contour = 'y' + endif + else + if (fxy(irx,jrx) > contlo .and. fxy(irx,jrx) <= conthi) + & then + found_a_point_in_our_contour = 'y' + endif + endif + + if (found_a_point_in_our_contour == 'y') then + ! We've found a data point in our interval, something + ! that is inside the closed contour, and it hasn't been + ! marked as being found in a previous iteration of this + ! loop, so mark it now and store the (i,j) location so + ! that we can scan a ring around this point in a + ! successive iteration of this loop for more potential + ! points within this interval... + + point_is_already_in_our_contour(irx,jrx) = .true. + + next_ring_ct = next_ring_ct + 1 + search_next_i(next_ring_ct) = irx + search_next_j(next_ring_ct) = jrx + +c print *,'at B, next_ring_ct= ',next_ring_ct +c & ,' search_next_i()= ',search_next_i(next_ring_ct) +c & ,' search_next_j()= ',search_next_j(next_ring_ct) + + num_pts_in_one_contour = num_pts_in_one_contour + 1 + temp_mask_i_loc(num_pts_in_one_contour) = irx + temp_mask_j_loc(num_pts_in_one_contour) = jrx + + if (get_last_isobar_flag == 'y') then + call calcdist (glon(ix),glat(jx) + & ,glon(irx),glat(jrx),dist,degrees) + rlast_distsum = rlast_distsum + dist + rlast_distct = rlast_distct + 1 + endif + +ctm +c print *,' ' +c print *,' PT IN! irx= ',irx,' jrx= ',jrx,' xval= ' +c & ,fxy(irx,jrx) +c print *,'next_ring_ct= ',next_ring_ct +c print *,'num_pts_in_one_contour= ' +c & ,num_pts_in_one_contour + endif + +c If we've made it this far AND the +c found_a_point_in_our_contour flag indicates that this +c point is not in our contour interval, then by default that +c means that this point is for a contour interval beyond +c what we're currently looking at. E.g., if we're looking +c at the contours around a 972 mb low and we're moving +c outward and currently checking the 984-988 mb contour +c interval, it means that we found, say, a gridpoint with +c 991 mb. So we want to mark that point for a future +c iteration of this loop that would be checking the +c 988-992 mb contour interval. + + if (found_a_point_in_our_contour /= 'y' .and. + & .not. point_is_already_in_next_contour(irx,jrx)) then + ! We've found a data point that is beyond our interval, + ! so this is not a concern for finding the bounds of + ! our current contour interval, but we want to mark + ! these points and remember them for the next iteration + ! of successive_scan_loop. (For example, suppose we + ! are currently searching for points in the 984-988 mb + ! range, and we find a point that is 990 -- mark it + ! here to be remembered when we scan for 988-992 mb). + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + if (fxy(irx,jrx) >= contlo_next .and. + & fxy(irx,jrx) < conthi_next) then + ! "NEXT_CONTOUR" Comment: + ! We've found a point that is in the very next + ! contour interval.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. + else if (fxy(irx,jrx) >= conthi_next) then + ! "BEYOND_CONTOUR" Comment: + ! This point is at least 1 contour interval beyond + ! the next contour interval. Dump the info into + ! these i and j arrays. This info will be used if + ! in the next iteration of single_contour_scan_loop, + ! next_contour_ct = 0. That would mean that we + ! have, e.g., an intensely deep low with a sharp + ! mslp gradient that essentially "skips" over a + ! contour interval. E.g., if using a 4 mb interval, + ! we go from 947 to 953 AND there are NO + ! intervening gridpoints in the 948-952 interval. + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) + endif + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + if (fxy(irx,jrx) > contlo_next .and. + & fxy(irx,jrx) <= conthi_next) then + ! See "NEXT_CONTOUR" comment just above.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. +c print *,'NEXT ncc= ',next_contour_ct +c & ,'next_contour_i()= ' +c & ,next_contour_i(next_contour_ct) +c & ,'next_contour_j()= ' +c & ,next_contour_j(next_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + else if (fxy(irx,jrx) <= contlo_next) then + ! See "BEYOND_CONTOUR" comment just above.... + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'BEYOND bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + endif + endif + endif + + enddo individual_ring_loop + + enddo multiple_ring_loop + + if (next_ring_ct > 0) then + iter = iter + 1 + else + icccret = 0 + still_scanning = .false. + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + num_found_contours = num_found_contours + 1 + closed_contour = 'y' + if (num_found_contours == 1) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Closed contour found ' + endif + + endif + endif + + enddo single_contour_scan_loop + + do insingle = 1,num_pts_in_one_contour + num_pts_in_all_contours = num_pts_in_all_contours + 1 + inall = num_pts_in_all_contours + hold_mask_i_loc(inall) = temp_mask_i_loc(insingle) + hold_mask_j_loc(inall) = temp_mask_j_loc(insingle) + enddo + + if (get_last_isobar_flag == 'y') then + if (cmaxmin == 'min') then + plastbar = conthi + else + plastbar = contlo + endif + if (rlast_distct > 0) then + rlastbar = rlast_distsum / float(rlast_distct) + rlastbar = rlastbar * 0.539638 ! convert km to nm + else + rlastbar = -999.0 + endif + endif + + enddo successive_contours_loop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'END SUM: num of iterations = ',isc_count + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_land_mask (imax,jmax,ix,jx,fract_land,valid_pt + & ,dx,dy,point_is_over_water,iclmret) +c +c ABSTRACT: This subroutine looks at the values for the land-sea +c mask surrounding an input (i,j) position to determine if less +c than 50% of the area surrounding the input (i,j) position within +c 75 km radius is land. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c +c OUTPUT: +c fract_land Fraction of points/area that is covered by land +c point_is_over_water y/n: A value of 'y' is returned if <50% +c of the points/area is covered by land +c iclmret Return code from this routine +c + USE grid_bounds; USE tracked_parms + USE trkrparms; USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + logical(1) valid_pt(imax,jmax) + character point_is_over_water*1 + integer, parameter :: numazim=8 + integer iazim,ibiret1,imax,jmax,ix,jx,iclmret,imct + real bear,targlat,targlon,xplon,yplat,rdist,xintrp_mask + real fract_land,dx,dy,xmask_sum +c + iclmret = 0 + +c First, calculate the longitude and latitude of the input ix and +c jx points. If the xplon value ends up being >360.0 (this can +c happen for basin-scale HWRF), don't worry about it. Just leave +c it be, as the trigonometry will work out the same for lons >360. + + xplon = glonmin + (ix-1)*dx + yplat = glatmax - (jx-1)*dy + + rdist = 75.0 ! (We will always look only 75 km radius out for + ! this particular land-sea mask application) + + imct = 0 + +c Now go around the storm via azimloop and get interpolated +c values of the land-sea mask at each azimuth at a radial +c distance of 75 km from the center point.... + + xmask_sum = 0.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 45.0 + + call distbear (yplat,xplon,rdist,bear,targlat,targlon) + + ! These calls to bilin_int_uneven pass a variable, level, + ! that is used for applications of interpolating wind + ! data. Here, we are instead interpolating the land-sea + ! mask data, so we don't care about the level, so just + ! pass a dummy value of 850, which never gets used. + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,850,'m',xintrp_mask,ibiret1) + + if (ibiret1 == 0) then + xmask_sum = xmask_sum + xintrp_mask + imct = imct + 1 + else + iclmret = 95 + return + endif + + enddo azimloop + +c Now get the mask value directly at the point that was input to +c this routine.... + + xmask_sum = xmask_sum + lsmask(ix,jx) + imct = imct + 1 + +c Now get the mean land fraction.... + + if (imct > 0) then + + fract_land = xmask_sum / float(imct) + if (fract_land < 0.50) then + point_is_over_water = 'y' + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Land check yes: Point is over water. ' + print *,' Land check value: fract_land= ',fract_land + endif + else + point_is_over_water = 'n' + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Land check NO: Point is over land. ' + print *,' Land check value: fract_land= ',fract_land + endif + endif + + else + + iclmret = 95 + return + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine get_ijplus1_check_wrap (imax,jmax,i,j,iplus1,jplus1 + & ,iminus1,jminus1,trkrinfo,igicwret) +c +c ABSTRACT: This subroutine takes an (i,j) position input and +c returns the four neighboring (i,j) points to the east, south, +c west and north. The routine checks for wrap around the GM, so +c that if, for example, you are on a global 360x181 grid and you +c are at point i=360, then i+1 = 361, so you need something to +c adjust that back to i = 1. Likewise, if you are at i=1 and +c looking for point i-1, it will adjust it to be point 360 +c instead of the meaningless point 0 (i=0). + + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer i,j,imax,jmax,iplus1,jplus1,iminus1,jminus1,igicwret + + igicwret = 0 + + jplus1 = j + 1 + jminus1 = j - 1 + iplus1 = i + 1 + iminus1 = i - 1 + + if (iplus1 > imax) then + if (trkrinfo%gridtype == 'global') then + iplus1 = iplus1 - imax ! If wrapping east of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is too close to the eastern bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested eastern ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',iplus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (iminus1 < 1) then + if (trkrinfo%gridtype == 'global') then + iminus1 = imax + iminus1 ! If wrapping west of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested western search boundary' + print *,'!!! is too close to the western bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested western ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested western i = ',iminus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (jplus1 > jmax .or. jminus1 < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The ' + print *,'!!! user-requested northern or southern search' + print *,'!!! boundary is too close to the bounds of the' + print *,'!!! grid. Cut back your requested northern or' + print *,'!!! southern boundary by a degree or 2 in the' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested northern j = ',jminus1 + print *,'!!! User-requested southern j = ',jplus1 + print *,'!!! jmax of grid = ',jmax + print *,' ' + endif + + igicwret = 91 + return + endif + + return + end + +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + SUBROUTINE qsort(x,ind,n) +c +c Code converted using TO_F90 by Alan Miller +c Date: 2002-12-18 Time: 11:55:47 + + IMPLICIT NONE + INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(12, 60) + + REAL (dp), INTENT(IN) :: x(n) + INTEGER, INTENT(OUT) :: ind(n) + INTEGER, INTENT(IN) :: n + +c *************************************************************************** +c +c ROBERT RENKA +c OAK RIDGE NATL. LAB. +c +c THIS SUBROUTINE USES AN ORDER N*LOG(N) QUICK SORT TO SORT A REAL (dp) +c ARRAY X INTO INCREASING ORDER. THE ALGORITHM IS AS FOLLOWS. IND IS +c INITIALIZED TO THE ORDERED SEQUENCE OF INDICES 1,...,N, AND ALL INTERCHANGES +c ARE APPLIED TO IND. X IS DIVIDED INTO TWO PORTIONS BY PICKING A CENTRAL +c ELEMENT T. THE FIRST AND LAST ELEMENTS ARE COMPARED WITH T, AND +c INTERCHANGES ARE APPLIED AS NECESSARY SO THAT THE THREE VALUES ARE IN +c ASCENDING ORDER. INTERCHANGES ARE THEN APPLIED SO THAT ALL ELEMENTS +c GREATER THAN T ARE IN THE UPPER PORTION OF THE ARRAY AND ALL ELEMENTS +c LESS THAN T ARE IN THE LOWER PORTION. THE UPPER AND LOWER INDICES OF ONE +c OF THE PORTIONS ARE SAVED IN LOCAL ARRAYS, AND THE PROCESS IS REPEATED +c ITERATIVELY ON THE OTHER PORTION. WHEN A PORTION IS COMPLETELY SORTED, +c THE PROCESS BEGINS AGAIN BY RETRIEVING THE INDICES BOUNDING ANOTHER +c UNSORTED PORTION. +c +c INPUT PARAMETERS - N - LENGTH OF THE ARRAY X. +c +c X - VECTOR OF LENGTH N TO BE SORTED. +c +c IND - VECTOR OF LENGTH >= N. +c +c N AND X ARE NOT ALTERED BY THIS ROUTINE. +c +c OUTPUT PARAMETER - IND - SEQUENCE OF INDICES 1,...,N PERMUTED IN THE SAME +c FASHION AS X WOULD BE. THUS, THE ORDERING ON +c X IS DEFINED BY Y(I) = X(IND(I)). +c +c ********************************************************************* + + ! NOTE -- IU AND IL MUST BE DIMENSIONED >= LOG(N) WHERE LOG HAS BASE 2. + + !********************************************************************* + + INTEGER :: iu(21), il(21) + INTEGER :: m, i, j, k, l, ij, it, itt, indx + REAL :: r + REAL (dp) :: t + + ! LOCAL PARAMETERS - + + ! IU,IL = TEMPORARY STORAGE FOR THE UPPER AND LOWER + ! INDICES OF PORTIONS OF THE ARRAY X + ! M = INDEX FOR IU AND IL + ! I,J = LOWER AND UPPER INDICES OF A PORTION OF X + ! K,L = INDICES IN THE RANGE I,...,J + ! IJ = RANDOMLY CHOSEN INDEX BETWEEN I AND J + ! IT,ITT = TEMPORARY STORAGE FOR INTERCHANGES IN IND + ! INDX = TEMPORARY INDEX FOR X + ! R = PSEUDO RANDOM NUMBER FOR GENERATING IJ + ! T = CENTRAL ELEMENT OF X + + IF (n <= 0) RETURN + + ! INITIALIZE IND, M, I, J, AND R + + DO i = 1, n + ind(i) = i + END DO + m = 1 + i = 1 + j = n + r = .375 + + ! TOP OF LOOP + + 20 IF (i >= j) GO TO 70 + IF (r <= .5898437) THEN + r = r + .0390625 + ELSE + r = r - .21875 + END IF + + ! INITIALIZE K + + 30 k = i + + ! SELECT A CENTRAL ELEMENT OF X AND SAVE IT IN T + + ij = i + r*(j-i) + it = ind(ij) + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) > t) THEN + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + END IF + + ! INITIALIZE L + + l = j + + ! IF THE LAST ELEMENT OF THE ARRAY IS LESS THAN T, + ! INTERCHANGE IT WITH T + indx = ind(j) + IF (x(indx) >= t) GO TO 50 + ind(ij) = indx + ind(j) = it + it = indx + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) <= t) GO TO 50 + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + GO TO 50 + + ! INTERCHANGE ELEMENTS K AND L + + 40 itt = ind(l) + ind(l) = ind(k) + ind(k) = itt + + ! FIND AN ELEMENT IN THE UPPER PART OF THE ARRAY WHICH IS + ! NOT LARGER THAN T + + 50 l = l - 1 + indx = ind(l) + IF (x(indx) > t) GO TO 50 + + ! FIND AN ELEMENT IN THE LOWER PART OF THE ARRAY WHCIH IS NOT SMALLER THAN T + + 60 k = k + 1 + indx = ind(k) + IF (x(indx) < t) GO TO 60 + + ! IF K <= L, INTERCHANGE ELEMENTS K AND L + + IF (k <= l) GO TO 40 + + ! SAVE THE UPPER AND LOWER SUBSCRIPTS OF THE PORTION OF THE + ! ARRAY YET TO BE SORTED + + IF (l-i > j-k) THEN + il(m) = i + iu(m) = l + i = k + m = m + 1 + GO TO 80 + END IF + + il(m) = k + iu(m) = j + j = l + m = m + 1 + GO TO 80 + + + ! BEGIN AGAIN ON ANOTHER UNSORTED PORTION OF THE ARRAY + + 70 m = m - 1 + IF (m == 0) RETURN + i = il(m) + j = iu(m) + + 80 IF (j-i >= 11) GO TO 30 + IF (i == 1) GO TO 20 + i = i - 1 + + ! SORT ELEMENTS I+1,...,J. NOTE THAT 1 <= I < J AND J-I < 11. + + 90 i = i + 1 + IF (i == j) GO TO 70 + indx = ind(i+1) + t = x(indx) + it = indx + indx = ind(i) + IF (x(indx) <= t) GO TO 90 + k = i + + 100 ind(k+1) = ind(k) + k = k - 1 + indx = ind(k) + IF (t < x(indx)) GO TO 100 + + ind(k+1) = it + GO TO 90 + END SUBROUTINE qsort + +c +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT11/FORT31 + subroutine open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + +C ABSTRACT: This subroutine must be called before any attempt is +C made to read from the input GRIB files. The GRIB and index files +C are opened with a call to baopenr. This call to baopenr was not +C needed in the cray version of this program (the files could be +C opened with a simple Cray assign statement), but the GRIB-reading +C utilities on the SP do require calls to this subroutine (it has +C something to do with the GRIB I/O being done in C on the SP, and +C the C I/O package needs an explicit open statement). +C +C INPUT: +c inp Contains user-input info on the date & data +C lugb The Fortran unit number for the GRIB data file +C lugi The Fortran unit number for the GRIB index file +c ifh integer index for lead time level +c gfilename If using individual files for each tau, gfilename will +c contain the grib data filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +c ifilename If using individual files for each tau, gfilename will +c contain the grib index filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +C lout The Fortran unit number for the output grib file +C +C OUTPUT: +C iret The return code from this subroutine + + USE inparms + USE verbose_output + + implicit none +c + type (datecard) inp + + logical(1) output_file_open + logical(1) file_open + character(*) gfilename,ifilename +c character(120) gopen_g_file,gopen_i_file +cPeng 05/28/2018 Bug Fixed for FV3-GFS Job Crashed. + character(255) gopen_g_file,gopen_i_file + character(2) lugb_c,lugi_c + character(6) enameb,enamei + integer igoret,iioret,iooret,lugb,lugi,lout,iret,nlen1,nlen2 + + iret=0 + + if (inp%file_seq == 'onebig') then + write(lugb_c,'(i2)')lugb + write(lugi_c,'(i2)')lugi + enameb='FORT'//adjustl(lugb_c) + enamei='FORT'//adjustl(lugi_c) + call get_environment_variable(enameb,gopen_g_file,status=igoret) + call get_environment_variable(enamei,gopen_i_file,status=iioret) +c if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then +cPENG---2018-06-07-------------------------------- + if (igoret /= 0 .or. iioret /= 0 ) then + gopen_g_file(1:5) = "fort." + gopen_i_file(1:5) = "fort." + write(gopen_g_file(6:7),'(I2)') lugb + write(gopen_i_file(6:7),'(I2)') lugi + endif + else + nlen1 = len_trim(gfilename) + gopen_g_file = trim(gfilename(1:nlen1)) + nlen2 = len_trim(ifilename) + gopen_i_file = trim(ifilename(1:nlen2)) + endif + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + +c print *,'gopen_g_file= ',gopen_g_file,'....' +c print *,'gopen_i_file= ',gopen_i_file,'....' + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end + +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT12 + subroutine read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + ii=1 + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + maxstorm = numtcv + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that you have the Fortran' + print *,'!!! unit assigned right in your script.' + endif + + iret = 99 + return + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT14 + subroutine read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + ii = numtcv + 1 + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1,1x + & ,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end + +cPENG----2018-06-07 ------------------------ + subroutine get_ushe_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanushe,igridushe,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,iushet) +c +cABSTRACT: This subroutine finds the minimum and mean U-shear values +c between 200 & 850 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cPENG----2018-06-07--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07------------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanushe,dx,dy,re,ri,parmlon,parmlat + real igridushe,imeanushe + integer n,ix1,ix2,npts,imax,jmax,iushet,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ushe_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + iushet = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_ushe_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + iushet = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max ushe values at 200 and 850 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_ushe_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_ushe_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeanushe = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (ushear(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid ushe data for this level, so + ! we first call barnes now to get the mean ushe + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then +c cvort_maxmin = 'max' +c else + cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'ushe' + & ,ushear(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanushe + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cPENG--------------------------- +c imeanushe = int (xmeanushe + 0.5) + imeanushe = xmeanushe + else + imeanushe = -999. + igridushe = -999. + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_ushe_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for ushe values will not be done.') + exit report_ushe_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanushe = -999. + igridushe = -999. + exit report_ushe_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeanushe,imeanushe + 621 format (1x,' xmeanushe= ',f9.3,' imeanushe= ',f9.3) + write (6,*) ' --- mean ushe raw = ',xmeanushe + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! ushe data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,ushear(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanushe,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cPENG--------------------------- +c igridushe = int (gridpoint_maxmin + 0.5) + igridushe = gridpoint_maxmin + else + igridushe = -999. + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridushe,ifilret + 623 format (1x, + & ' grid ushe= ',f9.3,' igrid ushe= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid ushe raw= ',gridpoint_maxmin + endif + + enddo report_ushe_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get U-shear for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +cPENG----2018-06-07-------------------------------------------------- +c----------------------------------------------------------------------- + subroutine get_rhum_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanrhum,igridrhum,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,irhumt) +c +c ABSTRACT: This subroutine finds the minimum and mean RH values +c at 500 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cPENG----2018-06-07--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07------------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanrhum,dx,dy,re,ri,parmlon,parmlat + real igridrhum,imeanrhum + integer n,ix1,ix2,npts,imax,jmax,irhumt,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_rhum_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + irhumt = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_rhum_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + irhumt = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max rhum values at 500 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_rhum_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_rhum_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeanrhum = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (rhumid(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid rhum data for this level, so + ! we first call barnes now to get the mean rhum + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then +c cvort_maxmin = 'max' +c else + cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'rhum' + & ,rhumid(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanrhum + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cPENG------------------------------------------------- +c imeanrhum = int (xmeanrhum + 0.5) + imeanrhum = xmeanrhum + else + imeanrhum = -99.0 + igridrhum = -99.0 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_rhum_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for rhum values will not be done.') + exit report_rhum_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanrhum = -99.0 + igridrhum = -99.0 + exit report_rhum_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeanrhum,imeanrhum + 621 format (1x,' xmeanrhum= ',f9.3,' imeanrhum= ',f9.3) + write (6,*) ' --- mean rhum raw = ',xmeanrhum + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! rhum data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,rhumid(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanrhum,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cPENG------------------------------------------------- +c igridrhum = int (gridpoint_maxmin + 0.5) + igridrhum = gridpoint_maxmin + else + igridrhum = -99.0 + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridrhum,ifilret + 623 format (1x, + & ' grid rhum= ',f9.3,' igrid rhum= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid rhum raw= ',gridpoint_maxmin + endif + + enddo report_rhum_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get RH for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end + +cPENG----2018-06-07------------------------------------------- +c This is for area mean caculations +c--------------------------------------------------------------------- + subroutine fix_latlon_mean_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +cPENG----2018-06-07--------------------------------------- + real dsum, dnum + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.10) then + grfact = 4 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif +cPENG----2012--04--18---10X10 average ------------------------- + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (4*grfact) + iend = ipfix + (5*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (5*grfact) + iend = ipfix + (4*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (4*grfact) + iend = ipfix + (4*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (5*grfact) + jend = jpfix + (4*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (4*grfact) + jend = jpfix + (5*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (4*grfact) + jend = jpfix + (4*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix +cPENG----2018-06-07--------------------------------------- + dsum=0.0 + dnum=0.0 + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif +cPENG----2018-06-07--------------------------------------- + dsum=dsum+fxy(i,j) + dnum=dnum+1 + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +cPENG----2018-06-07--------------------------------------- + if (dnum.gt.0.0) then + gridpoint_maxmin=dsum/dnum + else + gridpoint_maxmin=-9999.0 + endif + + print *,'istart=',istart,'iend=',iend + print *,'jstart=',jstart,'jend=',jend + print *,'End of fix_latlon_mean_ij, gridpoint_maxmin = ' + & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +cPENG----2018-06-07------10X10 area mean temperature----------------- +c----------------------------------------------------------------------- + subroutine get_temp_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeantemp,igridtemp,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,itempt) +c +c ABSTRACT: This subroutine finds the maximum and mean temperature +c between 300 & 500 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cPENG----2018-06-07--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07------------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeantemp,dx,dy,re,ri,parmlon,parmlat + real igridtemp,imeantemp + integer n,ix1,ix2,npts,imax,jmax,itempt,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_temp_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + itempt = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_temp_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + itempt = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max temp values at 300 and 500 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_temp_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_temp_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeantemp = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (tmean(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid temp data for this level, so + ! we first call barnes now to get the mean temp + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' +c else +c cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'temp' + & ,tmean(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeantemp + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cPENG------------------- +c imeantemp = int (xmeantemp + 0.5) + imeantemp = xmeantemp + else + imeantemp = -99.0 + igridtemp = -99.0 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_temp_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for temp values will not be done.') + exit report_temp_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeantemp = -99.0 + igridtemp = -99.0 + exit report_temp_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeantemp,imeantemp + 621 format (1x,' xmeantemp= ',f9.3,' imeantemp= ',f9.3) + write (6,*) ' --- mean temp raw = ',xmeantemp + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! temp data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,tmean(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeantemp,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cPENG------------------- +c igridtemp = int (gridpoint_maxmin + 0.5) + igridtemp = gridpoint_maxmin + else + igridtemp = -99.0 + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridtemp,ifilret + 623 format (1x, + & ' grid temp= ',f9.3,' igrid temp= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid temp raw= ',gridpoint_maxmin + endif + + enddo report_temp_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get T-mean for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_gfs.f_10072019 b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_gfs.f_10072019 new file mode 100644 index 0000000000..b817280860 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_gfs.f_10072019 @@ -0,0 +1,27266 @@ + program trakmain +c +c$$$ MAIN PROGRAM DOCUMENTATION BLOCK +c +c Main Program: GETTRK Track model vortices +C PRGMMR: MARCHOK ORG: NP22 DATE: 2002-05-20 +c +c ABSTRACT: This program tracks the average of the max or min +c of several parameters in the vicinity of an input +c first guess (lat,lon) position of a vortex in order to give +c forecast position estimates for that vortex for a given numerical +c model. For the levels 700 & 850 mb, the tracked parameters are: +c Relative vorticity (max), wind magnitude (min), and geopotential +c height (min). Also tracked is the min in the MSLP. So many +c parameters are tracked in order to provide more accurate position +c estimates for weaker storms, which often have poorly defined +c structures/centers. Currently, the system is set up to be able +c to process GRIB input data files from the GFS, MRF, UKMET, GDAS, +c ECMWF, NGM, NAM and FNMOC/NAVGEM models. Two 1-line files +c are output from this program, both containing the forecast fix +c positions that the tracker has obtained. One of these output +c files contains the positions at every 12 hours from forecast +c hour 0 to the end of the forecast. The other file is in ATCF +c format, which is the particular format needed by the Tropical +c Prediction Center, and provides the positions at forecast hours +c 12, 24, 36, 48 and 72, plus the maximum wind near the storm center +c at each of those forecast hours. +c +c Program history log: +c 98-03-16 Marchok - Original operational version. +c 98-07-15 Marchok - Added code to calculate radii of gale-, storm-, +c and hurricane-force winds in each quadrant. +c 99-04-01 Marchok - Added code to be able to read in 4-digit years +c off of the TC Vitals records. +c Added code, including subroutine is_it_a_storm, +c to make a better determination of whether or +c not the center that was found at each time is +c the center of a storm, and not just a passing +c vort max, etc. +c 99-06-15 Marchok - Fixed a bug in calcdist that was triggered by a +c rounding error sending a number just above 1 +c into ACOS to get the distance between 2 +c identical points (which, obviously, is 0). +c 00-06-20 Marchok - Added GDAS option for vortex relocation work. +c Changed nhalf from 3 to 5. Relaxed the +c requirements for pthresh and vthresh. +c 00-11-30 Marchok - Added ability to handle GFDL and NCEP Ensemble +c model data. Extended time range to be able to +c handle 5-day capability. Forecast hours are +c now input via a namelist (easiest way to account +c for NAM, GFS and GFDL having different forecast +c lengths at 00/12z and 06/18z). Model ID's are +c now input via a namelist (makes it easier, for +c example, to run for many different ensemble +c members). Added new output, the atcfunix +c format, needed for 5-day forecasts. +c 01-08-24 Marchok Fixed a bug in rvcal and getgridinfo. When a +c grid that was south-->north is flipped in +c conv1d2d_real to be north-->south, the scanning +c mode flag remains 64 and what we would consider +c the max and min latitudes are reversed, so I +c added code to correct this in both routines. +c 02-05-20 Marchok Weakened the mslp gradient threshold and v850 +c threshold in is_it_a_storm to cut down on the +c number of dropped storms. +c 03-03-18 Marchok Fixed a bug in get_ij_bounds that was allowing +c a cos(90) and cos(-90), which then led to a +c divide by zero. +c 05-08-01 Marchok Updated to allow tracking of ECMWF hi-res, ECMWF +c ensemble, CMC hi-res, CMC ensemble, NCEP +c ensemble. +c 06-11-07 Marchok Updated to locate, and report to the atcfunix +c file, the value of the gridpoint minimum value +c of mslp. Previously, the barnes-averaged +c value had been used. +c 08-01-10 Marchok Changed the storm ID for genesis tracking so +c that the ID includes info +c on storm detection location & time. Added +c algorithms for Hart's cyclone phase space. +c Added new output fields to the atcfunix +c records, actually creating a modified atcfunix +c record, to include things such as the mean & +c max values of zeta850 & zeta700 centered on +c the storm, the speed & direction of storm +c translation, and the Hart CPS parameters. +c 10-01-07 Marchok - input grib lead time can be hrs or minutes +c - added code for warm core check +c - added code to detect genesis +c - added code to report on sfc wind structure +c - added buffer ("grid_buffer") to avoid fixing +c center to boundaries on regional grids +c - modified rvcal to report missing zeta values +c as background coriolis instead of -999, since +c the -999 was messing up center-fixing +c - added 10-m wind and sfc zeta as center-fixing +c parms. +c +c 10-05-25 Slocum Add verbose feature to code +c 0 = Not terminal output, 1 = error messages only +c 2 = all output +c +c 10-05-26 Marchok - added flags and code to check the temporal +c consistency of the mslp closed contour and +c Vt850 checks for tcgen and midlat cases. +c +c 13-04-01 Marchok Added code to upgrade the wind radii diagnosid. +c Hurricane Sandy exposed an issue with the +c tracker for large storms. The code was modified +c to use an iterative technique that can +c diagnose radii for large storms but still +c accurately diagnost radii for small storms. See +c subroutine getradii for more details. +c +c 15-11-01 Marchok Replaced the routine which tracks the wind +c minimum at the center of a storm, as that +c routine proved troublesome with very hi-res +c grids (0.02-deg) from HWRF for very small +c storms. This has been replaced with a routine +c that looks for "wind circulation difference", +c whereby the center for this parm is located at +c the spot where the tangential wind circulation +c minus the wind magnitude at the candidate +c center position is maximized. ALSO: Added in +c tracking of thickness as an additional +c tracked parm. ALSO: Added a separate verbose +c flag for only the GRIB2 read diagnostics, which +c can be voluminous. +c +c 16-09-01 Marchok Added in the ability to read in NetCDF files. +c As with GRIB data, the NetCDF data must be on +c a lat/lon grid. +c +c 17-08-31 Marchok Added a logical bitmap capability for NetCDF +c files to prevent the accessing of missing data. +c Also modified the code to permit more accurate +c reporting of the grid point value of the +c minimum SLP for reporting to the atcfunix file. +c Finally, fixed a bug (reported by JTWC) whereby +c radii were being reported for thresholds that +c were in exceedance of the tracker-diagnosed +c Vmax (e.g., 34-kt radii for a storm with +c Vmax = 25 kts). +c +c Input files: +c unit 11 Unblocked GRIB1 file containing model data +c unit 12 Text file containing TC Vitals card for current time +c unit 31 Unblocked GRIB index file +c +c Output files: +c unit 61 Output file with forecast positions every 12h from +c vt=00h to the end of the forecast +c unit 62 Output file in ATCF format, with forecast positions +c at vt = 12, 24, 36, 48 and 72h, plus wind speeds. +c unit 63 Output file with forecast wind radii for 34, 50 and +c 64 knot thresholds in each quadrant of each storm. +c +c Subprograms called: +c read_nlists Read input namelists for input date & storm number info +c read_tcv_card Read TC vitals file to get initial storm position +c getgridinfo Read GRIB file to get basic grid information +c tracker Begin main part of tracking algorithm +c +c Attributes: +c Language: Standard Fortran_90 +c +c$$$ +c +c------- +c +c LOCAL: +c +c ifhours: Integer array holding numerical forecast times for +c the input model (99 = no more times available). +c These values are read in via a namelist. +c Model numbers used: (1) GFS, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) NAM, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble (13) SREF +c Ensemble, (14) NCEP Ensemble (from ensstat mean +c fields), (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) Ensemble RELOCATION (21) UKMET hi-res (NHC) +c (23) FNMOC Ensemble +c stormswitch: This switch tells how to handle each storm in +c the TCV file: +c 1 = process this storm for this forecast hour. +c 2 = Storm was requested to be tracked, but either +c the storm went off the grid (regional models), +c the storm dissipated, or the program was +c unable to track it. +c 3 = Storm was NOT requested to be tracked at all. +c storm: An array of type tcvcard. Each member of storm +c contains a separate TC Vitals card. +c maxstorm: Maximum number of storms the system is set up to +c handle at any 1 time. +c slonfg,slatfg: Holds first guess positions for storms. The +c very first, first guess position is read from the +c TC vitals card. (maxstorm,maxtime) +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms) +c + USE def_vitals; USE inparms; USE set_max_parms; USE level_parms + USE trig_vals; USE atcf; USE trkrparms; USE verbose_output + USE netcdf_parms +c + implicit none +c + logical(1) file_open + integer date_time(8) + character (len=10) big_ben(3) + character :: ncfile*180,ncfile_has_hour0*1 + integer itret,iggret,iicret,igcret,iret,ifhmax,maxstorm,numtcv + integer iocret,enable_timing,ncfile_id,ncfile_tmax,irnhret + integer, parameter :: lugb=11,lugi=31,lucard=12,lgvcard=14,lout=51 +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + +c -------------------------------------------------------- + + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: beginning ... ',i2.2,':',i2.2,':',i2.2) + + call w3tagb('GETTRK ',1999,0104,0058,'NP22 ') + + pi = 4. * atan(1.) ! Both pi and dtr were declared in module + dtr = pi/180.0 ! trig_vals, but were not yet defined. + ncfile_has_hour0 = 'n' ! Default value; set in read_netcdf_hours +c + call read_nlists (inp,trkrinfo,netcdfinfo) + enable_timing=trkrinfo%enable_timing + + call read_fhours (ifhmax) + + call read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_tcv_card, num vitals = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_tcv_card, rc= ',iret + endif + goto 890 + endif + + call read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_gen_vitals, total number of vitals (both' + & ,' TC and non-TC) now = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_gen_vitals, rc= ' + & ,iret + endif + goto 890 + endif + + if (inp%file_seq == 'onebig') then + if (trkrinfo%inp_data_type == 'netcdf') then + ncfile = netcdfinfo%netcdf_filename + print *,' ' + print *,'before open_ncfile call, ncfile= ',ncfile + call open_ncfile (ncfile,ncfile_id) + print *,'after open_ncfile call, ncfile_id= ',ncfile_id + call read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) + if (irnhret /= 0) then + print *,'(/,a32,a5,i4,/)','!!! ERROR: in read_netcdf_hours,' + & ,' rc= ',irnhret + goto 890 + endif + else + call open_grib_files (inp,lugb,lugi,'dummy','dummy',lout,iret) + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: in open_grib_files, rc= ' + & ,iret + goto 890 + endif + endif + endif + + call tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +890 continue + + igcret=0 + iicret=0 + iocret=0 + + inquire (unit=lugb, opened=file_open) + if (file_open) call baclose(lugb,igcret) + inquire (unit=lugi, opened=file_open) + if (file_open) call baclose(lugi,iicret) + inquire (unit=lout, opened=file_open) + if (file_open) call baclose(lout,iocret) + if ( verb .ge. 3 ) then + print *,'baclose: igcret= ',igcret,' iicret= ',iicret + print *,'baclose: iocret= ',iocret + endif + call w3tage('GETTRK ') +c + stop + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +c ABSTRACT: This subroutine is the core of the program. It contains +c the main loop for looping through all the forecast hours and all +c the storms. Basically, the way it works is that it has an outer +c loop that loops on the forecast hour. At the beginning of this +c loop, the data are read in for all parameters and levels needed +c for tracking. The full regional or global grid is read in. +c If vorticity was not read in (some of the centers do not send us +c vorticity), then vorticity calculations are done on the whole +c grid at both 850 and 700 mb. Then the program goes into the inner +c loop, which loops on storm number (program originally set up to +c handle a max of 15 storms). For each storm, subroutine +c find_maxmin is called for the following parameters: Rel Vort and +c geopotential hgt at 700 & 850 mb, and MSLP. Within find_maxmin, +c a barnes analysis is performed over the guess position of the +c storm to find the max or min value, and then iteratively, the +c grid size is cut in half several times and the barnes analysis +c rerun to refine the positioning of the max or min location. After +c the center positions for these parameters have been obtained, +c subroutine get_uv_center is called to get a center fix for the +c minimum in the wind field, specifically, a minimum in the +c magnitude of the wind speed (vmag). The calculation of the vmag +c minimum is done differently than the calculation for the other +c parameters; for vmag, the grid near the storm center guess +c position is interpolated down to a very fine grid, and then +c find_maxmin is called and a barnes analysis is done on that +c smaller grid. For vmag, there are no further calls made to barnes +c with a smaller grid, since the grid has already been interpolated +c down to a smaller grid. Once all of the parameter center fixes +c have been made, subroutine fixcenter is called to average these +c positions together to get a best guess fix position. Then a check +c is done with a call to subroutine is_it_a_storm to make sure that +c the center that we have found does indeed resemble a tropical +c cyclone. Finally, subroutine get_next_ges is called to make a +c guess position for the next forecast time for this storm. +c +c INPUT: +c inp contains input date and model number information +c maxstorm maximum # of storms to be handled +c numtcv number of storms read off of the tcvitals file +c ifhmax max number of analysis & forecast times to be handled +c trkrinfo derived type that holds/describes various tracker parms +c ncfile if the input data type is netcdf, then this ncfile +c variable contains the name of the netcdf file +c ncfile_id if the input data type is netcdf, then this ncfile_id +c variable contains an integer id assigned to the netcdf +c file from the open_ncfile subroutine +c ncfile_has_hour0 character flag (y|n) that, if the tracker is +c running on NetCDF data, tells if the NetCDF file +c actually contains hour0 data or not (some, like the +c 2016 version of FV3, do not). +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file itself in +c subroutine read_netcdf_fhours. +c +c OUTPUT: +c itret return code from this subroutine +c +c LOCAL PARAMETERS: +c storm contains the tcvitals for the storms +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c maxtime Max number of forecast times program can track +c maxtp Max number of tracked parameters program will track. +c Currently (7/2015), this maxtp is 11, and these 11 are +c listed just a few lines below. +c readflag L Indicates status of read for each of 16 parms: +c 1: 850 mb absolute vorticity +c 2: 700 mb absolute vorticity +c 3: 850 mb u-comp +c 4: 850 mb v-comp +c 5: 700 mb u-comp +c 6: 700 mb v-comp +c 7: 850 mb gp hgt +c 8: 700 mb gp hgt +c 9: MSLP +c 10: near-surface u-comp +c 11: near-surface v-comp +c 12: 500 mb u-comp +c 13: 500 mb v-comp +c 14: Mean temperature, centered at 400 mb +c 15: 500 mb gp hgt +c 16: 200 mb gp hgt +c 17: Land-Sea Mask (for use in tcgen applications, and +c even there, it's optional) +c +c calcparm L indicates which parms to track and which not to. +c Array positions are defined exactly as for clon +c and clat, listed next, except that, in general, when +c flag 3 is set to a value, flag 4 is set to the same +c value as 3, and when flag 5 is set to a value, flag +c 6 is set to the same value as 5. This is because +c 3 & 4 are for the 850 mb winds, and if either u or +c v is missing, we obviously can't calculate the +c magnitude of the wind. The same applies for 5 & 6, +c which are for the 700 mb winds. And also for reference, +c here is a list of all the variables & levels for the +c tracked parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms). +c For the third position (max_#_parms), here they are: +c 1: Relative vorticity at 850 mb +c 2: Relative vorticity at 700 mb +c 3: Wind circulation difference at 850 mb +c 4: NOT CURRENTLY USED +c 5: Wind circulation difference at 700 mb +c 6: NOT CURRENTLY USED +c 7: Geopotential height at 850 mb +c 8: Geopotential height at 700 mb +c 9: Mean Sea Level Pressure +c 10: Wind circulation difference at 10 m +c 11: Relative vorticity at 10 m +c 12: Lower-level thickness (500-850) +c 13: Upper-level thickness (200-500) +c 14: Deep-Layer thickness (200-850) +c +c xmaxwind Contains maximum near-surface wind near the storm +c center for each storm at each forecast hour. +c stderr Standard deviation of the position "errors" of the +c different parameters for each storm at each time. +c fixlat,fixlon: Contain the final coordinates for each storm at +c each forecast hour. These coordinates are a +c weighted average of all the individual parameter +c positions (hgt, zeta, mslp, vmag). +c cvort_maxmin: Contains the characters 'max' or 'min', and is +c used when calling the find_maxmin routine for the +c relative vorticity (Look for max in NH, min in SH). +c vradius Contains the distance from the storm fix position to +c each of the various near-surface wind threshhold +c distances in each quadrant. +c (3,4) ==> (# of threshholds, # of quadrants) +c See subroutine getradii for further details. +c wfract_cov Fractional coverage (areal coverage) of winds +c exceeding a certain threshold (34, 50, 64 kts) in +c each quadrant. +c (5,5,3) ==> (# of quadrants + 1, # of distance bins, +c # of thresholds). +c The "extra" array size for quadrants (5, instead of 4) +c is there to hold the total (i.e., "whole disc") +c statistics. +c See subroutine get_fract_wind_cov for further details +c +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c isastorm Character array used in the call to is_it_a_storm, +c tells whether the minimum requirement for an MSLP +c gradient was met (isastorm(1)), whether for the midlat +c and tcgen cases if a closed mslp contour was found +c (isastorm(2)), and if a circulation exists at 850 mb +c (isastorm(3)). Can have a value of 'Y' (requirement +c met), 'N' (requirement not met) or 'U' (requirement +c undetermined, due to the fact that no center location +c was found for this parameter). +c maxmini These 2 arrays contain the i and j indeces for the +c maxminj max/min centers that are found using the rough check +c in first_ges_ctr and subsequent routines. Only needed +c for a midlatitude or a genesis run, NOT needed for a +c TC tracker run. +c stormct Integer: keeps and increments a running tab of the +c number of storms that have been tracked at any time +c across all forecast hours. Used only for midlat or +c tcgen runs. +c gridprs This contains the actual value of the minimum pressure +c at a gridpoint. The barnes analysis will return an +c area-averaged value of pressure; this variable will +c contain the actual minimum value at a gridpoint near +c the lat/lon found by the barnes analysis. +c closed_mslp_ctr_flag This flag keeps track of the value of the +c closed contour flag returned from subroutine +c check_closed_contour. +c vt850_flag This flag keeps track of the value of the flag for +c the 850 mb Vt check. +c----- +c + USE def_vitals; USE inparms; USE tracked_parms; USE error_parms + USE set_max_parms; USE level_parms; USE grid_bounds; USE trkrparms + USE contours; USE atcf; USE radii; USE trig_vals; USE phase + USE gen_vitals; USE structure; USE verbose_output + USE waitfor_parms; USE module_waitfor; USE netcdf_parms + USE tracking_parm_prefs +c + implicit none +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (cint_stuff) contour_info +c + character, allocatable :: closed_mslp_ctr_flag(:,:)*1 + character, allocatable :: vt850_flag(:,:)*1 + character :: r34_check_okay*1,had_to_try_backup_850_vt_check*1 + character :: need_to_expand_r34(4)*1,ncfile_has_hour0*1 + character*(*), intent(in) :: ncfile + integer :: ncfile_id +c integer, parameter :: nreadparms=17 +cPENG----2018-06-07 ------------------------ + integer, parameter :: nreadparms=19 + + real, allocatable :: prstemp(:),iwork(:) + integer, parameter :: numdist=14,numquad=4,lout=51 + integer, allocatable :: prsindex(:) + integer imax,jmax,ifh,ist,irf,jj,istmp,ifhtemp,itret,ivpa + integer isiret1,isiret2,isiret3,idum,m,iix,jjx,imode,numtcv + integer iha,isa,iua,iva,iza,maxstorm,ivort,ifix,jfix,issret + integer imoa,imoca,iksa,isda,ileadtime,leadtime_check + integer ioaret,ioaxret,ifgcret,ifmret,igugret,isoiret,icccret + integer igrret,igmwret,iorret,ignret,iovret,icbret,igucret,ita +cPENG----2018-06-07 ------------------------ + integer ish, irh + + integer ifilret,ifret,iaret,isret,iotmret,iwa,iisa,sl_counter + integer iicret,igcret,pfcret,igwcret,imbowret,iatret + logical(1), allocatable :: valid_pt(:,:) + logical(1), allocatable :: masked_outc(:,:),masked_out(:,:) + logical(1) readflag(nreadparms),calcparm(maxtp,maxstorm) + logical(1) tracking_previously_known_storms + logical(1) need_to_flip_lats,need_to_flip_lons + logical(1) file_open,first_time_thru_getradii + character cvort_maxmin*3,isastorm(3)*1,ccflag*1,gotten_avg_value*1 + character cmaxmin*3,get_last_isobar_flag*1,wcore_flag*1 + character gfilename*120,ifilename*120,gridmove_status*7 + integer vradius(3,4),igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) +cPENG----2018-06-07 ------------------------ + real igridushe,imeanushe + real igridrhum,imeanrhum + real igridtemp,imeantemp + + integer maxmini(maxstorm),maxminj(maxstorm),pdf_ct_bin(16) + integer ifcsthour,stormct,prevstormct,kf,istmspd,istmdir,iggret + integer igiret,iuret,jdum,icount,ilonfix,jlatfix,igpret,ifhmax + integer ibeg,jbeg,iend,jend,ix1,ix2,n,ilev,npts,icpsa,igzvret +cPENG----2018-06-07 ------------------------ + integer iushet, irhumt, itempt + real wcore_mean_val,wcore_point_max + + integer igfwret,ioiret,igisret,iofwret,iowsret,igwsret,igscret + integer pdf_ct_tot,lugb,lugi,iret,icmcf,iccfh,ivt8f + integer waitfor_gfile_status,waitfor_ifile_status,ncfile_tmax + integer wait_max_ifile_wait,ivr,r34_good_ct,itha,ilma,inctcv + integer date_time(8) + character (len=10) big_ben(3) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridprs(maxstorm,maxtime) + real wfract_cov(5,5,3) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real ike(max_ike_cats) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmaxwind(maxstorm,maxtime),xmeanzeta + real stderr(maxstorm,maxtime),xval(maxtp),cps_vals(3) + real gridpoint_maxmin,dist,distnm,xknots,xmaxspeed + real uvgeslon,uvgeslat,xavg,stdv,search_cutoff,re,ri,dx,dy + real xinp_fixlat,xinp_fixlon,degrees,plastbar,rlastbar + real xinterval_fhr,cc_time_sum_tot,cc_time_sum_yes + real rmax,sdp,wdp,paramb,vtl_slope,vtu_slope + real xsfclon,xsfclat,cc_time_pct,radmax,r34_dist_thresh + real prev_latmax,prev_latmin,prev_lonmax,prev_lonmin + real vradius_km,hold_old_contint,tcv_max_wind_ms + real tcv_mslp_pa,r34_from_tcv,roci_from_tcv + real proci_from_tcv,prs_contint_thresh + integer enable_timing,igrct + character(pfc_cmd_len) :: pfc_final +c + prev_latmax = -999.0 + prev_latmin = -999.0 + prev_lonmax = -999.0 + prev_lonmin = -999.0 + enable_timing=trkrinfo%enable_timing + icmcf = 0 + ivt8f = 0 + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + allocate (closed_mslp_ctr_flag(maxstorm,ifhmax),stat=icmcf) + allocate (vt850_flag(maxstorm,ifhmax),stat=ivt8f) + ! Initialize flags to 'u', not 'n'. That way, + ! when we are evaluating its value back over recent past hours, + ! we can distinguish a "no" value from an initialized value of + ! 'u' for which a storm hadn't yet been detected. + closed_mslp_ctr_flag = 'u' + vt850_flag = 'u' + endif + + allocate (prsindex(maxstorm),stat=iisa) + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iisa /= 0 .or. iva /= 0 .or. iwa /= 0 .or. icmcf /= 0 .or. + & ivt8f /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating prsindex,' + print *,'!!! prstemp or iwork array for storms: iisa = ',iisa + print *,'!!! iva= ',iva,' iwa= ',iwa,' icmcf= ',icmcf + print *,'!!! ivt8f= ',ivt8f + endif + itret = 94 + return + endif + + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + clon = 0.0 + clat = 0.0 + stderr = stermn ! initialize stderr to 0.1 (error_parms) + itret = 0 + xmaxwind = 0.0 + stormct = 0 + + ! It is critical to initialize the gridprs array to something + ! greater than normal atmospheric pressures (I've chosen 9999.99 + ! mb). This is so that in the sort on pressure before stormloop, + ! the top of the sorting index array will be filled with pressure + ! values from active storms, while those inactive 9999 storms + ! will fill the bottom of the sorting index array (prsindex). + + gridprs = 999999.0 + fixlon = -999.0 + fixlat = -999.0 + + if (inp%file_seq == 'multi') then + ! Each tau will have a separate file, starting with unit + ! number 200 (GRIB data) and 5200 (GRIB index file) and + ! incrementing upwards from there for each tau. + if (trkrinfo%gribver == 1) then + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + else + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + endif + else + ! All lead times are included in one big file. These values + ! for lugb and lugi will remain static for all taus. + lugb = 11 + lugi = 31 + endif + + ifh = 1 + + if ( verb .ge. 3 ) then + print *,'top of tracker, ifh= ',ifh,' ifhmax= ',ifhmax + endif + + ifhloop: do while (ifh <= ifhmax) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------*' + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* New forecast hour: ',i4,':',i2.2) + print *,'*-------------------------------------------*' + endif + + if (inp%file_seq == 'multi') then + + lugb = lugb + 1 + lugi = lugi + 1 + + call get_grib_file_name (ifh,gfilename,ifilename) + + if (use_waitfor == 'y') then + + ! First check for existence of grib file.... + + call waitfor(trim(gfilename),waitfor_gfile_status + & ,wait_min_age,wait_min_size,wait_max_wait + & ,wait_sleeptime) + if (waitfor_gfile_status /= 0) then + print *,' ' + write(6,405) + write(6,406) wait_max_wait,trim(gfilename) + 405 format('ERROR: TIMEOUT from waitfor for GRIB file.') + 406 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + ! Now check for existence of index file. Use a separate + ! max_wait time -- a much shorter one -- since once the + ! grib file is there, the index file should appear within + ! a matter of seconds. Also, the index file is much + ! smaller, so set the wait_min_size accordingly. + + wait_max_ifile_wait = 180 + wait_min_size = 500 + call waitfor(trim(ifilename),waitfor_ifile_status + & ,wait_min_age,wait_min_size,wait_max_ifile_wait + & ,wait_sleeptime) + if (waitfor_ifile_status /= 0) then + print *,' ' + write(6,415) + write(6,416) wait_max_ifile_wait,trim(ifilename) + 415 format('ERROR: TIMEOUT from waitfor for INDEX file.') + 416 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + endif + + call open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: from open_grib_files, rc= ' + & ,iret + print *,'!!! Files after hour0 are missing, ' + & ,'exiting normally' + stop 0 + endif + endif + + if (trkrinfo%inp_data_type == 'grib') then + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is CLOSED' + endif + endif + + !-------------------------------------------------------------- + ! Within this next IF statement, we deal with writing out atcf + ! records for storms for the case in which we have netcdf data, + ! but that netcdf data does not have hour0 data (as of Nov 2016, + ! this is the case for FV3 data). In this case, we write out + ! missing values for the hour0 time, and then we update the + ! guess for next lead time by extrapolating data from TC Vitals. + ! Note in the IF statement itself, "iftotalmins" is the array + ! of *user-requested* lead times, meaning that the user has + ! requested to look at hour0, but the ncfile_has_hour0 flag + ! indicates the hour0 time is not in the NetCDF data. + !-------------------------------------------------------------- + + if (ifh == 1 .and. iftotalmins(ifh) == 0 .and. + & trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') then + + null_netcdf_hour0_storm_loop: do inctcv = 1,numtcv + + call output_atcfunix (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,inctcv +c & ,0,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,inctcv + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',inctcv + write (6,431) storm(inctcv)%tcv_storm_id + & ,storm(inctcv)%tcv_storm_name + 431 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + + call advect_tcvitals_from_hour0 (slonfg,slatfg,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) + + if (iatret /= 0) then + fixlon (inctcv,ifh) = -999.0 + fixlat (inctcv,ifh) = -999.0 + stormswitch(inctcv) = 2 + cycle null_netcdf_hour0_storm_loop + endif + + stormswitch(inctcv) = 1 + + enddo null_netcdf_hour0_storm_loop + + ifh = ifh + 1 + cycle ifhloop + + endif + + !-------------------------------------------------------------- + ! Make call to getgridinfo in order to get info on the imax, + ! jmax, as well as the x- and y-increments, and also to see if + ! the grid is correctly oriented for the tracker so that the + ! data go north to south and west to east or if we need to flip + ! either the lats or the lons. + !-------------------------------------------------------------- + + if (trkrinfo%inp_data_type == 'grib') then + call getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) + else + print *,' ' + print *,'!!! ERROR: trkrinfo%inp_data_type NOT VALID ' + print *,'!!! trkrinfo%inp_data_type= ',trkrinfo%inp_data_type + print *,'!!! Should have value of grib or netcdf.' + print *,'!!! EXITING....' + print *,' ' + stop 93 + endif + + if (iggret == 0) then + if ( verb .ge. 1 ) then + print *,'TEST after getgridinfo in sub tracker, ' + & ,'iggret= ',iggret + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in getgridinfo, rc= ' + & ,iggret + endif + stop 95 + endif + + if (inp%modtyp == 'regional' .and. inp%nesttyp == 'moveable') + & then + if (glatmax == prev_latmax .and. glatmin == prev_latmin .and. + & glonmax == prev_lonmax .and. glonmin == prev_lonmin) then + ! The moveable, nested regional grid has not moved since + ! the last lead time. This could be an indication that the + ! model lost the storm and so the grid has not moved to + ! stay with the cyclone center. Set a flag to indicate this. + gridmove_status = 'stopped' + else + gridmove_status = 'moving' + endif + else + gridmove_status = 'notappl' + endif + + prev_latmax = glatmax + prev_latmin = glatmin + prev_lonmax = glonmax + prev_lonmin = glonmin + + gotten_avg_value = 'n' + +c First, allocate the working data arrays.... + + if (allocated(valid_pt)) deallocate (valid_pt) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) +cPENG----2018-06-07------------------------------- + if (allocated(ushear)) deallocate (ushear) + if (allocated(rhumid)) deallocate (rhumid) + + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + + ! Allocate all of the allocatable arrays.... + + allocate (valid_pt(imax,jmax),stat=ivpa) + allocate (zeta(imax,jmax,nlevzeta),stat=iza) + allocate (u(imax,jmax,nlevs),stat=iua) + allocate (v(imax,jmax,nlevs),stat=iva) + allocate (hgt(imax,jmax,nlevhgt),stat=iha) + allocate (slp(imax,jmax),stat=isa) + allocate (tmean(imax,jmax),stat=ita) +cPENG----2018-06-07------------------------------- + allocate (ushear(imax,jmax),stat=ish) + allocate (rhumid(imax,jmax),stat=irh) + + allocate (thick(imax,jmax,nlevthick),stat=itha) + allocate (lsmask(imax,jmax),stat=ilma) + allocate (masked_out(imax,jmax),stat=imoa) + allocate (masked_outc(imax,jmax),stat=imoca) + + ita=0 + icpsa=0 + if (phaseflag == 'y') then + if (phasescheme == 'cps' .or. phasescheme == 'both') then + if (allocated(cpshgt)) deallocate (cpshgt) + allocate (cpshgt(imax,jmax,nlevs_cps),stat=icpsa) + endif + endif + +c if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. +c & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. +c & itha /= 0 .or. imoa /= 0 .or. imoca /= 0 .or. ilma /= 0) +c & then +cPENG----2018-06-07 ------------------------ + if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. + & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. + & itha /= 0 .or. imoa /= 0 .or. imoca /= 0 .or. ilma /= 0 + & .or. ish /= 0 .or. irh /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating arrays.' + print *,'!!! iza = ',iza,' iua= ',iua,' iha= ',iha + print *,'!!! iva = ',iva,' isa= ',isa,' icpsa= ',icpsa + print *,'!!! iksa = ',iksa,' isda= ',isda,' ivpa= ',ivpa + print *,'!!! ita = ',ita,' imoa= ',imoa,' imoca= ',imoca + print *,'!!! itha = ',itha,' ilma= ',ilma + endif + itret = 94 + return + endif + + masked_out = .false. ! Initialize all pts to false at each hr + masked_outc = .false. ! Initialize all pts to false at each hr + + if ( verb .ge. 3 ) then + print *,'in beginning of tracker, imax= ',imax,' jmax= ',jmax + endif + +c Initialize all readflags to NOT FOUND for this forecast time, +c then call subroutine to read data for this forecast time. + + zeta = -9999.0 + u = -9999.0 + hgt = -9999.0 + v = -9999.0 + slp = -9999.0 + tmean = -9999.0 +cPENG----2018-06-07 ------------------------ + ushear = -9999.0 + rhumid = -9999.0 + + readflag = .FALSE. + + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: b4 getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + + if (trkrinfo%inp_data_type == 'grib') then + call getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,32) date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: after getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + +c Count how many parms were successfully read for this fcst time. +c Also, for right now, put the value of readflag into all of the +c calcparms for parameters 3 through 9. Note that in getdata we +c read in 17 parms, but in this next loop we only check the +c readflags up to maxtp (= 14 as of 7/2015). That's because +c parms 12 & 13 are for 500 mb u & v, which are not used for +c tracking, only for calculating the deep layer mean wind for +c the next guess, and parm 14 is the 300-500 mb mean temperature, +c which is used for determining storm phase. Parms 10 & 11 are +c for the near-surface winds, which are used in estimating surface +c winds near the storm, and will now also be used as a +c parameter for position estimates. Finally, parm 17 is the +c land-sea mask, which is not used as a tracking parm. + + idum = 0 + do irf = 1,nreadparms + if (readflag(irf)) idum = idum + 1 + if (irf > 2 .and. irf < 10) then + ! calcparm for parms > 9 is done further below. + do jj=1,maxstorm + calcparm(irf,jj) = readflag(irf) + enddo + endif + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Of ',nreadparms,' readable parms, you read in ',idum + print *,'parms for this fcst hour from the input grib file.' + endif + +c If not enough tracked parms were read in, exit the program.... + + if (idum == 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in subroutine tracker' + print *,'!!! Not enough tracked parms read in from getdata.' + print *,'!!! Check for a problem with the input GRIB file.' + print *,'!!! Model identifier = ',inp%model + print *,'!!! STOPPING EXECUTION FOR THIS MODEL' + endif + itret = 99 + ifhtemp = ifh + do while (ifhtemp <= ifhmax) + do istmp=1,maxstorm + fixlon (istmp,ifhtemp) = -999.0 + fixlat (istmp,ifhtemp) = -999.0 + enddo + ifhtemp = ifhtemp + 1 + enddo +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) + if (ifh == 1) then + ! Per Jim Gross (1/01), if the tracker ran but was unable + ! to get an initial fix (or, in this case, unable to get + ! the data needed to run), write out zeroes for the 00h + ! fixes to indicate that the tracker ran unsuccessfully, + ! but don't write out any subsequent forecast times + ! with zeroes.... + vradius = 0 + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initial value of 'undetermined' + do istmp = 1,maxstorm + if (stormswitch(istmp) /= 3) then + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0,-999.0,inp,istmp + & ,ifcsthour,0.0,0.0,vradius,maxstorm + & ,trkrinfo,-99.0,-99.0,-99.0,cps_vals + & ,wcore_flag,ioaxret) + call output_hfip (-999.0,-999.0,inp,istmp + & ,ifh,0.0,0.0,vradius,-99.0,ioaxret) + endif + enddo + endif + return + endif + +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for z850, z700 and mslp.... + + if (user_wants_to_track_gph850 == 'n' .or. + & user_wants_to_track_gph850 == 'N') then + do jj=1,maxstorm + calcparm(7,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_gph700 == 'n' .or. + & user_wants_to_track_gph700 == 'N') then + do jj=1,maxstorm + calcparm(8,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_mslp == 'n' .or. + & user_wants_to_track_mslp == 'N') then + do jj=1,maxstorm + calcparm(9,jj) = .FALSE. + enddo + endif + + +c Parameters 1 & 2 are abs vorticity at 850 & 700. If the data +c files had this parm at 850 & 700 (ECMWF & UKMET do NOT), then +c we don't need to re-calculate relative vorticity, we just need +c to subtract out the Coriolis component. If the files did not +c have vorticity, then we need to calculate relative vorticity. +c If we're able to read vorticity or calculate it, then set the +c vorticity calcparms to TRUE for all storms for now. + + vortloop: do ivort=1,2 + + if (ivort == 1) then + if (user_wants_to_track_zeta850 == 'n' .or. + & user_wants_to_track_zeta850 == 'N') then + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (ivort == 2) then + if (user_wants_to_track_zeta700 == 'n' .or. + & user_wants_to_track_zeta700 == 'N') then + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (readflag(ivort)) then + + call subtract_cor (imax,jmax,dy,ivort) + + do jj=1,maxstorm + calcparm(ivort,jj) = .TRUE. + enddo + else + if (ivort == 1) then + if (readflag(3) .and. readflag(4)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(1,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + endif + else + if (readflag(5) .and. readflag(6)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(2,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + endif + endif + endif + + enddo vortloop + + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for user preferences for the wind circulation +c difference at 850 & 700... + + if (readflag(3) .and. readflag(4)) then + if (user_wants_to_track_wcirc850 == 'n' .or. + & user_wants_to_track_wcirc850 == 'N') then + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(3,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + endif + + if (readflag(5) .and. readflag(6)) then + if (user_wants_to_track_wcirc700 == 'n' .or. + & user_wants_to_track_wcirc700 == 'N') then + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(5,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + endif + + +c Compute the sfc vorticity if sfc_u and sfc_v have been read in. + + if (readflag(10) .and. readflag(11)) then + + if (user_wants_to_track_wcircsfc == 'n' .or. + & user_wants_to_track_wcircsfc == 'N') then + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(10,jj) = .TRUE. + enddo + endif + + if (user_wants_to_track_zetasfc == 'n' .or. + & user_wants_to_track_zetasfc == 'N') then + do jj=1,maxstorm + calcparm(11,jj) = .FALSE. + enddo + else + ! The 3 in the next call to rvcal is to indicate the 3rd + ! level for the zeta array, which is for the surface (or + ! 10m) data. + call rvcal (imax,jmax,dx,dy,3,valid_pt) + do jj=1,maxstorm + calcparm(11,jj) = .TRUE. + enddo + endif + + else + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + calcparm(11,jj) = .FALSE. + enddo + endif + + +c Compute the thicknesses for 200-850, 200-500 and 500-850 mb +c if the gp hgt fields have been read in for 200, 500 and 850. + + if (readflag(7) .and. readflag(15) .and. readflag(16)) then + + call thickness_calc (imax,jmax,valid_pt) + + do jj=1,maxstorm + + if (user_wants_to_track_thick500850 == 'n' .or. + & user_wants_to_track_thick500850 == 'N') then + calcparm(12,jj) = .FALSE. + else + calcparm(12,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200500 == 'n' .or. + & user_wants_to_track_thick200500 == 'N') then + calcparm(13,jj) = .FALSE. + else + calcparm(13,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200850 == 'n' .or. + & user_wants_to_track_thick200850 == 'N') then + calcparm(14,jj) = .FALSE. + else + calcparm(14,jj) = .TRUE. + endif + + enddo + else + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Thickness will not be tracked since at least' + print *,'one of the gp height fields was not read in.' + print *,' readflag(7) -- 850 mb ---> ',readflag(7) + print *,' readflag(15) -- 500 mb ---> ',readflag(15) + print *,' readflag(16) -- 200 mb ---> ',readflag(16) + print *,' ' + endif + do jj=1,maxstorm + calcparm(12,jj) = .FALSE. + calcparm(13,jj) = .FALSE. + calcparm(14,jj) = .FALSE. + enddo + endif + +c --------------------------------------------------------------- +c Now call find_maxmin for the variables zeta, hgt and slp. Only +c process those storms for which stormswitch is set to 1. If a +c storm is selected to be processed, we still have to check the +c calcparm for each parameter, to make sure that the particular +c parm exists at that level and is able to be processed. +c +c The following commented-out data statements are just included +c as a reference so you can see the array positioning of the +c different parameters and levels that are read in: +c +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,sfc,sfc +c ,100,100,100,100,100/ +c data iglev /850,700,850,850,700,700,850,700,0,sfc,sfc +c ,500,500,400,500,200/ +c +c And also for reference, here are the variables / levels for +c the *tracked* parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c NOTE: For mid-latitude cases, we will track ONLY mslp, which +c is why we set all the other calcparms to 'false' just below. + + if (trkrinfo%type == 'midlat') then + do m = 1,maxstorm + calcparm(1,m) = .false. + calcparm(2,m) = .false. + calcparm(3,m) = .false. + calcparm(4,m) = .false. + calcparm(5,m) = .false. + calcparm(6,m) = .false. + calcparm(7,m) = .false. + calcparm(8,m) = .false. + calcparm(10,m) = .false. + calcparm(11,m) = .false. + calcparm(12,m) = .false. + calcparm(13,m) = .false. + calcparm(14,m) = .false. + enddo + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + call sort_storms_by_pressure (gridprs,ifh,maxstorm,prsindex + & ,issret) + if ( (ifh == 1) .or. + & (ifh == 2 .and. trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') ) then + stormct = numtcv + endif + endif + + prevstormct = stormct + tracking_previously_known_storms = .true. + + stormloop: do sl_counter = 1,maxstorm + + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initialized value of 'undetermined' + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + ist = prsindex(sl_counter) + else + ist = sl_counter + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + + if (ist == (prevstormct + 1)) then + + ! For the mid-latitude and tropical cyclogenesis cases, we + ! need to scan the mslp field to find new storms. If we + ! are at this point inside the if statement in stormloop, + ! then that means we have looped through and attempted to + ! track all storms that have already been found up to this + ! point in the forecast, and we need to scan the field for + ! any new storms at this forecast hour. If this is for + ! forecast hour = 0, then right off the bat we may be + ! scanning the field (if there were no tcvitals records + ! read in for this forecast), since ist = 1 and + ! (prevstormct + 1) = 0 + 1 = 1. All that the call just + ! below to first_ges_center does is return a rough idea + ! of the location of new lows; more specific locations are + ! obtained through the barnes analysis tracking algorithm + ! further below. + + if (readflag(9)) then + if (ifh > 1) then + ! We need the use of 2 different masks. One + ! (masked_out) is to be used when looking for new lows, + ! so that after we find a new low, we mask out the + ! surrounding area so we don't find it on a subsequent + ! search for this forecast hour. The other + ! (masked_outc) is used in the routine to check for a + ! closed contour. If checking for a closed contour + ! at, say 70W/25N, this and surrounding points may have + ! already been masked out in first_ges_center, so "N" + ! would misleadingly/incorrectly be returned from + ! check_closed_contour, so that is why we need 2 masks. + ! But now after the first forecast hour (t=0), the way + ! we have this set up is that we track previously known + ! storms first, and once we're done with them, we + ! search for new storms at that same forecast hour. + ! But when looking for new storms, we need to know the + ! positions of the previously tracked storms at this + ! current forecast hour, so we copy the masked_outc + ! array to masked_out in this case.... + + masked_out = masked_outc + + endif + call first_ges_center (imax,jmax,dx,dy,'mslp',slp + & ,'min',trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) + tracking_previously_known_storms = .false. + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In subroutine tracker, readflag' + print *,'!!! for mslp indicates that the mslp data' + print *,'!!! is not available for this forecast ' + print *,'!!! hour, and it is needed for a "midlat"' + print *,'!!! or "tcgen" run of the tracker. ' + print *,'!!! We will exit....' + print *,'!!! readflag(9) = ',readflag(9) + print *,'!!! ifh= ',ifh + print *,' ' + endif + itret = 98 + return + endif + endif + endif + + xval = 0.0 ! initialize entire xval array to 0 + isastorm = 'U' ! re-initialize flag for each time, each storm + + select case (stormswitch(ist)) + + case (1) + + vradius = 0 + + if ( verb .ge. 2 ) then + print *,' ---------------------------------------------' + print *,' | *** TOP OF STORM LOOP *** ' + print *,' | Beginning of storm loop in tracker for' + print *,' | Storm number ',ist + write (6,418) ifhours(ifh),ifclockmins(ifh) + 418 format (1x,' | Forecast hour: ',i4,':',i2.2) + print *,' | Storm name = ',storm(ist)%tcv_storm_name + print *,' | Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,' ---------------------------------------------' + print *,' ' + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3 + & ,'_',i3.3,a1,'_',i4.4,a1,'_',a3) + + endif +c First, make sure storm is within the grid boundaries... + + call check_bounds (slonfg(ist,ifh),slatfg(ist,ifh),ist,ifh + & ,trkrinfo,icbret) + if (icbret == 95) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + + if (slatfg(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + if (calcparm(1,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,1),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(1,ist),clon(ist,ifh,1),clat(ist,ifh,1) + & ,xval(1),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(2,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,2),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(2,ist),clon(ist,ifh,2),clat(ist,ifh,2) + & ,xval(2),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(7,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,1),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(7,ist) + & ,clon(ist,ifh,7),clat(ist,ifh,7),xval(7) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(8,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,2),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(8,ist) + & ,clon(ist,ifh,8),clat(ist,ifh,8),xval(8) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(9,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for mslp' + endif + + call find_maxmin (imax,jmax,dx,dy,'slp' + & ,slp,'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(9,ist) + & ,clon(ist,ifh,9),clat(ist,ifh,9),xval(9) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(11,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for sfc zeta' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,3),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(11,ist),clon(ist,ifh,11),clat(ist,ifh,11) + & ,xval(11),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 + + if (calcparm(12,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 500-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,1),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(12,ist) + & ,clon(ist,ifh,12),clat(ist,ifh,12),xval(12) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(13,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-500 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,2),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(13,ist) + & ,clon(ist,ifh,13),clat(ist,ifh,13),xval(13) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(14,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,3),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(14,ist) + & ,clon(ist,ifh,14),clat(ist,ifh,14),xval(14) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c Now get centers for wind circulation at 700 & 850 mb and +c at 10m. First, get a modified guess lat/lon position for +c wind circulation. Do this because we will be searching +c for this wind circulation center over a smaller area and +c so it's more crucial to have a better first guess position. +c This modified guess position will be an average of the first +c guess position for this time and the fix positions for this +c time from some of the other parameters. + + if (slatfg(ist,ifh) >= 0.0) then + cmaxmin = 'max' + else + cmaxmin = 'min' + endif + + if (calcparm(3,ist) .and. calcparm(4,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 850 mb ' + endif + + print *,' ' + print *,'Before first call to get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' inp%modtyp= ',inp%modtyp + print *,' cmaxmin= ',cmaxmin + print *,' nlev850= ',nlev850 + print *,' u(1,1,nlev850)= ',u(1,1,nlev850) + print *,' u(imax,jmax,nlev850)= ',u(imax,jmax,nlev850) + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' calcparm(3,ist)= ',calcparm(3,ist) + print *,' clon(ist,ifh,3)= ',clon(ist,ifh,3) + print *,' clat(ist,ifh,3)= ',clat(ist,ifh,3) + print *,' xval(3)= ',xval(3) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,141) date_time(5),date_time(6),date_time(7) + 141 format (1x,'TIMING: Before GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,850,valid_pt,calcparm(3,ist) + & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,142) date_time(5),date_time(6),date_time(7) + 142 format (1x,'TIMING: After GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,850,valid_pt,calcparm(3,ist) +c & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + endif + else + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + clon(ist,ifh,3) = 0.0 + clat(ist,ifh,3) = 0.0 + endif + endif + + if (calcparm(5,ist).and. calcparm(6,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 700 mb ' + endif + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,143) date_time(5),date_time(6),date_time(7) + 143 format (1x,'TIMING: Before GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,700,valid_pt,calcparm(5,ist) + & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,144) date_time(5),date_time(6),date_time(7) + 144 format (1x,'TIMING: After GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,700,valid_pt,calcparm(5,ist) +c & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + endif + else + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + clon(ist,ifh,5) = 0.0 + clat(ist,ifh,5) = 0.0 + endif + endif + + if (calcparm(10,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for the' + print *,'surface (10m) level' + endif + + ! NOTE: The 1020 in the call here is just a number/code + ! to indicate to the subroutine to process sfc winds. + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,145) date_time(5),date_time(6),date_time(7) + 145 format (1x,'TIMING: Before GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,1020,valid_pt,calcparm(10,ist) + & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) + & ,trkrinfo,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,146) date_time(5),date_time(6),date_time(7) + 146 format (1x,'TIMING: After GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,1020,valid_pt,calcparm(10,ist) +c & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) +c & ,trkrinfo,igucret) + + if (igwcret /= 0) then + calcparm(10,ist) = .FALSE. + endif + else + calcparm(10,ist) = .FALSE. + clon(ist,ifh,10) = 0.0 + clat(ist,ifh,10) = 0.0 + endif + endif + +c ------------------------------------------------------ +c All of the parameter center fixes have been done. Now +c average those positions together to get the best guess +c fix position. If a center fix is able to be made, then +c call subroutine get_max_wind to get the maximum near- +c surface wind near the center, and then call get_next_ges +c to get a guess position for the next forecast hour. + + if (stormswitch(ist) == 1) then + + call fixcenter (clon,clat,ist,ifh,calcparm + & ,slonfg(ist,ifh),slatfg(ist,ifh),inp + & ,stderr,fixlon,fixlat,xval,maxstorm,ifret) + + if (ifret == 0) then + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'regional')then + if (fixlon(ist,ifh) > (trkrinfo%eastbd + 7.0) .or. + & fixlon(ist,ifh) < (trkrinfo%westbd - 7.0) .or. + & fixlat(ist,ifh) > (trkrinfo%northbd + 7.0) .or. + & fixlat(ist,ifh) < (trkrinfo%southbd - 7.0)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! will NOT be made for this time due' + print *,'!!! the storm being more than 7 degrees' + print *,'!!! outside the user-specified lat/lon' + print *,'!!! bounds for this run. We will stop' + print *,'!!! tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + 432 format (1x,'!!! Fcst hr = ',i4,':',i2.2) + print *,'!!! fixlat= ',fixlat(ist,ifh) + print *,'!!! fixlon= ',fixlon(ist,ifh) + print *,'!!! User East Bound = ',trkrinfo%eastbd + print *,'!!! User West Bound = ',trkrinfo%westbd + print *,'!!! User North Bound = ',trkrinfo%northbd + print *,'!!! User South Bound = ',trkrinfo%southbd + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + endif + cycle stormloop + endif + endif + else + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + +c Just because we've found a center doesn't mean there is +c actually a storm there. I noticed in the first year that +c for some decaying or just weak storms, the tracker would +c identify a center to follow, but it may have only been +c a weak trough passing by, or something else that's not +c our storm. This next subroutine checks to see that the +c surface pressure gradient and/or tangential winds at +c 850 mb resemble a storm. It is called twice; the first +c time for MSLP, the 2nd time for 850 mb winds. We will +c apply these storm-checking criteria if either the mslp +c or v850 check come back negative. Remember, there +c is the possibility that centers could not be found for +c 1 or both of these parameters, in which case the isastorm +c flag will have a value of 'U', for "undetermined". + + isiret1 = 0; isiret2 = 0; isiret3 = 0 + + print *,' ttest, ifret= ',ifret + + if (ifret == 0) then + + print *,' ttest, calcparm(9,ist)= ',calcparm(9,ist) + + if (calcparm(9,ist)) then + + ! Do a check of the mslp gradient.... + + print *,' ttest, in IF part: ' + print *,' clon(ist,ifh,9)= ',clon(ist,ifh,9) + print *,' clat(ist,ifh,9)= ',clat(ist,ifh,9) + print *,' xval(9)= ',xval(9) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,clon(ist,ifh,9),clat(ist,ifh,9) + & ,xval(9),trkrinfo,isastorm(1),isiret1) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for mslp (e.g., + ! maybe the mslp fix was too far away from the + ! guess?), then this check isn't performed. We are + ! changing this so that the mslp gradient check will + ! still be performed, but using the mean fixlat and + ! fixlon positions as the center. Still, we first + ! need to check to see if mslp was even read in. If + ! it wasn't, then we are just out of luck. + + print *,' ttest, in ELSE part: ' + + if (trkrinfo%use_backup_mslp_grad_check == 'y' .or. + & trkrinfo%use_backup_mslp_grad_check == 'Y') then + + print *,' ttest ELSE, readflag(9)= ',readflag(9) + + if (readflag(9)) then + + print *,'ttest ELSE A, ist= ',ist,' ifh= ',ifh + print *,'ttest ELSE A, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE A, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,9999.0,ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + + print *,'ttest ELSE B, ifilret= ',ifilret + + if (ifilret == 0) then + + print *,'ttest ELSE B, ifilret= ',ifilret + print *,'ttest ELSE B, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE B, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,gridpoint_maxmin,trkrinfo,isastorm(1) + & ,isiret1) + + if (isiret1 == 0) then + ! Even though calcparm(9) is FALSE and mslp + ! will not be used for center-fixing + ! purposes, we need to fill the clat and clon + ! arrays just a few lines below so that + ! calls to fix_latlon_to_ij below do not + ! get screwed up. So, into the clat and clon + ! arrays we put the mean fixlat and fixlon + ! positions for this lead time. + clat(ist,ifh,9) = fixlat(ist,ifh) + clon(ist,ifh,9) = fixlon(ist,ifh) + xval(9) = gridpoint_maxmin + endif + + endif + + endif + + endif + + endif + + ! If we have found a valid mslp gradient, then make + ! a call to fix_latlon_to_ij to (1) get the actual + ! gridpoint value of the mslp (the value previously + ! stored in xval(9) is an area-averaged value coming + ! from the barnes analysis), and (2) to get the + ! (i,j) indices for this gridpoint to be used in the + ! call to check_closed_contour below. + ! + ! NOTE: If a mslp fix was not made, or if the mslp + ! "isastorm" flag comes back as no, we make the same + ! call to fix_latlon_to_ij, but we use the mean fix + ! position as our input to search around, and then + ! basically we just find the lowest mslp near that + ! mean fix position. There is a check on the value + ! of xinp_fixlat and xinp_fixlon to make sure that + ! they contain valid values and not just the + ! initialized -999 values. + + if (isiret1 == 0 .and. isastorm(1) == 'Y') then + xinp_fixlat = clat(ist,ifh,9) + xinp_fixlon = clon(ist,ifh,9) + if (verb >= 3) then + print *,' ttest at location C IF....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + else + xinp_fixlat = fixlat(ist,ifh) + xinp_fixlon = fixlon(ist,ifh) + if (verb >= 3) then + print *,' ttest at location C ELSE....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + endif + + if (xinp_fixlat > -99.0 .and. xinp_fixlon > -990.0) + & then + if (verb >= 3) then + print *,' ttest at location D' + endif + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,xinp_fixlon,xinp_fixlat + & ,xval(9),ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (verb >= 3) then + print *,' ttest at location E, ifilret= ',ifilret + endif + if (ifilret == 0) then + gridprs(ist,ifh) = gridpoint_maxmin + else + ! Search went out of regional grid bounds.... + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + print *,' ttest at location F' + + ! For a "tracker" case, check to see if the user has + ! requested to compute and write out the ROCI. If + ! so, then we make a call to check_closed_contour, + ! being sure to specify 999 as the number of levels + ! to check.... + + if (isiret1 == 0 .and. isastorm(1) == 'Y' .and. + & trkrinfo%type == 'tracker') then + + if (trkrinfo%want_oci) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + if (xval(9) < 1100.0) then + ! Pressure units are in mb... + prs_contint_thresh = 4.0 + elseif (xval(9) >80000.0) then + ! Pressure units are in Pa... + prs_contint_thresh = 400.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' tracker. The mslp value' + print *,' (xval(9)) is not in range.' + print *,' before call to' + print *,' check_closed_contour.' + print *,' xval(9) = ',xval(9) + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + if (trkrinfo%contint < prs_contint_thresh) then + hold_old_contint = trkrinfo%contint + trkrinfo%contint = prs_contint_thresh + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before going into routine to diagnose' + print *,'the ROCI for a tracker run, the ' + print *,'requested contour interval is being ' + print *,'adjusted up (coarser) to avoid having' + print *,'the contour check routine break and ' + print *,'return an invalid value.' + print *,'User-requested contint value (Pa) = ' + & ,hold_old_contint + print *,'Modified contint value (Pa) = ' + & ,trkrinfo%contint + endif + endif + + masked_outc = .false. + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ' + & ,rlastbar,' nm' + print *,' ' + endif + + endif + + endif + + ! For the midlat & tcgen cases, do a check to see if + ! there is a closed mslp contour. The ifix and jfix + ! values passed into check_closed_contour are the + ! values for the (i,j) at the gridpoint minimum, + ! which was obtained just above from the call to + ! fix_latlon_to_ij. + ! UPDATE 7/12/2016 tpm: A change was made to fix a + ! hole in the logic. Previously, for a genesis run + ! (type = midlat or tcgen), if a fix was not made + ! for mslp, then the isastorm(1) flag would not be + ! 'Y', and so the call to check_closed_contour in + ! the following IF statement would not be made, and + ! that would prevent the mask from getting updated + ! for this particular storm, allowing the same storm + ! to be detected when the scan for new storms takes + ! place at this lead time (i.e., after all previously- + ! known storms from the last lead time have been + ! tracked). As a fix, if that isastorm(1) flag is not + ! 'Y', then we call a new subroutine which updates the + ! mask based on the circulation at 850 mb. + + if (isastorm(1) == 'Y' .and. isiret1 == 0 .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ',rlastbar + & ,' nm' + print *,' ' + endif + + ! This next bit of code adds a second layer of closed + ! contour checking. This is to decrease the + ! occurrence of interrupted midlat and tcgen tracks, + ! which usually happens when the closed contour + ! criterion is not met for one time period. So in + ! this next code, we check to see if the ccflag was + ! 'y' for at least half the time over the last 24h. + ! For time periods shorter than 24h (e.g., the storm + ! was just detected at 144h and we are now at 156h), + ! the threshold is still that for at least half of + ! the time the system has been detected as a storm, + ! it must have a ccflag value of 'y'. + + if (ccflag == 'y') then + closed_mslp_ctr_flag(ist,ifh) = 'y' + else + closed_mslp_ctr_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & closed_mslp_ctr_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (closed_mslp_ctr_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.50) then + ccflag = 'y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE ON CLOSED CONTOUR CHECK: The' + print *,' ccflag returned for this hour was' + print *,' NO, but a check of recent ccflags' + print *,' indicates that more than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + ccflag = 'n' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!! NOTE ON CLOSED CONTOUR CHECK: The' + print *,'!! ccflag returned for this hour was' + print *,' NO, and a check of recent ccflags' + print *,' indicates that less than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + if (ccflag == 'y') then + isastorm(2) = 'Y' + else if (ccflag == 'n') then + isastorm(2) = 'N' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*---------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*---------------------------------------*' + print *,' ' + endif + + + else if (isastorm(1) /= 'Y' .and. + & calcparm(3,ist) .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + ! The isastorm(1) flag indicates that a mslp gradient + ! could not be found at this lead time, so the mask + ! cannot be updated using mslp. Instead, + ! do a check of the 850 mb wind circulation + ! surrounding the 850 wind circulation fix, and then + ! set the mask to be TRUE for all points within the + ! area where mean cyclonic Vt exceed +1 m/s.... + +c call check_closed_contour (imax,jmax,ifix,jfix,slp +c & ,valid_pt,masked_outc,ccflag,'min',trkrinfo +c & ,999,contour_info,get_last_isobar_flag,plastbar +c & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Calling mask_based_on_wind_circ at ' + & ,ifcsthour + endif + + call mask_based_on_wind_circ (imax,jmax,dx,dy,850 + & ,valid_pt,masked_outc,trkrinfo + & ,clon(ist,ifh,3),clat(ist,ifh,3),inp%modtyp + & ,imbowret) + + endif + + ! For tropical cyclones, check the avg 850 mb tangential + ! windspeed close to the storm center.... + + if (trkrinfo%type == 'tcgen' .or. + & trkrinfo%type == 'tracker') then + + had_to_try_backup_850_vt_check = 'n' + + if (calcparm(3,ist)) then + + if (verb .ge. 3) then + print *,' ' + print *,'Checking 850 mb Vt speed using 850 mb ' + print *,'wind circulation fix: ' + print *,' 850 mb wcirc fix lon= ',clon(ist,ifh,3) + print *,' 850 mb wcirc fix lat= ',clat(ist,ifh,3) + print *,' Multi-parm fix lon= ',fixlon(ist,ifh) + print *,' Multi-parm fix lat= ',fixlat(ist,ifh) + print *,' ' + endif + + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,clon(ist,ifh,3),clat(ist,ifh,3) + & ,xval(3),trkrinfo,isastorm(3),isiret3) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for 850 mb wind + ! circulation (maybe the 850 mb wind circulation fix + ! was too far away from the guess?), then this check + ! isn't performed. We are changing this so that the + ! 850 mb Vt wind speed check will still be + ! performed, but using the mean fixlat and fixlon + ! positions as the center. Still, we first need to + ! check to see if 850 mb u-comp and v-comp were even + ! read in. If they weren't, then we are just out + ! of luck. + + had_to_try_backup_850_vt_check = 'y' + isiret3 = -99 + + if (trkrinfo%use_backup_850_vt_check == 'y' .or. + & trkrinfo%use_backup_850_vt_check == 'Y') then + + if (readflag(3) .and. readflag(4)) then + + if (verb .ge. 3) then + print *,' ' + print *,'!!! NOTE: 850 mb wcirc fix not ' + print *,'available. We are instead ' + print *,'checking 850 mb Vt speed using ' + print *,'multi-parm fix position: ' + print *,' Multi-parm fix lon= ' + & ,fixlon(ist,ifh) + print *,' Multi-parm fix lat= ' + & ,fixlat(ist,ifh) + print *,' ' + endif + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,0.00,trkrinfo,isastorm(3),isiret3) + + endif + + endif + + endif + + if (calcparm(3,ist) .or. + & (had_to_try_backup_850_vt_check == 'y' .and. + & isiret3 == 0) ) then + + if (trkrinfo%type == 'tcgen') then + ! This next bit of code adds a second layer of 850 + ! mb Vt magnitude checking. This is to decrease + ! the occurrence of interrupted tcgen tracks, + ! which occasionally happens for weak storms when + ! this criterion is not met for one time period. + ! So in this next code, we check to see if the + ! vt850_flag was 'y' for at least 75% of the time + ! over the last 24h. For time periods shorter + ! than 24h (e.g., the storm was just detected at + ! 144h and we are now at 156h), the threshold is + ! still that for at least 75% of the time the + ! system has been detected as a storm, it must + ! have a vt850_flag value of 'y'. + + if (isastorm(3) == 'Y') then + vt850_flag(ist,ifh) = 'y' + else + vt850_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & vt850_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - + & fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (vt850_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / + & cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.75) then + isastorm(3) = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ NOTE ON Vt_850 CHECK: The ' + print *,' isastorm flag returned for ' + print *,' this hour was NO, but a' + print *,' check of recent vt850_flags' + print *,' indicates that more than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE ON Vt_850 CHECK: The ' + print *,'!!! isastorm flag returned for ' + print *,' this hour was NO, and a' + print *,' check of recent vt850_flags ' + print *,' indicates that less than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + endif + + endif + endif + + else + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + isastorm(1) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! could not be made for mslp, ' + print *,'!!! therefore we will stop tracking ' + print *,'!!! for this storm.' + endif + + else + isastorm(1) = 'N' + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a TC tracker case, a fix could' + print *,'!!! not be made using any tracked parms,' + print *,'!!! therefore we will stop tracking for' + print *,'!!! this storm.' + endif + + endif + + if ( verb .ge. 3 ) then + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + + endif + + if (isiret1 /= 0 .or. isiret2 /= 0 .or. isiret3 /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: One of the calls to ' + print *,'!!! is_it_a_storm produced an error.' + print *,'!!! Chances are this is from a call to ' + print *,'!!! get_ij_bounds, meaning we are too close' + print *,'!!! to a regional grid boundary to do this ' + print *,'!!! analysis. Processing will continue....' + print *,'!!! isiret1= ',isiret1,' isiret2= ',isiret2 + print *,'!!! isiret3= ',isiret3 + endif + + endif + + if (isastorm(1) == 'N' .or. isastorm(2) == 'N' .or. + & isastorm(3) == 'N') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! At least one of the isastorm flags from' + print *,'!!! subroutine is_it_a_storm is "N", so ' + print *,'!!! either we were unable to find a good ' + print *,'!!! mslp gradient and/or a valid 850 mb ' + print *,'!!! circulation for the storm at this time,' + print *,'!!! or, for the cases of midlat or tcgen ' + print *,'!!! tracking, a closed mslp contour could ' + print *,'!!! not be found, thus we will stop tracking' + print *,'!!! this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! mslp gradient flag = ',isastorm(1) + print *,'!!! closed contour flag = ',isastorm(2) + print *,'!!! 850 mb winds flag = ',isastorm(3) + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + + ! Now do another check for the tracker and tcgen cases. + ! If the isastorm flags for mslp gradient and v850 BOTH + ! came back positive AND you have been able to locate an + ! 850 mb vort center, just do a check to make sure that + ! the distance between the 850 vort center and the mslp + ! center is not too great. + + if (trkrinfo%type == 'tracker' .or. + & trkrinfo%type == 'tcgen') then + if (isastorm(1) == 'Y' .and. isastorm(3) == 'Y' .and. + & calcparm(1,ist) .and. stormswitch(ist) == 1) then + +c if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) >= 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) < 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else +c trkrinfo%max_mslp_850 = 323.0 +c endif + + call calcdist (clon(ist,ifh,9),clat(ist,ifh,9) + & ,clon(ist,ifh,1),clat(ist,ifh,1),dist + & ,degrees) + + if (dist > trkrinfo%max_mslp_850) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, the dist betw' + print *,'!!! the mslp center & the 850 zeta ' + print *,'!!! center is too great, thus we will' + print *,'!!! stop tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + print *,'!!! Actual distance (km) = ',dist + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Actual distance between the parm centers' + print *,'for 850 zeta and mslp is ',dist,' (km)' + print *,'Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + endif + + endif + endif + endif + + ! Do one final check. Check the new fix position and + ! the old fix position and calculate the speed that the + ! storm would have had to travel to get to this point. + ! If that speed exceeds a certain threshold (~60 kt), + ! assume you're tracking the wrong thing and quit. + ! Obviously, only do this for times > 00h. The check + ! in the if statement to see if the previous hour's + ! lats and lons were > -999 is for the midlat and + ! tcgen cases -- remember, they can have genesis at + ! any hour of the forecast, in which case the previous + ! forecast hour's lat & lon would be -999. + + if (ifh > 1 .and. stormswitch(ist) == 1) then + if (fixlon(ist,ifh-1) > -999.0 .and. + & fixlat(ist,ifh-1) > -999.0 ) then + + if (trkrinfo%type == 'midlat') then + xmaxspeed = maxspeed_ml + else + xmaxspeed = maxspeed_tc + endif + + call calcdist (fixlon(ist,ifh-1),fixlat(ist,ifh-1) + & ,fixlon(ist,ifh),fixlat(ist,ifh),dist + & ,degrees) + + ! convert distance from km to nm and get speed. + + distnm = dist * 0.539638 + xinterval_fhr = fhreal(ifh) - fhreal(ifh-1) + xknots = distnm / xinterval_fhr + + if (xknots > xmaxspeed) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, calculated spd' + print *,'!!! of the storm from the last position' + print *,'!!! to the current position is too high,' + print *,'!!! so we will stop tracking this storm' + print *,'!!! (For fear that we are not actually ' + print *,'!!! tracking our storm, but have instead' + print *,'!!! locked onto some other feature....)' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max speed allowed (kt) = ',xmaxspeed + print *,'!!! Actual speed (kt) = ',xknots + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'The average speed that the storm moved' + print *,'at since the previous forecast time is' + & ,xknots,' knots.' + endif + + endif + + endif + + endif + + endif + +c Now get the maximum near-surface wind speed near the storm +c center (get_max_wind). Also, call getradii to get the +c radii in each storm quadrant of gale-force, storm-force +c and hurricane force winds. + + if (readflag(10) .and. readflag(11) .and. ifret == 0 + & .and. stormswitch(ist) == 1) then + call get_max_wind (fixlon(ist,ifh),fixlat(ist,ifh) + & ,imax,jmax,dx,dy,valid_pt,levsfc + & ,xmaxwind(ist,ifh),trkrinfo,rmax,igmwret) +c if (igmwret /= 0 .and. gridmove_status == 'stopped') then + if (igmwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Return code from get_max_wind is /= 0. ' + print *,'!!! rcc= igmwret= ',igmwret + print *,'!!! Also, this is a moveable, regional grid' + print *,'!!! and the grid did not change from last' + print *,'!!! lead time to current one, so what has' + print *,'!!! likely happened is that the storm has ' + print *,'!!! moved close to the edge of the nested ' + print *,'!!! grid domain, but the nested grid itself' + print *,'!!! had stopped moving, probably because it' + print *,'!!! dropped or lost the storm.' + print *,'!!! ' + print *,'!!! TRACKING WILL STOP FOR THIS STORM' + print *,'!!! ' + endif + + stormswitch(ist) = 2 + cycle stormloop + endif + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + + ! For the radii, we encountered a problem with radmax + ! being too small. It was set at 650 km. Hurricane + ! Sandy exceeded this in the models, so the values + ! returned from getradii were close to the default + ! radmax value of 650 km (350 nm), instead of higher. + ! To fix it, we now use an iterative technique, where + ! we start with radmax as a small value (500 km). If + ! getradii returns a value for R34 in a quadrant that + ! does not exceed 0.97*radmax, then that value is ok. + ! If it does exceed 0.97*radmax, then we bump up radmax + ! by 50 km and call getradii again, looking to diagnose + ! radii only in those quadrants where the + ! need_to_expand_r34 flag = 'n'. BTW... note the + ! initial IF statement... we will only go into this + ! routine if the max wind just diagnosed for this lead + ! time is at least 34 kts (17.5 m/s). + + if (xmaxwind(ist,ifh) >= 17.5) then + + vradius = 0 + first_time_thru_getradii = .true. + r34_check_okay = 'n' + do ivr = 1,4 + need_to_expand_r34(ivr) = 'y' + enddo + radmax = 500.0 ! Initial radmax, in km + + igrct = 1 + + if ( verb .ge. 3 ) then + write (6,242) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 242 format (1x,'TIMING: b4 getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + getrad_iter_loop: do while + & (r34_check_okay == 'n' .and. radmax <= 1050.) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,244) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 244 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + call getradii (fixlon(ist,ifh),fixlat(ist,ifh),imax + & ,jmax,dx,dy,valid_pt,storm(ist)%tcv_storm_id + & ,ifcsthour,xmaxwind(ist,ifh),vradius + & ,trkrinfo,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) + + if (igrret /= 0) then + if (verb >= 3) then + print *,' ' + print *,'!!! ERROR: Return code from getradii = ' + & ,igrret + print *,'!!! Searching for radii will not be ' + print *,'!!! completed for this lead time and' + print *,'!!! all radii values will be set to ' + print *,'!!! missing.' + print *,' ' + exit getrad_iter_loop + endif + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,245) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 245 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + first_time_thru_getradii = .false. + igrct = igrct + 1 + r34_dist_thresh = 0.97 * radmax + r34_good_ct = 0 + do ivr = 1,4 + vradius_km = float(vradius(1,ivr)) / 0.5396 + if (vradius_km < r34_dist_thresh) then + r34_good_ct = r34_good_ct + 1 + need_to_expand_r34(ivr) = 'n' + endif + enddo + if (r34_good_ct == 4) then + r34_check_okay = 'y' + endif + radmax = radmax + 50.0 + enddo getrad_iter_loop + + if ( verb .ge. 3 ) then + write (6,246) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 246 format (1x,'TIMING: after getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + endif + + endif + +c If the user has requested so, then call a routine to +c determine the type of cyclone, using Bob Hart's +c cyclone phase space (CPS) algorithms. It is only used +c for times after t=0, since for the first check (of the +c "parameter B" thickness asymmetry), we need to know +c in which direction the storm is moving. Pulling that +c storm movement data off of the tcvitals is not reliable +c since the model storm may not be moving in the same +c direction as the observed storm. However, we could do +c an upgrade later where this storm movement data is +c pulled from the "genesis vitals", which are derived +c from the model forecast data itself, not the obs. + + if (phaseflag == 'y' .and. stormswitch(ist) == 1) then + wcore_flag = 'u' ! 'u' = undetermined +c call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm +c & ,cps_vals,wcore_flag,igpret) +cPENG----2018-06-07 ------------------------ + call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag + & ,wcore_mean_val,wcore_point_max,igpret) + + endif + + if (structflag == 'y' .or. ikeflag == 'y') then + call get_sfc_center (fixlon(ist,ifh),fixlat(ist,ifh) + & ,clon,clat,ist,ifh,calcparm,xsfclon + & ,xsfclat,maxstorm,igscret) + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,er_wind,sr_wind,er_vr,sr_vr + & ,er_vt,sr_vt,maxstorm,trkrinfo,igwsret) + if (igwsret == 0) then + call output_wind_structure (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),er_wind,sr_wind + & ,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iowsret) + endif + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,wfract_cov,pdf_ct_bin + & ,pdf_ct_tot,maxstorm,trkrinfo,igfwret) + if (igfwret == 0) then + call output_fract_wind (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),wfract_cov,'earth' + & ,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) + endif + endif + + if (ikeflag == 'y' .and. stormswitch(ist) == 1) then + call get_ike_stats (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,ike,sdp,wdp,maxstorm + & ,trkrinfo,igisret) + if (igisret == 0) then + call output_ike (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),ike,sdp,wdp,maxstorm + & ,ioiret) + endif + endif + +c Now print out the current fix position and intensity +c (in knots) to standard output. Conversion for m/s to +c knots (1.9427) is explained in output_atcf. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to fixcenter, fix positions at ' + write (6,442) ifhours(ifh),ifclockmins(ifh) + 442 format (1x,'forecast hour= ',i4,':',i2.2,' follow:') + print *,' ' + endif + + if (ifret == 0 .and. stormswitch(ist) == 1) then + + if ( verb .ge. 3 ) then + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + & ,int((xmaxwind(ist,ifh)*1.9427) + 0.5) + print *,' ' + endif + + ! Only call output routines every atcffreq/100 hours.... + + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + + if (leadtime_check == 0) then + + ifcsthour = ileadtime / 100 + + call output_atcfunix (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + + ! Get the storm motion vector and the speed of + ! motion so that we can output this in the + ! "atcf_sink" forecast text file. + + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'vitals',trkrinfo + & ,ignret) + else + istmdir = -999 + istmspd = -999 + ignret = 0 + endif + + if ( verb .ge. 3 ) then + write (6,617) istmspd,istmdir,ignret + 617 format (1x,'+++ RPT_STORM_MOTION: istmspd= ',i5 + & ,' istmdir= ',i5,' rcc= ',i3) + endif + + ! Call a routine to find the mean & max relative + ! vorticity near the storm at 850 & 700. These will + ! be written out to the "atcf_sink" fcst text file. + + imeanzeta = -99 + igridzeta = -99 + call get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) +cPENG----2018-06-07 ------------------------ + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + + call get_temp_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeantemp,igridtemp,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,itempt) + + call get_ushe_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanushe,igridushe,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,iushet) + + call get_rhum_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanrhum,igridrhum,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,irhumt) +cPENG----2018-06-07 ------------------------ + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (fixlon(ist,ifh) +c & ,fixlat(ist,ifh),inp,ist +c & ,ifcsthour,xmaxwind(ist,ifh) +c & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo +c & ,istmspd,istmdir,plastbar,rlastbar,rmax +c & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo + & ,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + + call output_atcf_sink (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta + & ,igridzeta,cps_vals,plastbar,rlastbar + & ,ioaxret) + + if (inp%model == 12 .and. ifcsthour == 0) then + ! Write vitals for GFS ens control analysis + call output_tcvitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,iovret) + + endif + endif + + ! The exception here is for the call to the output_hfip + ! routine, which will be called for every lead time + ! that is processed.... + + call output_hfip (fixlon(ist,ifh),fixlat(ist,ifh),inp,ist + & ,ifh,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,rmax,ioaxret) + else + + if ( verb .ge. 3 ) then + write (6,452) 'fixpos ',storm(ist)%tcv_storm_id + & ,' fhr= ',ifhours(ifh),ifclockmins(ifh) + & ,' Fix not made for this forecast hour' + 452 format (1x,a7,1x,a4,a6,i4,':',i2.2,a36) + + print *,' ' + print *,'!!! RETURN CODE from fixcenter not equal to 0,' + print *,'!!! or output from is_it_a_storm indicated the' + print *,'!!! system found was not our storm, or the ' + print *,'!!! speed calculated indicated we may have ' + print *,'!!! locked onto a different center, thus a fix' + print *,'!!! was not made for this storm at this ' + print *,'!!! forecast hour.' + print *,'!!! mslp gradient check = ',isastorm(1) + print *,'!!! mslp closed contour check = ',isastorm(2) + print *,'!!! 850 mb winds check = ',isastorm(3) + print *,'!!! fixcenter return code = ifret = ',ifret + print *,' ' + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + +c if (inp%model == 1 .or. inp%model == 8 .or. +c & inp%model == 22) then +cPENG + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + + ! For the vt=00h lead time, if the tracker failed to + ! locate a position, we are going to write out an + ! atcfunix record that contains the position, + ! intensity, mslp and 34-kt wind radii from TC Vitals + ! for this storm and initial time. Only do this for + ! the GFS or GDAS runs of the tracker. + + tcv_max_wind_ms = float(storm(ist)%tcv_vmax) + tcv_mslp_pa = float(storm(ist)%tcv_pcen) * 100.0 + + ! Convert tcvitals NE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15ne) + if (r34_from_tcv > 0.0) then + vradius(1,1) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,1) = 0 + endif + + ! Convert tcvitals SE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15se) + if (r34_from_tcv > 0.0) then + vradius(1,2) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,2) = 0 + endif + + ! Convert tcvitals SW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15sw) + if (r34_from_tcv > 0.0) then + vradius(1,3) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,3) = 0 + endif + + ! Convert tcvitals NW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15nw) + if (r34_from_tcv > 0.0) then + vradius(1,4) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,4) = 0 + endif + + ! Convert tcvitals roci from km to nm + + if (storm(ist)%tcv_penvrad > 0) then + roci_from_tcv = float(storm(ist)%tcv_penvrad) + rlastbar = roci_from_tcv * 0.5396 + else + rlastbar = -99.0 + endif + + ! Convert tcvitals pressure at roci from km to nm + + if (storm(ist)%tcv_penv > 0) then + proci_from_tcv = float(storm(ist)%tcv_penv) + plastbar = proci_from_tcv * 100.0 + else + plastbar = -99.0 + endif + + write (6,291) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + & ,atcfymdh + 291 format (1x,'NOTE: TCVITALS_USED_FOR_ATCF_F00 ' + & ,' Storm ID: ',a4,' Storm name: ',a9 + & ,' YMDH: ',i10) + + call output_atcfunix (slonfg(ist,ifh) + & ,slatfg(ist,ifh),inp,ist + & ,ifcsthour,tcv_max_wind_ms + & ,tcv_mslp_pa,vradius,maxstorm,trkrinfo + & ,plastbar,rlastbar,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + else + + ! For all other models, we print out missing + ! data values at tau=00h if the tracker was + ! unable to find the storm.... + + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + + endif + + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (trkrinfo%type == 'tracker') then + ! Update 11/11: For a 'tracker' run, i.e., one in + ! which we know that there is an observed storm in + ! the area, we will assume that there was some type + ! of problem in the initialization that prevented + ! the storm from being found. In this case, even + ! though we have written out zeroes for the 00h + ! time, we want to at least try tracking again at + ! the next lead time. Requested by HWRF folks.... + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',ist + write (6,301) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + 301 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + call get_next_ges (slonfg,slatfg,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + stormswitch(ist) = 1 + endif + + endif + cycle stormloop + endif + + +c Now get first guess for next forecast time's position. +c But first, if this is the first time level (ifh=1) and +c the user has requested that storm vitals be output (this +c is usually only done for model analyses in order to get +c an analysis position from one time to the next), we will +c write out a storm vitals record for this time level. +c Note that we have already gotten the next guess position +c info just above for the case of the repeated analysis +c data, so we'll just output the genesis vitals record. + + if (ifh <= ifhmax) then + if (ifh == 1 .and. trkrinfo%out_vit == 'y') then + call output_gen_vitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,istmspd,istmdir,iovret) + endif + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: Problem getting first guess ' + print *,'!!! position for next lead time. Return' + print *,'!!! code from call to get_next_ges = ' + print *,'!!! ignret = ',ignret + print *,'!!! Storm name = ' + & ,storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! TRACKING WILL STOP FOR THIS STORM.' + endif + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + else + istmdir = -999 + istmspd = -999 + endif + endif + + case (2) + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Case 2 in tracker for stormswitch' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + endif + + case (3) + continue + +c print *,' ' +c print *,'!!! Case 3 in tracker for stormswitch' +c print *,'!!! Storm name = ',storm(ist)%tcv_storm_name +c print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + if (leadtime_check == 0) then + ifcsthour = ileadtime / 100 + endif + if (trkrinfo%inp_data_type == 'grib') then + call output_tracker_mask (masked_outc,valid_pt,ifh + & ,ifcsthour,imax,jmax,iotmret) + endif + endif + + if(use_per_fcst_command=='y') then +c User wants us to run a command per forecast time + +! Replace %[FHOUR] with forecast hour, %[FMIN] with forecast minute. + +! The %[] format is chosen to avoid shell syntax errors if someone +! includes unknown %[] constructs. A stray , for example, +! would generate syntax errors or unexpected results in some +! shells. + +! If an unrecognized %[xxx] sequence is used, it will be retained in +! the final command. This allows the underlying command to detect +! the unreplaced %[] and use suitable default values or abort, as +! appropriate. + + pfc_final=per_fcst_command + call argreplace(pfc_final,pfc_cmd_len,'%[FHOUR]', & + & ifhours(ifh)) + call argreplace(pfc_final,pfc_cmd_len,'%[FMIN]', & + & iftotalmins(ifh)) + + if(verb.ge.2) then + print *,' ' + print *,'!!! Running per-fcst command' + print *,'!!! Unparsed = ',trim(per_fcst_command) + print *,'!!! Parsed = ',trim(pfc_final) + endif + call run_command(trim(pfc_final),pfcret) + if(pfcret/=0 .and. verb.ge.1) then + print *,' ' + print *,'!!! Non-zero exit status from per-fcst command' + print *,'!!! Command = ',trim(pfc_final) + print *,'!!! Exit status = ',pfcret + print *,'!!! Continuing anyway...' + elseif(pfcret==0 .and. verb.ge.2) then + print *,' ' + print *,'!!! Per-fcst command returned success status (0)' + endif + endif + + ifh = ifh + 1 + if (ifh > ifhmax) exit ifhloop + + if (inp%file_seq == 'multi') then + call baclose(lugb,igcret) + call baclose(lugi,iicret) + if ( verb .ge. 3 ) then + print *,'baclose return code for unit ',lugb,' = igcret = ' + & ,igcret + print *,'baclose return code for unit ',lugi,' = iicret = ' + & ,iicret + endif + endif + + enddo ifhloop +c +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) +c + 73 format ('fixpos ',a4,' fhr= ',i4,':',i2.2,' Fix position= ' + & ,f7.2,'E (',f6.2,'W)',2x,f7.2,' Max Wind= ',i3,' kts') + + if (allocated(prstemp)) deallocate (prstemp) + if (allocated(prsindex)) deallocate (prsindex) + if (allocated(iwork)) deallocate(iwork) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) +cPENG----2018-06-07 ------------------------ + if (allocated(ushear)) deallocate (ushear) + if (allocated(rhumid)) deallocate (rhumid) + + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(vt850_flag)) deallocate (vt850_flag) + if (allocated(closed_mslp_ctr_flag)) + & deallocate (closed_mslp_ctr_flag) + if (allocated(netcdf_file_time_values)) + & deallocate (netcdf_file_time_values) + if (allocated(nctotalmins)) + & deallocate (nctotalmins) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine argreplace(arg,n,name,val) + ! This subroutine is used to generate the pre-forecast-command + ! It will edit the command (argument "arg") and replace string + ! name with value val. That is how the per-forecast-command + ! has these modifications: + + ! %[FHOUR] -> replace with -> last forecast hour + ! %[FMIN] -> replace with -> last forecast minute + + implicit none + + integer, intent(in) :: n + character(n), intent(inout) :: arg + character(*), intent(in) :: name + integer, intent(in) :: val + + integer found,namelen,i1,i2 + character(n) :: out + + found=index(arg,name) + namelen=len(name) + i1=found-1 ! last char that is before name + i2=found+namelen ! index of last char in name + + if(found==0) return + + out=' ' + + if(found>1 .and. i21) then +! special case: name is at end of string +! hope the value fits... + write(out,'(A,I0)') arg(1:i1),val + elseif(i2 + & ,'... gopen_i_file= ...',a,'...') + + print *,'gopen_g_file= ',gopen_g_file,'....' + print *,'gopen_i_file= ',gopen_i_file,'....' + + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + inquire (unit=lout, opened=output_file_open) + if (output_file_open) then + iooret = 0 + else + fnameo(1:5) = "fort." + write(fnameo(6:7),'(I2)') lout + call baopenw (lout,fnameo,iooret) + endif + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + inquire (file=gopen_g_file, opened=file_open4) + if (file_open4) then + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is OPEN' + else + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is CLOSED' + endif + + inquire (file=gopen_i_file, opened=file_open5) + if (file_open5) then + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is OPEN' + else + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'gettrk baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine open_ncfile (filename,ncid) + +c ABSTRACT: This subroutine opens a netcdf file specified by the +c input file "ncfile" and returns the netcdf file id that will be +c associated with that file. +c +c INPUT: +c ncfile character full-path file netcdf name +c +c OUTPUT: +c ncfile_id integer, netcdf id assigned to the netcdf file + + implicit none + + include "netcdf.inc" + + character*(*), intent(in) :: filename + integer, intent(out) :: ncid + integer :: status + + status = nf_open (filename, NF_NOWRITE, ncid) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine open_ncfile +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine is_it_a_storm (imax,jmax,dx,dy,cparm,ist + & ,defined_pt,parmlon,parmlat + & ,parmval,trkrinfo,stormcheck,isiret) + +c ABSTRACT: This subroutine is called after the center of the storm +c has been fixed. Its purpose is to determine whether or not +c the center that was found is actually a storm, and not just some +c passing trough (this has happened in the case of decaying or weak +c storms). It's called twice -- once to check for a minimum MSLP +c gradient, and once to check for a circulation at 850 mb. The +c subroutine input parameter "cparm" determines which parameter to +c check for. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is to be checked: +c slp = mslp, for a check of mslp gradient +c v850 = tangential winds at 850 mb +c ist integer storm number (internal to the tracker) +c defined_pt Logical; bitmap indicating if valid data at that pt. +c parmlon Longitude of the max/min value for the input parameter +c parmlat Latitude of the max/min value for the input parameter +c parmval Data value at parm's max/min point (used for mslp call) +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c stormcheck Character; set to 'Y' if mslp gradient or 850 mb +c tangential winds check okay. +c isiret Return code for this subroutine. +c + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE tracked_parms; USE atcf; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + real vt,vtavg,vr,parmlat,parmlon,parmval,dist + real pthresh,vthresh,degrees,dx,dy,dell,ri,radinf + real pgradient,xmaxpgrad + character(*) cparm + logical(1) defined_pt(imax,jmax) + character*1 stormcheck + integer isiret,imax,jmax,ist,npts,ilonfix,jlatfix,igvtret + integer ibeg,iend,jbeg,jend,ivt,i,j,iix,jix,bskip,igiret + + isiret = 0 + stormcheck = 'N' + + dell = (dx+dy)/2. + +c First define the radius of influence, which depends on the +c grid spacing of the model data being used. The ceiling statement +c for npts in the first if statement is needed in case the +c resolution of the grib files eventually goes very low, down to +c say a half degree or less, in order to cover enough points in +c the search. + + if (dell < 1.24) then ! GFS, MRF, NAM, NGM, NAVGEM, GDAS, + ! GFDL, NCEP Ensemble & Ensemble + ! Relocation, SREF Ensemble + ri = ritrk_most + if (cparm == 'slp') then + radinf = 300.0 + else + radinf = 225.0 + endif + npts = ceiling(radinf/(dtk*(dx+dy)/2.)) + else if (dell >= 1.24 .and. dell < 2.49) then ! UKMET + ri = ritrk_most + radinf = 275.0 + npts = 2 + else ! ECMWF + ri = ritrk_coarse + radinf = 350.0 + npts = 1 + endif + + pthresh = trkrinfo%mslpthresh ! These are read in in + vthresh = trkrinfo%v850thresh ! subroutine read_nlists.... + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,parmlon,parmlat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij B, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij B, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print*,' ' + print*,'!!! ERROR in is_it_a_storm from call to' + print*,'!!! get_ij_bounds, stopping processing for ' + print*,'!!! storm number ',ist + endif + + isiret = 92 + return + endif + +c If the input cparm is slp, then check to see that the MSLP +c gradient in any direction from the MSLP center is at least +c 1mb / 200km, or 0.005mb/km. This is based on discussions with +c Morris & Bob, who have had good results using a 2mb/200km +c requirement. Since their model has a much finer resolution than +c all of the models we run the tracker on AND a much better +c depiction of the hurricane vortex, we do not use a requirement +c as strict as theirs, and so make the requirement only half as +c strong as theirs. +c +c If the input cparm is v850, then check to see that there is +c a circulation at 850 mb. We will do this by calculating the +c tangential wind of all points within a specified radius of +c the 850 minimum wind center, and seeing if there is a net +c average tangential wind speed of at least 5 m/s. +c +c UPDATE APRIL 2000: I've relaxed the thresholds slightly from +c 0.005 mb/km to 0.003 mb/km, and the wind threshold from +c 5 m/s to 3 m/s. Also, note that a special case for GDAS has +c been hardwired in that is weaker (0.002 mb/km and 2 m/s). +c That weaker GDAS requirement is for Qingfu's relocation stuff. +c +c UPDATE JULY 2001: The relaxed requirement put in place in +c April 2000 for the GDAS relocation has also been put in place +c for the GFS ensemble relocation. + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the loop. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, ilonfix= ',ilonfix + & ,' jlatfix= ',jlatfix + print *,'ibeg jbeg iend jend = ',ibeg,jbeg,iend,jend + print *,'cparm= ',cparm,' parmlon parmlat = ',parmlon,parmlat + print *,'parmval= ',parmval + print *,' ' + endif + + vtavg = 0.0 + ivt = 0 + + xmaxpgrad = -999.0 + + jloop: do jix = jbeg,jend,bskip + iloop: do iix = ibeg,iend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine is_it_a_storm' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + i = iix - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine ' + print *,'!!! is_it_a_storm for a non-global grid.' + print *,'!!! STOPPING....' + print *,'!!! i= ',i,' imax= ',imax + print *,' ' + endif + + stop 97 + endif + endif + + call calcdist(parmlon,parmlat,glon(i),glat(j),dist,degrees) + + if (dist > radinf .or. dist == 0.0) cycle + + if (defined_pt(i,j)) then + + if (cparm == 'slp') then + pgradient = (slp(i,j) - parmval) / dist + if (pgradient > xmaxpgrad) xmaxpgrad = pgradient + + if ( verb .ge. 3 ) then + write (6,93) i,j,glon(i),glat(j),dist,slp(i,j),pgradient + endif + + if (pgradient > pthresh) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, valid pgradient found.' + print '(a23,f8.5)',' pgradient threshold = ',pthresh + print '(a23,f8.5)',' pgradient found = ',pgradient + print *,'mslp center = ',parmlon,parmlat,parmval + print *,'pgrad loc = ',glon(i),glat(j),slp(i,j) + endif + + stormcheck = 'Y' + exit jloop + endif + endif + + if (cparm == 'v850') then + call getvrvt (parmlon,parmlat,glon(i),glat(j) + & ,u(i,j,nlev850),v(i,j,nlev850),vr,vt,igvtret) + if ( verb .ge. 3 ) then + write (6,91) i,j,glon(i),glat(j),u(i,j,nlev850) + & ,v(i,j,nlev850),vr,vt + endif + + vtavg = vtavg + vt + ivt = ivt + 1 + endif + + endif + + enddo iloop + enddo jloop + + 91 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' u= ',f8.4,' v= ',f8.4,' vr= ',f9.5,' vt= ',f9.5) + + 93 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' dist= ',f8.2,' slp= ',f10.2,' pgradient= ',f8.5) + + if (stormcheck /= 'Y' .and. cparm == 'slp') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, valid pgradient NOT FOUND.' + write (6,94) '!!! (Max pgradient less than ',pthresh,' mb/km)' + 94 format (1x,a29,5x,f8.5,a7) + write (6,95) '!!! Max pgradient (mb/km) found = ',xmaxpgrad + 95 format (1x,a34,f8.5) + print *,' ' + endif + + endif + + if (cparm == 'v850') then + + if (ivt > 0) then + vtavg = vtavg / float(ivt) + else + vtavg = 0.0 + endif + + if (parmlat > 0) then + if (vtavg >= vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (>= +',vthresh,' m/s for a NH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed +',vthresh + & ,' m/s (NH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + else + if (vtavg <= -vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (<= -',vthresh,' m/s for a SH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed -',vthresh + & ,' m/s (SH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + endif + + endif +c + return + end +c +c----------------------------------------------------------------------- +ccPENG----2018-06-07 ------------------------ +c----------------------------------------------------------------------- +c subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm +c & ,cps_vals,wcore_flag,igpret) + subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag + & ,wcore_mean_val,wcore_point_max,igpret) + +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure or phase of a cyclone. Initially, we +c will just have it use the Hart cyclone phase space (CPS) scheme. + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trkrparms; USE grid_bounds + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character wcore_flag*1,okay_to_call_cps_routines*1 +cPENG----2018-06-07 ------------------------ + real wcore_mean_val,wcore_point_max + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real cps_vals(3) + real dx,dy,paramb,vtl_slope,vtu_slope + integer imax,jmax,igpret,igcpret,ist,ifh,maxstorm + integer igvpret,igcv1ret,igcv2ret + logical(1) valid_pt(imax,jmax) +c + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,611) + write (6,613) + write (6,615) + write (6,*) ' ' + + 611 format(1x,'#-----------------------------------------------#') + 613 format(1x,'# start of routine to determine cyclone phase...#') + 615 format(1x,'#-----------------------------------------------#') + endif + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + if (ifh > 1 .or. (ifh == 1 .and. trkrinfo%type == 'tracker')) + & then + + ! This condition that ifh > 1 is so that we *not* do the cps + ! stuff for fhour=0 if it's a tcgen or midlat case, since we + ! don't know the model storm motion direction for the + ! analysis. For a regular case where type = 'tracker', we + ! have the observed storm's heading direction from tc vitals, + ! so we can use that (even though the model's storm direction + ! may differ slightly from the observed storm). This current + ! if statement and the ones below carefully check for these + ! various instances. + + okay_to_call_cps_routines = 'n' + + if (ifh > 1) then + if (fixlon(ist,ifh-1) > -990.0 .and. + & fixlat(ist,ifh-1) > -990.0) then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level ' + print *,' >< since the fixlon and fixlat at the ' + print *,' >< previous lead time are undefined.' + print *,' >< This is likely the first found position' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + endif + endif + elseif (ifh == 1 .and. trkrinfo%type == 'tracker') then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level.' + print *,' >< The likely reason is that ifh=0 and' + print *,' >< this is a genesis case, so we do not ' + print *,' >< know the storm motion direction.' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + print *,' >< trkrinfo%type ',trkrinfo%type + endif + endif + + if (okay_to_call_cps_routines == 'y') then + + ! Similarly, these next two conditions (previous lat and + ! previous lon > -999) are in there in case we're doing a + ! tcgen or midlat case and this is the *first* time level + ! within a forecast that the storm has been detected (again, + ! we don't yet know the storm heading). + + call get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'lower',vtl_slope + & ,maxstorm,igcv1ret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'upper',vtu_slope + & ,maxstorm,igcv2ret) + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh) + & ,paramb,vtl_slope,vtu_slope + endif + + cps_vals(1) = paramb + cps_vals(2) = vtl_slope + cps_vals(3) = vtu_slope + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diagnostics were requested but will NOT' + print *,' >< be performed for this time level since we ' + print *,' >< are either at the first time level for a ' + print *,' >< genesis case (type = midlat or tcgen), or' + print *,' >< we are at any time level in which for some' + print *,' >< reason the fixlon and fixlat at the' + print *,' >< previous time level are not defined.' + print *,' >< ifh= ',ifh + endif + + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diags were requested but will NOT be' + print *,' >< performed for this time level since we are at' + print *,' >< time level 1 for a genesis case ' + print *,' >< (type = midlat or tcgen) and we cannot' + print *,' >< diagnose the model direction of storm' + print *,' >< movement. ifh= ',ifh + endif + + endif + + endif + + 73 format ('cps_stats: ',a4,' lead time= ',i3,':',i2,' paramb= ' + & ,f8.2,' vtl= ',f9.2,' vtu= ',f9.2) + + + if (phasescheme == 'vtt' .or. phasescheme == 'both') then +c call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +cPENG----2018-06-07 ------------------------ + call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag + & ,wcore_mean_val,wcore_point_max,igvpret) + + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + + 631 format(1x,'#-------------------------------------------------#') + 633 format(1x,'# End of routine to determine cyclone phase... #') + 635 format(1x,'#-------------------------------------------------#') + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines "Parameter B", which determines +c the degree of thermal symmetry between the "left" and "right" +c hemispheres of a storm, in the layer between 900 and 600 mb. +c We evaluate only those points that are within 500 km of the +c storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zthicksum(2) + real rlonc,rlatc,rlonb,rlatb,xdist,degrees,d,cosarg + real st_heading,st_heading_rad,ricps,dx,dy + real pt_dir,pt_dir_rad,zthick,hemval,paramb + real zthick_right_mean,zthick_left_mean + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer left_ct,right_ct,hemis,icount,maxstorm,ip + logical(1) valid_pt(imax,jmax) +c + ricps = 500.0 + +c ----------------------------------------------------------------- +c First, determine the angle that the storm took getting from the +c last position to the current one. If this is for ifh=1 for a +c regular type=tracker case, we will just use the storm direction +c as read from the tcvitals card. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + if (d == 0.0) then + + ! Storm is stationary... + st_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + print '(a43,f9.3)' + & ,' In get_cps_paramb, model storm heading = ' + & ,st_heading + print *,' ' + endif + +c ----------------------------------------------------------------- +c Now call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_paramb from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + igcpret = 92 + return + endif + +c ----------------------------------------------------------------- +c Now loop through all of the points of the subdomain. If the +c point is further than 500 km from the storm center, discard it. +c Otherwise, evaluate the angle from the storm center to this point +c to determine the hemisphere of the point, that is, if the point +c is to the left or the right of the storm track. +c ----------------------------------------------------------------- + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the + ! loop for the evaluation of parameter B. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + left_ct = 0 + right_ct = 0 + zthicksum = 0 + icount = 0 + +c print *,'CPS CORE: ibeg= ',ibeg,' iend= ',iend +c print *,'CPS CORE: jbeg= ',jbeg,' jend= ',jend + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + icount = icount + 1 + +c print *,'CPS CORE: ist= ',ist,' ifh= ',ifh,' j= ',j,' i= ',i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_paramb, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Parameter B will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_paramb' + print *,'!!! for a non-global grid.' + print *,'!!! Parameter B will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_PARAMB....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon= ',glon(ip),' glat= ',glat(j) + print *,'!!! Parameter B will not be computed.' + print *,'!!! EXITING GET_CPS_PARAMB....' + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + + !---------------------------------------------------------- + ! Calculate angle from storm center to point, in a 0-360 + ! framework, clockwise positive. + !---------------------------------------------------------- + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-fixlon(ist,ifh)) * dtr + rlatb = fixlat(ist,ifh) * dtr + d = degrees * dtr + + if (d > 0.) then + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_dir_rad = acos(cosarg) + else + pt_dir_rad = 2*pi - acos(cosarg) + endif + else + pt_dir_rad = 0.0 + endif + + pt_dir = pt_dir_rad / dtr + + !------------------------------------------------------------ + ! Based on the angle that the point is from the storm center, + ! determine if the point is to the left or the right of the + ! storm track. + !------------------------------------------------------------ + + if (st_heading >= 180.0) then + if ((st_heading - pt_dir) > 0.0 .and. + & (st_heading - pt_dir) <= 180) then + hemis = 2 + left_ct = left_ct + 1 + else + hemis = 1 + right_ct = right_ct + 1 + endif + else + if ((pt_dir - st_heading) > 0.0 .and. + & (pt_dir - st_heading) <= 180) then + hemis = 1 + right_ct = right_ct + 1 + else + hemis = 2 + left_ct = left_ct + 1 + endif + endif + + !------------------------------------------------------------ + ! Calculate the 600-900 mb thickness at this point and add + ! the thickness value to the array for the correct "storm + ! hemisphere". + !------------------------------------------------------------ + + zthick = cpshgt(ip,j,7) - cpshgt(ip,j,1) + zthicksum(hemis) = zthicksum(hemis) + zthick + + if ( verb .ge. 3 ) then + write (6,51) rlonb/dtr,rlatb/dtr,rlonc/dtr,rlatc/dtr + & ,st_heading,pt_dir,hemis,zthick + endif + + enddo iloop + enddo jloop + + 51 format (1x,'stlon stlat = ',2(f6.2,2x),' ptlon ptlat = ' + & ,2(f6.2,2x),' sthead= ',f6.2,' ptdir= ',f6.2,' hemis= ' + & ,i1,' zthick= ',f7.2) + +c ------------------------------------------------------------------ +c Now calculate parameter B. The hemval parameter = +1 for storms +c in the Northern Hemisphere and -1 for Southern Hemisphere storms. +c ------------------------------------------------------------------ + + zthick_right_mean = zthicksum(1) / float(right_ct) + zthick_left_mean = zthicksum(2) / float(left_ct) + + if (fixlat(ist,ifh) < 0.0) then + hemval = -1.0 + else + hemval = 1.0 + endif + + paramb = hemval * (zthick_right_mean - zthick_left_mean) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' right_ct= ',right_ct,' left_ct= ',left_ct + print *,' zthicksum(1)= ',zthicksum(1) + print *,' zthicksum(2)= ',zthicksum(2) + print *,' zthick_right_mean= ',zthick_right_mean + print *,' zthick_left_mean= ',zthick_left_mean + print *,' hemval= ',hemval + print *,' END of get_cps_paramb, paramb= ',paramb + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,clayer,vth_slope,maxstorm,igcvret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines the thermal wind profile for +c either the lower troposphere (i.e., between 600 and 900 mb) or the +c upper troposphere (i.e., between 300 and 600 mb). We evaluate +c only those points that are within 500 km of the storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character clayer*5 + real tmp1,tmp2,tmp3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zmax(7),zmin(7),zdiff(7),xlolevs(7),xhilevs(7),plev(7) + real dlnp(7),dzdlnp(7),dz(7),lnp(7) + real vth_slope,xdist,degrees,d,cosarg + real ricps,dx,dy,R2 + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j,k,kix + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igcvret,igiret + integer kbeg,kend,maxstorm,ip + logical(1) valid_pt(imax,jmax) + + data xlolevs /900.,850.,800.,750.,700.,650.,600./ + data xhilevs /600.,550.,500.,450.,400.,350.,300./ +c data xlolevs /90000.,85000.,80000.,75000.,70000.,65000.,60000./ +c data xhilevs /60000.,55000.,50000.,45000.,40000.,35000.,30000./ +c + ricps = 500.0 + plev = 0.0 + + if (clayer == 'lower') then + kbeg = 1 + kend = 7 + plev = xlolevs + else + kbeg = 7 + kend = 13 + plev = xhilevs + endif + +c ----------------------------------------------------------------- +c First, call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_vtl from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + igcvret = 92 + return + endif + +c ------------------------------------------------------------------ +c Now loop through all of the points of the subdomain at each level. +c If a point is further than 500 km from the storm center, discard +c it. Otherwise, evaluate the gp height at the point to determine +c if it is a max or a min for the given level. Store the max and +c min height at each level in an array. +c ------------------------------------------------------------------ + +c ! We will want to speed things up for finer resolution grids. +c ! We can do this by skipping some of the points in the +c ! loop for the evaluation of parameter B. +c +c if ((dx+dy)/2. > 0.20) then +c bskip = 1 +c else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then +c bskip = 2 +c else if ((dx+dy)/2. <= 0.10) then +c bskip = 3 +c endif + + bskip = 1 ! Don't do any skipping for now.... + + zmax = -9999999.0 + zmin = 9999999.0 + zdiff = 0.0 + lnp = 0.0 + + levloop: do k = kbeg,kend + + if (kbeg == 7) then + ! processing upper layers (600-300 mb) + kix = k - 6 + else + ! processing lower layers (900-600 mb) + kix = k + endif + + lnp(kix) = log(plev(kix)) + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_vth, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_vth' + print *,'!!! for a non-global grid.' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_VTH....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j,' k= ',k + & ,' clayer= ',clayer + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon(ip)= ',glon(ip),' glat= ',glat(j) + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! EXITING GET_CPS_VTH....' + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + + tmp1 = zmax(kix) + tmp2 = cpshgt(ip,j,k) + tmp3 = zmin(kix) + + zmax(kix) = max(tmp1,tmp2) + zmin(kix) = min(tmp3,tmp2) + +c zmax(kix) = max(zmax(kix),cpshgt(ip,j,k)) +c zmin(kix) = min(zmin(kix),cpshgt(ip,j,k)) + + enddo iloop + enddo jloop + + zdiff(kix) = zmax(kix) - zmin(kix) + + enddo levloop + +c ------------------------------------------------------------------ +c Now calculate the vertical derivative of the gp height, that is, +c d(dz)/d(ln(p)). Here, zdiff is the gp height perturbation at a +c given level, calculated in the loop above; dz is the vertical +c change in that perturbation from one level to the next. +c ------------------------------------------------------------------ + + dz = 0.0 + dlnp = 0.0 + dzdlnp = 0.0 + + do k = 2,7 + dz(k) = zdiff(k) - zdiff(k-1) + dlnp(k) = log(plev(k)) - log(plev(k-1)) + dzdlnp(k) = dz(k) / dlnp(k) + enddo + +c ------------------------------------------------------------------ +c Now call a correlation routine to get the slope of a regression +c line. The independent variable that we input is dlnp, the change +c in log of pressure with height. The dependent variable is +c dzdlnp, the vertical change in the height perturbation with +c respect to the change in pressure. The slope that is returned +c defines whether we've got a cold core or warm core system. +c See Hart (MWR, April 2003, Vol 131, pp. 585-616) for more +c details, specifically his Fig. 3 and the discussion surrounding. +c Note that in the call to calccorr, we are sending only 6 of the +c 7 elements of the dlnp and dzdlnp arrays, beginning with the +c 2nd element of each. That's because the first array value for +c each of those arrays is empty, since in the loop just above, we +c start with kbeg+1, not kbeg. +c ------------------------------------------------------------------ + + call calccorr(lnp(2),zdiff(2),6,R2,vth_slope) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ In get_cps_vth, values for vth follow for ' + & ,'lead time= ',ifhours(ifh),':',ifclockmins(ifh),' ' + & ,storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' ... clayer = ',clayer + print *,' ' + endif + + do k = kbeg,kend + + if (kbeg == 7) then + kix = k - 6 + else + kix = k + endif + + if ( verb .ge. 3 ) then + print *,' ' + write (6,31) k,plev(kix),zmax(kix),zmin(kix),zdiff(kix) + if (kix > 1) then + write (6,32) plev(kix),log(plev(kix)) + & ,plev(kix-1),log(plev(kix-1)) + write (6,33) dz(kix),dlnp(kix),dzdlnp(kix) + else + write (6,34) + endif + endif + + enddo + + 31 format (1x,' +++ k= ',i2,' press= ',f8.1,' zmax= ',f7.2 + & ,' zmin= ',f7.2,' zdiff= ',f7.2) + 32 format (1x,' ln(',f7.1,')= ',f9.6,' ln(',f7.1,')= ',f9.6) + 33 format (1x,' dz= ',f10.2,' dlnp= ',f13.6,' dzdlnp= ',f12.3) + 34 format (1x,' --- First level... no derivatives done...') +c + return + end +c +C---------------------------------------------------- +C +C---------------------------------------------------- + subroutine calccorr(xdat,ydat,numpts,R2,slope) +c +c This subroutine is the main driver for a series of +c other subroutines below this that will calculate the +c correlation between two input arrays, xdat and ydat. +c +c INPUT: +c xdat array of x (independent) data points +c ydat array of y (dependent) data points +c numpts number of elements in each of xdat and ydat +c +c OUTPUT: +c R2 R-squared, the coefficient of determination +c slope Slope of regression line +c +c xdiff array of points for xdat - xmean +c ydiff array of points for ydat - ymean +c yestim array of regression-estimated points +c yresid array of residuals (ydat(i) - yestim(i)) + + USE verbose_output + + implicit none + + real xdat(numpts),ydat(numpts) + real xdiff(numpts),ydiff(numpts) + real yestim(numpts),yresid(numpts) + real xmean,ymean,slope,yint,R2 + integer numpts,i + +c + call getmean(xdat,numpts,xmean) + call getmean(ydat,numpts,ymean) +c + call getdiff(xdat,numpts,xmean,xdiff) + call getdiff(ydat,numpts,ymean,ydiff) +c + call getslope(xdiff,ydiff,numpts,slope) + yint = ymean - slope * xmean +c + call getyestim(xdat,slope,yint,numpts,yestim) + call getresid(ydat,yestim,numpts,yresid) +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * CPS Thermal wind regression details * ' + print *,' *--------------------------------------------------* ' + endif + + call getcorr(yresid,ydiff,numpts,R2) + + if ( verb .ge. 3 ) then + print *,' i ydat xdat ydiff xdiff e' + & ,' e2 ydiff2' + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + do i = 1,numpts + write(6,'(2x,i3,2x,f7.2,2x,f7.4,2x,f7.2,2x,f7.4,3(2x,f7.2))') + & i,ydat(i),xdat(i),ydiff(i) + & ,xdiff(i),yresid(i),yresid(i)*yresid(i) + & ,ydiff(i)*ydiff(i) + enddo + + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + print *,' ' + write (6,'(1x,a13,f9.3,3x,a5,f7.2)') ' means: y: ',ymean + & ,' x: ',xmean + + write (6,*) ' ' + write (6,30) 'slope= ',slope,' y-intercept = ',yint + 30 format (2x,a7,f10.3,a23,f10.3) + if (slope .gt. 0.0) then + write(6,40) 'Regression equation: Y = ',yint,' + ',slope + else + write(6,40) 'Regression equation: Y = ',yint,' - ' + & ,abs(slope) + endif + 40 format (2x,a27,f8.2,a3,f8.2,'X') +c + print *,' ' + write (6,'(1x,a17,f7.4,5x,a7,f7.4)') ' R2(r_squared) = ',R2 + & ,' r = ',sqrt(R2) + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * End of regression details * ' + print *,' *--------------------------------------------------* ' + endif + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getmean(xarr,inum,zmean) +c +c This subroutine is part of the correlation calculation, +c and it simply returns the mean of the input array, xarr. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c +c OUTPUT: +c zmean mean of data values in xarr + + implicit none + + real xarr(inum) + real xsum,zmean + integer i,inum +c + xsum = 0.0 + do i = 1,inum + xsum = xsum + xarr(i) + enddo +c + zmean = xsum / float(MAX(inum,1)) +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getdiff(xarr,inum,zmean,zdiff) +c +c This subroutine is part of the correlation calculation, +c and it returns in the array zdiff the difference values +c between each member of the input array xarr and the +c mean value, zmean. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c zmean mean of input array (xarr) +c +c OUTPUT: +c zdiff array containing xarr(i) - zmean + + implicit none + + real xarr(inum),zdiff(inum) + real zmean + integer i,inum +c + do i = 1,inum + zdiff(i) = xarr(i) - zmean + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + + subroutine getslope(xarr,yarr,inum,slope) +c +c This subroutine is part of the correlation calculation, +c and it returns the slope of the regression line. +c +c INPUT: +c xarr input array of xdiffs (x - xmean) +c yarr input array of ydiffs (y - ymean) +c inum number of points in x & y arrays +c +c OUTPUT: +c slope slope of regression line + + real xarr(inum),yarr(inum) + real slope,sumxy,sumx2 + integer i,inum + +c First sum up the xarr*yarr products.... + + sumxy = 0.0 + do i = 1,inum + sumxy = sumxy + xarr(i) * yarr(i) + enddo + +c Now sum up the x-squared terms.... + + sumx2 = 0.0 + do i = 1,inum + sumx2 = sumx2 + xarr(i) * xarr(i) + enddo + +c Now get the slope.... + + slope = sumxy / sumx2 + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getyestim(xarr,slope,yint,inum,yestim) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the predicted y-values using the +c regression equation that has been calculated. +c +c INPUT: +c xarr array of x data points +c slope slope of the calculated regression line +c yint y-intercept of the calculated regression line +c inum number of input points +c +c OUTPUT: +c yestim array of y pts estimated from regression eqn. + + implicit none + + real xarr(inum),yestim(inum) + real slope,yint + integer i,inum +c + do i = 1,inum + yestim(i) = yint + xarr(i) * slope + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getresid(yarr,yestim,inum,yresid) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the residual values between the +c input y data points and the y-estim predicted y values. +c +c INPUT: +c yarr array of y data points +c yestim array of y pts estimated from regression eqn. +c inum number of input points +c +c OUTPUT: +c yresid array of residuals (ydat(i) - yestim(i)) + + implicit none + + real yarr(inum),yestim(inum),yresid(inum) + integer i,inum +c + do i = 1,inum + yresid(i) = yarr(i) - yestim(i) + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getcorr(yresid,ydiff,inum,R2) +c +c This subroutine is part of the correlation calculation, +c and it does the actual correlation calculation. +c +c INPUT: +c yresid array of residuals (ydat(i) - yestim(i)) +c ydiff array of points for ydat - ymean +c inum number of points in the arrays +c +c OUTPUT: +c R2 R-squared, the coefficient of determination + + USE verbose_output + + implicit none + + real yresid(inum),ydiff(inum) + real R2,sumyresid,sumydiff + integer i,inum +c + sumyresid = 0.0 + sumydiff = 0.0 + + do i = 1,inum + sumyresid = sumyresid + yresid(i) * yresid(i) + sumydiff = sumydiff + ydiff(i) * ydiff(i) + enddo + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,30) 'Sum of y-residuals squared (e2) = ',sumyresid + write (6,30) 'Sum of y-diffs squared (ydiff2) = ',sumydiff + write (6,*) ' ' + 30 format (1x,a35,f15.2) + endif + +c if (sumydiff == 0.0) then +c R2=1.0 +c else +c R2 = 1 - sumyresid / sumydiff +c endif +c PENG 05/14/2018 Bug-fixed for R2 calculation with FENS job crashed. + if (sumyresid .lt. sumydiff) then + if (sumydiff .le. 0.000001) then + R2 = 1.0 + else + R2 = 1 - sumyresid / sumydiff + endif + else + R2=0.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- +c subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +cPENG----2018-06-07 ------------------------ + subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag + & ,wcore_mean_val,wcore_point_max,igvpret) + +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. Here, we are only looking +c at the mid-to-upper tropospheric warm anomaly at the center of +c the storm. The temperature data that we are searching through in +c the tmean array should be the 300-500 mb mean temperature data. +c The criteria in this algorithm are based loosely on Vitart's +c criteria for warm core checking, but the nuts & bolts of the +c subroutine use algorithms from this tracker, including the barnes +c analysis. First, we locate the warm core with the find_maxmin +c routine. Then we use the check_closed_contour routine to see if +c there is a closed temperature contour surrounding the warm core. +c +c INPUT: +c inp +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c inp contains input date and model number information +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c ist integer storm number (internal to the tracker) +c ifh integer index for lead time +c trkrinfo derived type containing grid info on user boundaries +c fixlon array containing found fix longitudes +c fixlat array containing found fix latitudes +c valid_pt Logical; bitmap indicating if valid data at that pt. +c maxstorm maximum # of storms to be handled +c +c OUTPUT: +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c igvpret Return code for this subroutine. +c +c LOCAL: +c wcore_mean_val barnes-averaged value of the temperature at the +c location where the tracker found the warm core. +c wcore_point_max max temperature found at a gridpoint near the +c location where the tracker found the warm core using +c barnes analysis. + + USE set_max_parms; USE grid_bounds; USE trkrparms; USE contours + USE tracked_parms; USE gen_vitals; USE def_vitals; USE inparms + USE phase + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo,wcore_trkrinfo + type (cint_stuff) wcore_contour_info + type (datecard) inp + + character*1 get_last_contour_flag,wcore_flag + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dx,dy,wcore_mean_val,wcore_mean_lon,wcore_mean_lat + real wcore_point_max,tlastcont,rlastcont,tlastout,rlastout + integer imax,jmax,igvpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer icount,maxstorm,ip,ifmret,ifilret,ifix,jfix,icccret + integer num_check_conts + logical(1) valid_pt(imax,jmax),compflag,wcore_mask(imax,jmax) + logical(1) output_file_open +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of get_vtt_phase *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for warm core at hour ',i4,':',i2.2) + write (6,103) wcore_depth + 103 format (1x,'* Warm core depth threshold (wcore_depth) = ',f7.2) + print *,'*-------------------------------------------------*' + endif + +c ------------------------------------------------------------ +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + + wcore_mask = .false. + wcore_mean_lon = -999.0 + wcore_mean_lat = -999.0 + wcore_trkrinfo = trkrinfo ! set equal to values from trkrinfo... + wcore_trkrinfo%contint = wcore_depth ! ...except use the warm + ! core contour interval specified by + ! the user in the extrkr.sh script. + +c ------------------------------------------------------------ +c First, call find_maxmin to locate the warm core + + call find_maxmin (imax,jmax,dx,dy,'tmp' + & ,tmean,'max',ist,fixlon(ist,ifh),fixlat(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,compflag + & ,wcore_mean_lon,wcore_mean_lat,wcore_mean_val + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + + if (verb .ge. 3) then + print *,' ' + print *,'After call to find_maxmin for wcore, ifmret= ',ifmret + print *,' wcore_mean_val= ',wcore_mean_val + endif + +c ------------------------------------------------------------ +c Once find_maxmin returns a value and a location for the +c barnes-averaged value of a warm core, then make a call to +c fix_latlon_to_ij to (1) get the actual gridpoint value of the +c temperature (the value stored in wcore_mean_val is an +c area-averaged value coming from the barnes analysis), and +c (2) to get the (i,j) indeces for this gridpoint to be used in +c the call to check_closed_contour below. + + if (wcore_mean_lat > -99.0 .and. wcore_mean_lon > -990.0) then + call fix_latlon_to_ij (imax,jmax,dx,dy,tmean,'max' + & ,valid_pt,wcore_mean_lon,wcore_mean_lat + & ,wcore_mean_val,ifix,jfix,wcore_point_max,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Warm core stats: ' + write (6,105) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_mean_lon,360.-wcore_mean_lon + & ,wcore_mean_lat,wcore_mean_val + write (6,106) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,ifix,jfix,wcore_point_max + endif + + else + ! Search went out of regional grid bounds.... + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN get_vtt_phase. The call to ' + print *,'!!! fix_latlon_to_ij returned a non-zero return ' + print *,'!!! code, which means that the search for the fix' + print *,'!!! i and j went out of bounds for a regional ' + print *,'!!! grid. This should have been caught in a ' + print *,'!!! previous call to find_maxmin for one of the ' + print *,'!!! various fix parms. In any event, we will not' + print *,'!!! search for a warm core for this storm and ' + print *,'!!! lead time.' + print *,' ' + write (6,115) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,'U',-999.99,-9999.99 + endif + + igvpret = 95 + wcore_flag = 'u' + return + endif + endif + + 105 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' mean_lon: ',f7.2,'E' + & ,1x,'(',f7.2,'W)',2x,'mean_lat: ',f7.2,2x + & ,'wcore_mean_val(K): ',f12.3) + 106 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' ifix: ',i5,2x + & ,' jfix: ',i5,2x,'wcore_point_max(K): ',f12.3) + + +c ------------------------------------------------------------ +c The Vitart scheme specifies that the temperature must decrease +c by at least 1.0C in all directions from the warm core center +c within a distance of 8 deg. A rigorous check of this criterion +c is performed here by utilizing the check_closed_contour routine. +c If we have a closed contour in the temperature field +c surrounding the warm core (using a 1 deg K interval), that +c criterion is satisfied. For diagnostic purposes, we set the +c value of num_check_conts to 999 in order to keep searching for +c all contours surrounding the warm core, and this allows us to +c get an idea of the "depth" or magnitude of the warm core when +c the tlastcont and rlastcont values are returned. + + wcore_contour_info%numcont = maxconts + num_check_conts = 999 + + get_last_contour_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,tmean + & ,valid_pt,wcore_mask,wcore_flag,'max',wcore_trkrinfo + & ,num_check_conts,wcore_contour_info,get_last_contour_flag + & ,tlastcont,rlastcont,icccret) + + if (wcore_flag == 'y') then + tlastout = tlastcont + rlastout = rlastcont/0.539638 + else + tlastout = -999.0 + rlastout = -9999.0 + endif + + if ( verb .ge. 3 ) then + write (6,115) storm(ist)%tcv_storm_id,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_flag,tlastout,rlastout + + 115 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2 + & ,' wcore_flag= ',a1,2x,' Temp of last contour(K) = ' + & ,f7.2,2x,'Radius of last contour(km) = ',f8.2) + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_sfc_center (xmeanlon,xmeanlat,clon + & ,clat,ist,ifh,calcparm,xsfclon,xsfclat + & ,maxstorm,igscret) +c +c ABSTRACT: This subroutine computes a modified lat/lon fix position +c to use as the input center position for the subroutines that +c follow which calculate surface-wind related values. The reason +c for this is that since we are concerned with the positioning of +c low-level wind features (e.g., rmax), we want the center position +c to be based solely on low-level features. We'll use mslp and the +c min in the sfc wind speed. If a center fix was unable to be made +c at this forecast hour for mslp and low-level winds, then we will +c stick with just using the mean position we got using all the other +c parameters. +c +c INPUT: +c xmeanlon The mean center longitude computed from all the various +c parameter fixes found in array clon +c xmeanlat The mean center latitude computed from all the various +c parameter fixes found in array clat +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Index for storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c (if a parameter fix could not be made at this forecast +c hour, then calcparm is set to false for this time for +c that parameter). +c maxstorm Maximum number of storms that can be tracked +c +c OUTPUT: +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c igscret Return code from this subroutine + + USE set_max_parms + USE verbose_output + + implicit none + + integer ist,ifh,ipct,igscret,maxstorm + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmeanlon,xmeanlat + real xsfclon,xsfclat,xlonsum,xlatsum + logical(1) calcparm(maxtp,maxstorm) + + ipct = 0 + xlonsum = 0.0 + xlatsum = 0.0 + + ! Do NOT include MSLP for the surface center at this time. +c if (calcparm(9,ist)) then +c ipct = ipct + 1 +c xlonsum = xlonsum + clon(ist,ifh,9) +c xlatsum = xlatsum + clat(ist,ifh,9) +c endif + + if (calcparm(10,ist)) then +c ! NOTE: Put double weighting on surface wind center if +c ! the tracker was able to find a center for it.... +c ipct = ipct + 2 +c xlonsum = xlonsum + 2.*clon(ist,ifh,10) +c xlatsum = xlatsum + 2.*clat(ist,ifh,10) + ! Just use single weighting for the sfc wcirc fix + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,10) + xlatsum = xlatsum + clat(ist,ifh,10) + endif + + if (calcparm(11,ist)) then + ! This is for the sfc vorticity center.... + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,11) + xlatsum = xlatsum + clat(ist,ifh,11) + endif + + if (ipct > 0) then + xsfclon = xlonsum / float(ipct) + xsfclat = xlatsum / float(ipct) + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In get_fract_wind_cov, CANNOT get modified fix ' + print *,'!!! position because the parameter fixes for mslp' + print *,'!!! and the sfc winds could not be obtained at this' + print *,'!!! forecast hour. ist= ',ist,' ifh= ',ifh + print *,'!!! We will use the fixlon and fixlat values for' + print *,'!!! this forecast hour.' + endif + + xsfclon = xmeanlon + xsfclat = xmeanlat + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ In get_sfc_center, modified fix (mslp + sfc_winds)' + print *,'+++ position follows: ' + print *,'+++ ' + print *,'+++ mslp: lon: ',clon(ist,ifh,9),' lat: ' + & ,clat(ist,ifh,9) + print *,'+++ sfc_winds: lon: ',clon(ist,ifh,10),' lat: ' + & ,clat(ist,ifh,10) + print *,'+++ sfc_vorticity: lon: ',clon(ist,ifh,11),' lat: ' + & ,clat(ist,ifh,11) + print *,'+++ multi-parm mean: lon: ',xmeanlon,' lat: ' + & ,xmeanlat + print *,'+++ sfc-only mean: lon: ',xsfclon,' lat: ',xsfclat + endif + + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,er_wind,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm + & ,trkrinfo,igwsret) +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure of the low level winds of a cyclone. +c The algorithm will search out at specified distances from the +c storm center along arcs in each quadrant of the storm, +c evaluating the winds every 15 degrees along the arc. In each +c arc, start 7.5 degrees in, then make stops at 22.5, 37.5, +c 52.5, 67.5, and 82.5 degrees. At each of those points, we +c will bilinearly interpolate the winds to the points along those +c arcs. Then we compute a quadrant average of the wing magnitude, +c as well as the mean Vt and Vr values. This will be done +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 earth-relative +c quadrants: NE, SE, SW and NW. For the storm-relative estimates, +c these mean values of the wind will be computed for the same +c relative quadrants (front-right, back-right, back-left, front- +c left, but with respect (positive clockwise) to the +c direction of storm motion. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated +c er_wind: Quadrant winds in earth-relative framework +c sr_wind: Quadrant winds in storm-relative framework +c er_vr: Quadrant radial winds in earth-relative framework +c sr_vr: Quadrant radial winds in storm-relative framework +c er_vt: Quadrant tangential winds in earth-relative framework +c sr_vt: Quadrant tangential winds in storm-relative framework + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,num_qtr_azim=6 + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igvtret,ipct,maxstorm,iazim,azimuth_ct + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,xsfclon,xsfclat,wmag,wmag_sum + real vr,vt,vr_sum,vt_sum + logical(1) valid_pt(imax,jmax) +c + data rdist/10.,25.,50.,75.,100.,125.,150.,200.,250.,300.,350. + & ,400.,450.,500./ + + igwsret = 0 + + er_wind = 0.0 + sr_wind = 0.0 + er_vr = 0.0 + er_vt = 0.0 + sr_vr = 0.0 + sr_vt = 0.0 + +c ----------------------------------------------------------------- +c Now determine the angle that the storm took getting from the +c last position to the current one. If this is the initial time, +c use the observed direction of motion from the TC Vitals. This +c may not match up with the model storm's initial direction of +c motion, but it is all we have available to us in order to get +c a heading estimate for the initial time. This storm heading +c information will be used for the storm-relative profiles. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_wind_structure, fhr= ',fhreal(ifh) + & ,' ',storm(ist)%tcv_storm_id + & ,' ',storm(ist)%tcv_storm_name + print '(a25,a23,f9.3)',' In get_wind_structure, ' + & ,' model storm heading = ',st_heading + print *,' ' + endif + + endif + +c ----------------------------------------------------------------- +c Get the profiles for the earth-relative coordinate system. +c Start with NE, then SE, SW, and NW. First go through +c radiusloop, which goes from one radial distance to the next, +c then do the quadloop, which goes through each quadrant, and +c then within each quadrant, the qtr_azimloop goes through for +c six points along an arc, spaced 15 degrees apart, starting at +c 7.5 degrees clockwise from the north. +c ----------------------------------------------------------------- + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *****************************************************' + print *,' Wind Structure: distbear bilin interp starts here.' + print *,' *****************************************************' + print *,' ' + endif + + radiusloop1: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- ER structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop1: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + ! In each quadrant, run through six points along an + ! arc and evaluate the winds. + + qtr_azimloop1: do iazim = 1,num_qtr_azim + + bear = ((iquad-1) * 90.) + ((iazim-1) * 15.) + 7.5 + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' earth-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,' ' + print '(5(a10,f7.2))',' sfclat= ',xsfclat + & ,' sfclon= ',xsfclon + & ,' rdist= ',rdist(idist),' targlat= ',targlat + & ,' targlon= ',targlon + print '(19x,a8,f7.2,35x,a9,f7.2)','sfclon= ',360.-xsfclon + & ,'targlon= ',360.-targlon + endif + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop1 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + er_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + er_vr(iquad,idist) = vr_sum / float(azimuth_ct) + er_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + er_wind(iquad,idist) = -999.0 + er_vr(iquad,idist) = -999.0 + er_vt(iquad,idist) = -999.0 + endif + + enddo quadloop1 + + enddo radiusloop1 + +c ----------------------------------------------------------------- +c Get the profiles for the storm-relative coordinate system. +c Start with the front-right quadrant and go clockwise through +c back-right, back-left and front-left. +c ----------------------------------------------------------------- + + radiusloop2: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- SR structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop2: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + qtr_azimloop2: do iazim = 1,num_qtr_azim + +c temp_bear = st_heading + ((iquad-1) * 90.) + 45. + + temp_bear = st_heading + ((iquad-1) * 90.) + & + ((iazim-1) * 15.) + 7.5 + bear = mod(temp_bear,360.) + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' storm-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop2 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + sr_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + sr_vr(iquad,idist) = vr_sum / float(azimuth_ct) + sr_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + sr_wind(iquad,idist) = -999.0 + sr_vr(iquad,idist) = -999.0 + sr_vt(iquad,idist) = -999.0 + endif + + enddo quadloop2 + + enddo radiusloop2 +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,calcparm,wfract_cov,pdf_ct_bin,pdf_ct_tot,maxstorm + & ,trkrinfo,igfwret) +c +c ABSTRACT: This subroutine determines the fractional areal coverage +c of winds exceeding various thresholds within specified arcs +c (e.g., 200 km, 400 km, etc) in each quadrant of a storm. The bins +c that are used go as follows: (1) 0-100; (2) 0-200; (3) 0-300; +c (4) 0-400; (5) 0-500. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real wfract_cov(numquad+1,numbin,numthresh) + real area_total_quad_bin(numquad,numbin) + real area_exceed_quad_bin(numquad,numbin,numthresh) + real xintlon,xintlat + real :: windthresh(numthresh) = (/17.5,25.74,32.94/) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,conv_ms_knots,vmagkts + real rads,ri,dell,vmag,xarea,grdintincr,xsfclon,xsfclat + real sum_exceed_area(numbin,numthresh) + real sum_total_area(numbin,numthresh) + integer pdf_ct_bin(16) + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igfwret,ipct,i,j,numinterp,ixoa,ixaa,iq,ib,it,ii + integer jlatfix,ilonfix,npts,ibeg,iend,jbeg,jend,ngridint,ni,nj + integer itret,igiret,idistbin,ipdfbin,pdf_ct_tot,maxstorm + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) + character got_pdf*6 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*5 :: cbin(5) = + & (/'0-100','0-200','0-300','0-400','0-500'/) + character*2 :: cthresh(3) = (/'34','50','64'/) +c + igfwret = 0 + conv_ms_knots = 1.9427 + rads = 500.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + + wfract_cov = 0.0 + area_total_quad_bin = 0.0 + area_exceed_quad_bin = 0.0 + sum_exceed_area = 0.0 + sum_total_area = 0.0 + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_fract_wind_cov from call to ' + print *,'!!! get_ij_bounds, stopping processing for storm' + print *,'!!! number ',ist + endif + + igfwret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_fract_wind_cov calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igfwret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + +c When evaluating the winds at a gridpoint, keep in mind that each +c gridpoint represents area around it. There are 2 special cases +c we need to watch out for. The first is for cases in which the +c area of a gridpoint straddles across a distance threshold, so +c that some of the gridpoint's area is in the "<200" bin, while +c some is in the "<100" bin. The other is for the case in which +c the area of a gridpoint straddles between 2 adjacent quadrants +c (e.g., a gridpoint exactly to the north of the center would have +c half its area in the NW quadrant and half in the NE quadrant). +c +c To properly "partition" and assign gridpoint areas, we need to +c interpolate the current grid down to a fine resolution. +c +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the guidelines that +c will be used, keeping in mind that we want the final grid spacing +c to be on the order of between 0.05 and 0.10 degree (finer than +c 0.05 deg is superfluous, and coarser than 0.10 deg is too coarse). +c +c Original grid size (deg) # of interps +c ------------------------- ------------ +c 0.8 <= g 4 +c 0.4 <= g < 0.8 3 +c 0.2 <= g < 0.4 2 +c 0.1 <= g < 0.2 1 +c g < 0.1 0 + + + if ((dx+dy)/2. >= 0.8) then + numinterp = 4 + else if ((dx+dy)/2. < 0.8 .and. (dx+dy)/2. >= 0.4) then + numinterp = 3 + else if ((dx+dy)/2. < 0.4 .and. (dx+dy)/2. >= 0.2) then + numinterp = 2 + else if ((dx+dy)/2. < 0.2 .and. (dx+dy)/2. >= 0.1) then + numinterp = 1 + else + numinterp = 0 + endif + + grdintincr = (dx+dy)/2. + do i = 1,numinterp + grdintincr = 0.5 * grdintincr + enddo + +c Now loop through the points in this subdomain, determine if any +c are within 500 km of the center, and then determine what quadrant +c the point is in relative to the center, and then calculate the +c fractional area coverage for winds. + + pdf_ct_tot = 0 + pdf_ct_bin = 0 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_fract_wind_cov, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_fract_wind_cov' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle iloop ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > (rads+(0.75*((dx+dy)/2.)*dtk*cos(glat(j)*dtr)))) + & then + + ! If the distance is greater than "rads" (500 km at initial + ! writing) plus another 3/4 of a gridpoint, then cycle. + ! The extra 3/4 of a gridpoint is to allow for the case of + ! some portion of the area around a gridpoint (whose + ! center point > 500 km) being within the 500 km arc... + ! although that is only factored in for grids with spacing + ! >= 0.1 deg. For smaller grids, where no interpolation is + ! done in this subroutine, then the distance to that point + ! is considered representative and the point is ignored if + ! it is not less than 500 km from the center. + + cycle iloop + + else + + ! First interpolate the area surrounding each grid point to + ! get fine resolution of lats & lons for determining how to + ! partition the area of a gridpoint among quadrants as well + ! as among distance thresholds. + + vmag = sqrt (u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + vmagkts = vmag * conv_ms_knots + + if (numinterp > 0) then + + grdintincr = ((dx+dy)/2.) / 2**numinterp ! "grid spacing" + ! of interpolated grid + ngridint = (2**numinterp) / 2 + + got_pdf = 'notyet' + + njloop: do nj= ngridint,-ngridint,-1 + + xintlat = glat(j) + float(nj) * grdintincr + + niloop: do ni= -ngridint,ngridint + + xintlon = glon(ii) + float(ni) * grdintincr + + call calcdist (xintlon,xintlat,xsfclon + & ,xsfclat,xdist,degrees) + + if (xdist <= 350. .and. got_pdf == 'notyet') then + ! The got_pdf flag is needed because in these loops + ! for niloop & njloop, we are actually looking at + ! tiny areas around the same grid point. So we + ! want to make sure we only count each gridpoint + ! once. + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + got_pdf = 'got_it' + endif + + if (xdist < 500.) then + + ! Compute area of this fraction of a grid box + xarea = (grdintincr * 111195) * + & (grdintincr * 111195 + & * cos(xintlat * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Go through a loop of the bins. The purpose of + ! this is that these "bins" all go from the + ! the center out to a specified radius, they are + ! NOT 100-km wide bins. So if we are dealing with + ! a point at r = 250 km, then that falls in the + ! 0-300 km bin, but it also falls in the 0-400 and + ! 0-500 km bins as well. So we need to run through + ! this binloop multiple times to get the area data + ! into multiple bins. Here are the bins & indices: + ! 1: 0-100 km + ! 2: 0-200 km + ! 3: 0-300 km + ! 4: 0-400 km + ! 5: 0-500 km + + binloop: do ib = idistbin,numbin + + if (xintlon >= xsfclon .and. + & xintlat >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (xintlon >= xsfclon .and. + & xintlat < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop + + endif + + enddo niloop + + enddo njloop + + else + + ! In this else statement is the case for a grid whose + ! resolution is already fine enough that we don't need + ! to interpolate any further. For example, we will have + ! the H*Wind data on a 0.05 degree grid, so that's already + ! fine enough. + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat + & ,xdist,degrees) + + if (xdist <= 350.) then + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + endif + + if (xdist < 500.) then + + ! Compute area of this grid box + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Why the binloop2? See explanation above in the "if" + ! part of this if-then block, where binloop is. + + binloop2: do ib = idistbin,numbin + + if (glon(ii) >= xsfclon .and. + & glat(j) >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (glon(ii) >= xsfclon .and. + & glat(j) < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop2 + + endif + + endif + + endif + + enddo iloop + + enddo jloop + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different quadrants, bins and thresholds... +c ------------------------------------------------- + + if ( verb .ge. 3 ) then + write (6,109) ' ' + & ,' ' + & ,' ' + write (6,109) ' Quadrant Bin Wind_Thresh ' + & ,'Fract_coverage (%) Area_exceeded' + & ,' Area_total' + write (6,109) ' -------- --- ----------- ' + & ,'------------------ -------------' + & ,' ----------' + write (6,109) ' ' + & ,' ' + & ,' ' + + do iq = 1,numquad + do ib = 1,numbin + do it = 1,numthresh + wfract_cov(iq,ib,it) = area_exceed_quad_bin(iq,ib,it) / + & area_total_quad_bin(iq,ib) + write (6,117) cquad(iq),cbin(ib),cthresh(it) + & ,wfract_cov(iq,ib,it)*100.0 + & ,area_exceed_quad_bin(iq,ib,it) + & ,area_total_quad_bin(iq,ib) + enddo + enddo + enddo + endif + + + 109 format (1x,a33,a37,a16) + 117 format (5x,a2,5x,a5,7x,a2,13x,f6.2,10x,f16.1,2x,f16.1) + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different bins and thresholds, but for the +c entire "disc" of the storm, that is, summing all +c quadrants together. +c ------------------------------------------------- + + do it = 1,numthresh + do ib = 1,numbin + do iq = 1,numquad + sum_total_area(ib,it) = sum_total_area(ib,it) + & + area_total_quad_bin(iq,ib) + sum_exceed_area(ib,it) = sum_exceed_area(ib,it) + & + area_exceed_quad_bin(iq,ib,it) + enddo + wfract_cov(5,ib,it) = sum_exceed_area(ib,it) + & / sum_total_area(ib,it) + enddo + enddo + + if ( verb .ge. 3 ) then + do ib = 1,numbin + do it = 1,numthresh + write (6,117) 'TT',cbin(ib),cthresh(it) + & ,wfract_cov(5,ib,it)*100.0 + & ,sum_exceed_area(ib,it) + & ,sum_total_area(ib,it) + enddo + enddo + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_ike_stats (imax,jmax,inp,dx,dy,ist,ifh + & ,fixlon,fixlat,xsfclon,xsfclat,valid_pt,calcparm + & ,ike,sdp,wdp,maxstorm,trkrinfo,igisret) +c +c ABSTRACT: This subroutine computes the Integrated Kinetic Energy +c (IKE) and Storm Surge Damage Potential (SDP) values, based on +c Powell (BAMS, 2007). At this time, we are only computing the IKE +c values for TS threshold (17.5 m/s) and above. We are not yet +c computing wind damage potential (WDP) since, per Mark Powell +c (4/2008), he is currently re-formulating an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c sdp Storm surge damage potential + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4 + integer npts,ipct,igisret,imax,jmax,ist,ifh,ilonfix,jlatfix + integer ibeg,jbeg,iend,jend,igiret,i,j,maxstorm,ii + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real ike(max_ike_cats) + real dx,dy,degrees,rads,ri,dell,xdist,vmag,xarea + real xsfclon,xsfclat,sdp,wdp + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) +c + igisret = 0 + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + rads = 400.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_ike_stats from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for storm ' + print *,'!!! number ',ist + endif + + igisret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_ike_stats calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igisret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + +c Search a grid of points near the storm center, evaluate if the +c storm is within the "rads" distance threshold. If so, compute +c the IKE values for all applicable thresholds (10, 18, 33 m/s). + + do j = jbeg,jend + do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ike_stats, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_ike_stats' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > rads) then + cycle + else + + vmag = sqrt(u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + + if (vmag > 10.0) then + ! Add gridpoint to IKE_10. Compute area first... + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + ike(1) = ike(1) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 18.0) then + ! Add gridpoint to IKE_ts. Area already computed for 10 + ike(2) = ike(2) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 33.0) then + ! Add gridpoint to IKE_h. Area already computed for 10 + ike(3) = ike(3) + (0.5 * (vmag**2) * xarea) + endif + + endif + + enddo + enddo + + ike(1) = ike(1) * 1.e-12 ! Convert from J to TJ + ike(2) = ike(2) * 1.e-12 ! Convert from J to TJ + ike(3) = ike(3) * 1.e-12 ! Convert from J to TJ + +c Compute the storm surge damage potential (sdp) + + if (ike(2) >= 0.0) then + sdp = 0.676 + (0.43 * sqrt(ike(2))) + & - (0.0176 * ((sqrt(ike(2)) - 6.5)**2) ) + else + sdp = -99.0 + endif + +c Print out the IKE and SDP statistics... + + if ( verb .ge. 3 ) then + print *,' IKE_10 (storm energy) = ',ike(1) + print *,' IKE_TS (tropical storm) = ',ike(2) + print *,' IKE_H (hurricane) = ',ike(3) + print *,' SDP = ',sdp + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine distbear (xlatin,xlonin,dist,bear,xlatt,xlont) +c +c ABSTRACT: Given an origin at latitude, longitude=xlato,xlono, +c this subroutine will locate a target point at a distance dist in +c km or nautical miles (depends on what you use for "rad_earth..." +c below), at bearing bear (degrees clockwise from north). +c Returns latitude xlatt and longitude xlont of target point. +c +c *** NOTE *** +c This subroutine was written to handle input lats & lons as this: +c All latitudes are in degrees, north positive and south negative. +c All longitudes are in degrees, west positive and east negative. +c *** **** *** +c +c However, for the longitudes, the rest of the tracker uses all +c 0-360 longitudes. Therefore, we need to convert the input lons +c and then once again convert the lons that are returned back to +c the calling routine. +c +c NOTE-- When origin is at north or south pole, bearing is no +c longer measured from north. Instead, bearing is measured +c clockwise from the longitude opposite that specified in xlono. +c Example-- if xlato=90., xlono=80., the opposite longitude is +c -100 (100 East), and a target at bearing 30. will lie on the +c -70. (70 East) meridian. +c +c AUTHOR: The core of this subroutine was written by Albion +c Taylor, another NOAA employee, in 1981. +c + USE trig_vals + + implicit none +c + real, parameter :: rad_earth_nm = 3440.170 ! radius of earth + real, parameter :: rad_earth_km = 6372.797 ! radius of earth + real xlato,xlono,dist,bear,xlatt,xlont,xlatin,xlonin + real cdist,sdist,clato,slato,clono,slono,cbear,sbear + real z,y,x,r,xlattz,xlontz,ddist,dbear,dxlato,dxlono +c + xlato = xlatin + xlono = xlonin + +cstr print *,' ' +cstr print *,'+++ At top of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlon= ',xlono,'E ',360.-xlono +cstr & ,'W xlat=',xlato +cstr print '(a6,f7.2,a8,f7.2)','dist= ',dist,' bear= ',bear + + if (xlono > 180.) then + ! Longitude input for this subroutine must be positive west + xlono = 360. - xlono + else + ! Longitude input for this subroutine must be negative east + xlono = -1. * xlono + endif + +cstr print '(a31,a8,f8.2)','After conversion for distbear, ' +cstr & ,' xlono= ',xlono + + ddist = dist + dbear = bear + dxlato = xlato + dxlono = xlono + + cdist = cos(ddist/rad_earth_km) + sdist = sin(ddist/rad_earth_km) + clato = cos(dtr*dxlato) + slato = sin(dtr*dxlato) + +cstr print *,'cdist= ',cdist,' sdist= ',sdist,' clato= ',clato +cstr & ,' slato= ',slato + + clono = cos(dtr*dxlono) + slono = sin(dtr*dxlono) + +cstr print *,'dxlono= ',dxlono,' clono= ',clono +cstr & ,' slono= ',slono + + cbear = cos(dtr*dbear) + sbear = sin(dtr*dbear) + +cstr print *,'cbear= ',cbear,' sbear= ',sbear + + z=cdist*slato + clato*sdist*cbear + y=clato*clono*cdist + sdist*(slono*sbear - slato*clono*cbear) + x=clato*slono*cdist - sdist*(clono*sbear + slato*slono*cbear) + +cstr print *,'z= ',z,' y= ',y,' x= ',x + + r = sqrt(x**2 + y**2) + +cstr print *,'r = sqrt(x**2 + y**2) = ',r + + xlattz = atan2(z,r)/dtr + +cstr print *,'xlattz = datan2(z,r)/dtr = ',xlattz + + xlatt = xlattz + + if (r <= 0.) go to 20 + + xlontz = atan2(x,y)/dtr + +cstr print *,'xlontz = atan2(x,y)/dtr = ',xlontz + +c xlont = xlontz + + ! Return the target longitude back to the calling routine + ! as a 0-360 positive east longitude.... + + xlont = mod(360.-xlontz,360.) + +c xlont = mod(360.+xlontz,360.) + +cstr print *,' ' +cstr print *,'At end of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlont= ',xlont,'E ' +cstr ,360.-xlont,'W xlatt=',xlatt + + return + 20 xlont=0. +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_uneven (targlat,targlon,dx,dy + & ,imax,jmax,trkrinfo,level,cparm,xintrp_val,ibiret) +c +c ABSTRACT: This subroutine performs a bilinear interpolation to get +c a data value at a given lat/lon that may be anywhere within a box +c defined by the four surrouding grid points. In the diagram below, +c remember that for our grids we are using in the tracker, the +c latitude index starts at the north pole and increases southward. +c The point "X" indicates the target lat/lon location of the value +c for which we are bilinearly interpolating. The values to and ta +c below are ratios that determine how geographically close the +c target location is to the point of origin (pt.1 (i,j)) in terms +c of both longitude (to) and latitude (ta). +c +c +c pt.1 pt.2 +c (i,j) (i+1,j) +c +c +c +c X +c +c pt.4 pt.3 +c (i,j+1) (i+1,j+1) +c + + USE grid_bounds; USE tracked_parms; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + character cparm*1 + real targlat,targlon,xintrp_val,dx,dy + real to,ta,d1,d2,d3,d4,z,eastlon + integer ie,iw,jn,js,ibiret,imax,jmax,level,nlev + + ibiret = 0 + +c -------------------------------------------------------------- +c For the latitudes and longitudes surrounding our target +c lat/lon location, convert the lat/lon values into i- and +c j-indices. +c -------------------------------------------------------------- + +c Find the j-indices for the points just to the north and the +c south of targlat.... + + if (targlat >= 0.0) then + ! For a northern hemisphere storm, jn is the j-index for the + ! point just to the *NORTH* (poleward) of targlat. + jn = int((glatmax - targlat)/dy + 1.) + js = jn + 1 + else + ! For a southern hemisphere storm, js is the j-index for the + ! point just to the *SOUTH* (poleward) of targlat. + js = ceiling((glatmax - targlat)/dy + 1.) + jn = js - 1 + endif + + ! Check to make sure that points are not being requested beyond + ! the northern or southern boundaries of the grid. This is most + ! likely to happen for a smaller, regional grid. + + if (jn > jmax .or. js > jmax) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jmax exceeded in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + + if (jn < 1 .or. js < 1) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jn < 0 or js < 0 in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + +c Find the i-indices for the points just to the east and the +c west of targlon.... + + ie = int((targlon - glonmin)/dx + 2.) + iw = ie - 1 + + ! Check for GM wrapping. Check ie to see if it is between the + ! most eastward gridpoint and the GM (i.e., on a 1-deg global + ! grid (360x181), it would be if targlon was between 359.0 (i=360) + ! and the GM (i=1, not i=361)). Similarly then, if we adjust ie + ! to then be 1, then we have a problem with iw, + ! since iw = 1 - 1 = 0. + + if (ie > imax) then + if (trkrinfo%gridtype == 'global') then + ie = ie - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ie > imax in subroutine ' + print *,'!!! bilin_int_uneven for a non-global grid. ' + print *,'!!! Returning to calling routine after ' + print *,'!!! assigning missing wind value of -99.' + print *,'!!! ie= ',ie,' imax= ',imax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + + if (iw < 1) then + if (trkrinfo%gridtype == 'global') then + iw = iw + imax + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: iw < 1 in subroutine bilin_int_uneven' + print *,'!!! for a non-global grid. Returning to calling ' + print *,'!!! routine after assigning missing wind value ' + print *,'!!! of -99. iw= ',iw + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' +++ Interpolating winds for cparm= ',cparm +ctmwc print '(6x,4(a4,i3))','jn= ',jn,' js= ',js,' iw= ',iw,' ie= ',ie +ctmwc endif + +c ---------------------------------------------------------------- +c Calculate the longitude (to) and latitude (ta) location ratios. +c Check for GM wrapping, as we can run into a problem here if +c interpolating for points that are just west of the GM, since we +c would be interpolating using values of longitude just west of +c GM (say, glon(iw)=359.5) and the GM (glon(ie) = 0.0). This +c makes for an incorrect "to" ratio below, with 0-359.5 in the +c denominator. We have to account for this.... +c ---------------------------------------------------------------- + + if (glon(iw) > 300.0 .and. + & (glon(ie) < 10. .and. glon(ie) >= 0.)) then + eastlon = 360. - glon(ie) + else + eastlon = glon(ie) + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,'glat(js)= ',glat(js),' glat(jn)= ',glat(jn) +ctmwc endif + + to = (targlon - glon(iw)) / (eastlon - glon(iw)) + ta = (targlat - glat(jn)) / (glat(js) - glat(jn)) + +c -------------------------------------------------------------- +c Copy the data values at the 4 known points into simple scalar +c variables +c -------------------------------------------------------------- + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cparm == 'u') then + d1 = u(iw,jn,nlev) + d2 = u(ie,jn,nlev) + d3 = u(ie,js,nlev) + d4 = u(iw,js,nlev) + else if (cparm == 'v') then + d1 = v(iw,jn,nlev) + d2 = v(ie,jn,nlev) + d3 = v(ie,js,nlev) + d4 = v(iw,js,nlev) + else if (cparm == 'm') then + d1 = lsmask(iw,jn) + d2 = lsmask(ie,jn) + d3 = lsmask(ie,js) + d4 = lsmask(iw,js) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in bilin_int_uneven.' + print *,'!!! Input cparm not recognized.' + print *,'!!! cparm= ',cparm + print *,'!!! EXITING....' + endif + + stop 95 + endif + + z = 1.9427 + +cstr print '(2x,4(a4,f8.2))',' d1= ',d1*z,' d2= ',d2*z +cstr & ,' d3= ',d3*z,' d4= ',d4*z + +c ------------------------------------------------------------- +c Compute the interpolated value +c ------------------------------------------------------------- + + xintrp_val = (1.-to) * (1.-ta) * d1 + & + to * (1.-ta) * d2 + & + to * ta * d3 + & + (1.-to) * ta * d4 + +cstr print '(2x,2(a11,f8.2))',' xintrp= ',xintrp_val,' (in kts)= ' +cstr & ,xintrp_val*z +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine sort_storms_by_pressure (gridprs,ifh,maxstorm,sortindex + & ,issret) +c +c ABSTRACT: This subroutine sorts storms by mslp. It is called by +c subroutine tracker just before the loop for "stormloop" is done +c for all the storms at a particular forecast hour. It is only +c called for the "midlat" and "tcgen" cases. The end result of +c this sort is an array (prsindex) that contains the indeces of +c the storms, arranged from lowest pressure to highest (and note +c that the "undefined" storms have a pressure of 9999.99 mb and +c thus get sorted to the bottom of the array). The purpose of +c doing this is so that we track the most intense storms first. +c Why go to the trouble? Imagine a scenario in which we are +c tracking a complex system in which there are 2 low pressure +c centers. Let's say that one is becoming dominant and +c intensifying, while the other is weakening. Now, let's assume +c that the weakening one eventually gets absorbed into the +c stronger, more dominant low. Now we only have 1 low, but if in +c the tracker stormloop, we first process the data for the +c weakening low, we will attribute the track to that storm, and +c then when we get to the point in the loop where we are trying +c to get the track for the stronger storm, we will (erroneously) +c stop the tracking for that storm since the storm center has +c already been attributed to the weaker storm. But by using this +c subroutine, we will track the stronger storm first, and thus +c avoid this problem. +c +c NOTE: The pressures used in the sort are those obtained at the +c previous forecast hour. At forecast hour = 0, just use the +c values as they were input to this routine, since they were +c found in first_ges_center from strongest to weakest already. +c +c INPUT: +c gridprs real array of storm mslp values +c ifh integer index for the current forecast hour +c maxstorm max num of storms that can be handled in this run +c +c OUTPUT: +c sortindex contains a sorted array of indeces. The orders +c sort routine does NOT rearrange the data. Rather, it +c returns this array of sorted indeces which point to +c the correct order of data values in the data array. +c issret return code from this subroutine +c + USE set_max_parms + USE verbose_output + + real, allocatable :: iwork(:) + real gridprs(maxstorm,maxtime) + integer ifh,maxstorm + integer sortindex(maxstorm) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: prstemp(:) +c + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iva /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub sort_storms_by_pressure allocating' + print *,'!!! prstemp or iwork arrays: ' + print *,'!!! iva= ',iva,' iwa= ',iwa + endif + + STOP 94 + return + endif + + if (ifh > 1) then + +c print *,' ' +c print *,'--- Before sort, original prs values follow:' +c print *,' ' + + do ist = 1,maxstorm + prstemp(ist) = gridprs(ist,ifh-1) +c write (6,81) ist,prstemp(ist)/100.0 + enddo + + imode = 2 + sortindex = 0 + call qsort (prstemp,sortindex,maxstorm) + +ccccc call orders (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) +ccccc call orders_4byte (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Pressure-sorted storm list:' + print *,' ' + + do ist = 1,maxstorm + if (prstemp(sortindex(ist))/100.0 < 9999.0) then + write (6,82) ist,sortindex(ist) + & ,prstemp(sortindex(ist))/100.0 + endif + enddo + + 81 format (1x,'ist= ',i5,' Original (unsorted) prstemp= ',f7.2) + 82 format (1x,'ist= ',i5,' sortindex(ist)= ',i5 + & ,' prstemp= ',f7.2) + endif + + else + do ist = 1,maxstorm + sortindex(ist) = ist + enddo + endif + + deallocate (prstemp); deallocate (iwork) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getvrvt (centlon,centlat,xlon,xlat + & ,udat,vdat,vr,vt,igvtret) +c +c ABSTRACT: This subroutine takes as input a u-wind and v-wind value +c at an input (xlon,xlat) location and returns the tangential and +c radial wind components relative to the input center lat/lon +c position (centlon,centlat). The only trick to this whole +c subroutine is figuring out the angle from the center point to the +c data point, and we do this by creating a triangle with the leg +c from the center point to the data point being the hypotenuse. +c +c NOTE: All longitudes must be in positive degrees east (0-360) !!! +c +c INPUT: +c centlon Longitude of center point +c centlat Latitude of center point +c xlon Longitude of pt at which vr & vt will be computed +c xlat Latitude of pt at which vr & vt will be computed +c udat u-value of wind at the point (xlon,xlat) +c vdat v-value of wind at the point (xlon,xlat) +c +c OUTPUT: +c vr Radial wind component at (xlon,xlat) wrt (centlon,centlat) +c vt Tang wind component at (xlon,xlat) wrt (centlon,centlat) +c igvtret Return code from this subroutine +c + USE trig_vals + USE verbose_output + + implicit none + + real centlon,centlat,xlon,xlat,udat,vdat,vr,vt,degrees,tmpxlon + real angle,xlondiff,xlatdiff,opp_dist,hyp_dist,sin_value + real cos_value,adj_dist,tmpangle,sin_angle,cos_angle + real uvrcomp,vvrcomp,uvtcomp,vvtcomp + integer igvtret +c + call calcdist(centlon,centlat,xlon,xlat,hyp_dist,degrees) + +c xxxx + + tmpxlon = xlon + + if (centlon > 330.0) then + + if (xlon > 360.0) then + + tmpxlon = xlon ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (xlon < 30.0) then + + tmpxlon = xlon + 360. ! In this case, the fix center is just + ! to the west of the GM with a lon (centlon) + ! > 330, while the point being evaluated + ! (xlon) is just east of the GM, but with a + ! lon (centlon) < 30. Need to adjust here to + ! to get the xlon in the 330+ frame of + ! reference. + + endif + + elseif (centlon >= 0 .and. centlon < 30.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = 360. - xlon + + endif + + elseif (centlon < 0.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = -1 * (360. - xlon) + + endif + + endif + + xlatdiff = abs(centlat - xlat) + xlondiff = abs(centlon - tmpxlon) + + if (centlon > 355.0) then + write (6,91) centlon,tmpxlon,hyp_dist,degrees,xlondiff + 91 format (1x,'centlon= ',f8.3,' tmpxlon= ',f8.3,' hyp_dist= ' + & ,f10.2,' degrees= ',f10.2,' xlondiff= ',f12.2) + endif + + if (xlondiff == 0 .and. xlatdiff > 0) then + + if (centlat > xlat) angle = 180 ! pt directly south of ctr + if (centlat < xlat) angle = 0 ! pt directly north of ctr + + else if (xlondiff > 0 .and. xlatdiff == 0) then + + if (centlon > tmpxlon) angle = 270 ! pt directly west of ctr + if (centlon < tmpxlon) angle = 90 ! pt directly east of ctr + + else + + ! This next part figures out the angle from the center point + ! (centlon,centlat) to the data point (tmpxlon,xlat). It does + ! this by setting up a triangle and then using inverse trig + ! functions to get the angle. Since this is a kludgy way to + ! do it that doesn't account for the curvature of the earth, + ! we'll do it 2 ways, using asin and then acos, then take the + ! average of those 2 for the angle. hyp_dist, calculated just + ! above, is the distance from the center pt to the data pt. + + opp_dist = xlatdiff/360. * ecircum + sin_value = opp_dist / hyp_dist + if (sin_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, sin_value > 1, setting to 1.' + print *,'!!! opp_dist= ',opp_dist,' hyp_dist= ',hyp_dist + print *,'!!! sin_value = ',sin_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + sin_value = 0.99999 + endif + sin_angle = asin(sin_value) / dtr + + call calcdist(centlon,centlat,tmpxlon,centlat,adj_dist,degrees) + cos_value = adj_dist / hyp_dist + if (cos_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, cos_value > 1, setting to 1.' + print *,'!!! adj_dist= ',adj_dist,' hyp_dist= ',hyp_dist + print *,'!!! cos_value = ',cos_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + cos_value = 0.99999 + endif + cos_angle = acos(cos_value) / dtr + + tmpangle = 0.5 * (sin_angle + cos_angle) + + ! The previous lines of code just calculated an angle between + ! 0 and 90. This next if structure adjusts that angle to + ! instead be between 0 and 360. + + if (centlat <= xlat .and. centlon <= tmpxlon) then + angle = 90 - tmpangle + else if (centlat > xlat .and. centlon <= tmpxlon) then + angle = 90 + tmpangle + else if (centlat >= xlat .and. centlon >= tmpxlon) then + angle = 270 - tmpangle + else if (centlat < xlat .and. centlon >= tmpxlon) then + angle = 270 + tmpangle + endif + + endif + + uvrcomp = udat * sin(angle * dtr) + vvrcomp = vdat * cos(angle * dtr) + vr = uvrcomp + vvrcomp + + uvtcomp = (-udat) * cos(angle * dtr) + vvtcomp = vdat * sin(angle * dtr) + vt = uvtcomp + vvtcomp + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcfunix (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in the new ATCF UNIX format. +c Unlike the old atcf DOS format in which you waited until the +c whole tracking was over to write the output for all forecast +c hours, with this atcfunix format, each time we are calling this +c subroutine, it is to only write out 1 record, which will be the +c fix info for a particular storm at a given time. Also, even +c though we have some data (GFS, NAM) at 6-hour intervals, Jim +c Gross informed me that TPC does not need the positions at such +c frequency, and keeping the reporting at 12 hour intervals is fine. +c +c While this new atcfunix format contains much more information than +c the old 1-line atcf dos message, for our purposes we will use the +c slots for mslp and wind radii. An example set of output records +c will look like the following: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c plastbar pressure of the outermost closed isobar +c rlastbar radius (nm) of the outermost closed isobar +c rmax radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c cps_vals real array with the values for the 3 cyclone phase +c space parameters: (1) is for Parameter B (thermal +c asymmetry); (2) is for lower level (600-900 mb) thermal +c wind; (3) is for upper level (300-600 mb) thermal wind. +c wcore_flag character for value of 300-500 mb warm core: y, n, or +c 'u' for undetermined. +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE phase + USE verbose_output + + type (datecard) inp + type (trackstuff) trkrinfo + + real cps_vals(3) + real outlon,outlat,rmax,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,irmax,output_fhr,ic,iplastbar,irlastbar + integer vradius(3,4),icps_vals(3) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + character comma_fill1*48,comma_fill2*31,comma_filler*79 + + if ( verb .ge. 3 ) then + print *,'TTT top of atcfunix, ist= ',ist,' ifh= ',ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcfunix. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'in output_atcfunix, tcv_storm_id= ' + & ,storm(ist)%tcv_storm_id + print *,'in output_atcfunix, tcv_storm_id(3:3)= ' + & ,storm(ist)%tcv_storm_id(3:3) + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' +!zhang case ('A','a'); basinid = 'NA' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + if (trkrinfo%want_oci) then + if (plastbar > 0.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -99 + endif + if (rlastbar > 0.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -99 + endif + else + iplastbar = -99 + irlastbar = -99 + endif + + if ( verb .ge. 3 ) then + print *, 'output: rlastbar=',rlastbar,' irlastbar=',irlastbar + print *, 'output: plastbar=',plastbar,' iplastbar=',iplastbar + endif + +c Now convert all of the cyclone phase space parameter values from +c real to integer. + + do ic = 1,3 + if (cps_vals(ic) > -9999.0) then + if (cps_vals(ic) >= 0.0) then + icps_vals(ic) = int(cps_vals(ic)*10. + 0.5) + else + icps_vals(ic) = int(cps_vals(ic)*10. - 0.5) + endif + else + icps_vals(ic) = -9999 + endif + enddo + + if (wcore_flag == 'y'.or. wcore_flag == 'Y') then + wcore_flag = 'Y' + elseif (wcore_flag == 'n' .or. wcore_flag == 'N') then + wcore_flag = 'N' + elseif (wcore_flag == 'u' .or. wcore_flag == 'U') then + wcore_flag = 'U' + else + wcore_flag = 'U' + endif + + comma_fill1 = ', 0, 0, , 0, , 0, 0, ,' + comma_fill2 = ' , , , 0, 0, 0, 0' + comma_filler = comma_fill1//comma_fill2 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + else + + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,'rmax= ',rmax,' irmax= ',irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,a79,', THERMO PARAMS' + & ,3(', ',i7),', ',a1,', ',i2,', DT, -999') + 91 format (a2,', ',a4,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,2(', ',i3),', ',a3) + +c bug fix for IBM: flush the output stream so it actually writes + flush(64) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each +c storm. This message contains the model identifier, the forecast +c initial date, and the positions for 0, 12, 24, 36, 48, 60 and 72 +c hours. In the case of the regional models (NGM, Eta), which +c only go out to 48h, zeroes are included for forecast hours +c 60 and 72. +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by Steve Lord for plotting purposes, and his +c plotting routines need the longitudes in 0 - 360, increasing +c westward. Thus, a necessary adjustment is made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + print *,'top of output_all' + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + intlon(ifh) = 0 + intlat(ifh) = 0 + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + endif + + enddo ifhloop + + print *,'before select case, atcfname= ' + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(5),intlon(5),intlat(9),intlon(9),intlat(13) + & ,intlon(13),intlat(17),intlon(17),intlat(21),intlon(21) + & ,0,0,storm(ist)%tcv_storm_id + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(3),intlon(3),intlat(5),intlon(5),intlat(7) + & ,intlon(7),intlat(9),intlon(9),intlat(11),intlon(11) + & ,intlat(13),intlon(13),storm(ist)%tcv_storm_id + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3),intlat(4) + & ,intlon(4),intlat(5),intlon(5),intlat(6),intlon(6) + & ,intlat(7),intlon(7),storm(ist)%tcv_storm_id + + case ('GDA','HDA') ! GDAS, HDAS + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),0,0,0,0,0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case default +c print *,'!!! ERROR in subroutine output_all. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + print *,'!!! Model name is not identified: ',atcfname + + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + 81 format (i2,a4,4i2.2,14i4,1x,a3) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm + & ,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each storm +c in ATCF format. This message contains the model identifier, the +c forecast initial date, and the positions for 12, 24, 36, 48 +c and 72 hours. This message also contains the intensity +c estimates (in knots) for those same hours. The conversion for +c m/s to knots is to multiply m/s by 1.9427 (3.281 ft/m, +c 1 naut mile/6080 ft, 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by the atcf system at TPC for plotting +c purposes, and the atcf plotting routines need the longitudes in +c 0 - 360, increasing westward. Thus, a necessary adjustment is +c made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real xmaxwind(maxstorm,maxtime) + real conv_ms_knots + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4,basinid*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + conv_ms_knots = 1.9427 + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + + intlon(ifh) = 0 + intlat(ifh) = 0 + + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + + endif + + enddo ifhloop + + basinid = ' ' + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid(1:2) = 'AL' + case ('E','e'); basinid(1:2) = 'EP' + case ('C','c'); basinid(1:2) = 'CP' + case ('W','w'); basinid(1:2) = 'WP' + case ('O','o'); basinid(1:2) = 'SC' + case ('T','t'); basinid(1:2) = 'EC' + case ('U','u'); basinid(1:2) = 'AU' + case ('P','p'); basinid(1:2) = 'SP' + case ('S','s'); basinid(1:2) = 'SI' + case ('B','b'); basinid(1:2) = 'BB' +cPENG case ('A','a'); basinid(1:2) = 'NA' + case ('A','a'); basinid(1:2) = 'AA' + case default; basinid(1:2) = '**' + end select + basinid(3:4) = storm(ist)%tcv_storm_id(1:2) + + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(5),intlon(5) + & ,intlat(9),intlon(9),intlat(13),intlon(13),intlat(17) + & ,intlon(17),0,0 + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,17)*conv_ms_knots) + 0.5) + & ,0 + & ,basinid,inp%byy + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble, ECMWF hi-res + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4),intlat(5) + & ,intlon(5),intlat(7),intlon(7) + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('GDA','HDA') ! GDAS, HDAS + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4) + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,0,0,0,0 + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case default +c print *,'!!! ERROR in subroutine output_atcf. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + end select + + enddo stormloop + + 82 format (i2,a4,4i2.2,10i4,5i3,1x,a4,i2.2) +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_hfip (outlon,outlat,inp,ist + & ,ifh,vmaxwind,xminmslp,vradius,rmax,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified ATCF UNIX format. +c The modification is to allow for sub-hourly output. That is, +c instead of just integer output hours, we can have output at +c 10, 15 or 20 past an hour. This necessitates a change in the +c "forecast hour" placeholder in the ATCF format. Instead of it +c being an I3, we'll make it an I5, with something like a lead time +c of 36.25h being rounded and truncated to 03625 for output. +c +c An example set of output records using the standard atcf format +c looks like the following: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c An example set of modified output records will look like the +c following, for the case of a lead time of 36:15 (36.25): +c +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifh index for the lead time array +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c rmax Radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms + USE verbose_output + + type (datecard) inp + + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,rmax + integer intlon,intlat,output_fhr,irmax,ileadtime + integer vradius(3,4) + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_hfip. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + ! ST: ifcsthour does not exist, so output_fhr is always + ! filled with invalid data here. However, output_fhr is + ! never used, so it is safe to remove. + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + ! output_fhr = ifcsthour + 3 + ileadtime = nint((fhreal(ifh) + 3.0) * 100.0) + else + ! output_fhr = ifcsthour + ileadtime = nint(fhreal(ifh) * 100.0) + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4),irmax + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4),irmax + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4),irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i5.5,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', 0, 0, ',i3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(69) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_fract_wind (outlon,outlat,xsfclon,xsfclat + & ,inp,ist,ifcsthour,vmaxwind,xminmslp,wfract_cov + & ,wfract_type,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values for the fractional areal coverage of various wind +c thresholds. In addition, this subroutine also writes out +c records to a file containing data on the PDF of wind magnitudes +c within r=350 km. +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with areal coverage thresholds. +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, NEE, 981, 857, 629, 810 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, NEE, 874, 732, 319, 610 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, NEE, 454, 327, 99, 270 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, AAE, 721, 721, 721, 721 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, AAE, 465, 465, 465, 465 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, AAE, 298, 298, 298, 298 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the pctgs for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind pctg info; all the +c other info is identical for each entry. +c +c Listed after the "XX" in each record is the radius from which +c the coverage is valid (000 km in this case); Next is the radius +c at which the coverage stops (100 km in this case). Next is the +c wind threshold (34, 50, 64). Next is an identifier for which +c quadrant the coverage starts in (first 2 characters are NE, SE, +c SW, NW); the last character indicates if the coverages are +c computed in the quadrants as earth-relative ("E") or +c storm-motion relative ("R"). The ones listed there as "AAE" +c are for the full disc (i.e., 4-quadrant average), earth-relative. +c Next are the wind coverage percentages, listed as percentage * 10 +c (e.g., 981 = 98.1%). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c wfract_cov percent areal coverage for various wind thresholds +c wfract_type 'earth' or 'storm' relative analysis +c pdf_ct_bin array for pdf of wind magnitudes within r=350 km +c pdf_ct_tot total count of pdf points for r < 350 km +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,pdfval + real wfract_cov(numquad+1,numbin,numthresh) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer :: windthresh(numthresh) = (/34,50,64/) + integer pdf_ct_bin(16) + integer intlon,intlat,output_fhr,intlon100,intlat100,pdf_ct_tot + integer maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (wfract_type == 'earth') then + wt = 'E' + else if (wfract_type == 'storm') then + wt = 'R' + else + wt = 'X' + endif + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'NE',wt + & ,int((1000.*wfract_cov(1,ib,it))+0.5) + & ,int((1000.*wfract_cov(2,ib,it))+0.5) + & ,int((1000.*wfract_cov(3,ib,it))+0.5) + & ,int((1000.*wfract_cov(4,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'AA',wt + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a6,i3.3,', ',i3.3,', ' + & ,i3,', ',a2,a1,4(', ',i4),', ',i4,a1,', ',i5,a1) + +c -------------------------------------------------- +c Now compute and write out the pdf values for the +c wind magnitude.... +c -------------------------------------------------- + + do ip = 1,16 + pdfval = float(pdf_ct_bin(ip)) / float(pdf_ct_tot) + write (76,85) atcfymdh,basinid,storm(ist)%tcv_storm_id(1:2) + & ,output_fhr,10*(ip-1),10*ip,pdf_ct_bin(ip) + & ,pdf_ct_tot,pdfval + enddo + + 85 format (1x,i10.10,3x,a2,a2,3x,i3,3x,i3.3,'_',i3.3,3x,i7,2x,i7 + & ,2x,f6.3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(73) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_wind_structure (outlon,outlat,xsfclon + & ,xsfclat,inp,ist,ifcsthour,vmaxwind,xminmslp,er_wind + & ,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values of the winds at specified distances along 45-degree +c radials in each storm quadrant. These are output +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 radials: NE, SE, +c SW and NW (45,135,225,315). For the storm-relative estimates, +c these radials will be computed at the same relative angles (i.e., +c 45,135,225,315), but with respect (positive clockwise) to the +c direction of storm motion. For example, for a storm moving with +c a heading of 280, the wind structure is evaluated at these +c radials: 325 (front-right; 45 deg CW from heading), 55 (back- +c right; 135 deg CW from heading), 145 (back-left; 225 deg CW from +c heading), 235 (front-left; 315 deg CW from heading). +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with wind values at the 13 specified distances +c (10, 25, 50, 75, 100, 150, 200, 250, 300, 350, 400, 450, 500 km) +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NEE, 1137, 1221, 854, 655, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SEE, 947, 982, 474, 396, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SWE, 645, 683, 328, 277, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NWE, 725, 753, 619, 429, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FRR, 1134, 1224, 852, 654, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BRR, 944, 984, 472, 393, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BLR, 649, 686, 321, 272, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FLR, 729, 756, 613, 421, etc., ... out to 500 km +c +c NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text. +c NOTE: These winds are in m/s coming into this routine and will +c be converted to knots*10 for output (e.g., 1221 = 122.1 kts) +c +c The "71" ID indicates earth-relative winds, the "72" ID indicates +c storm-relative winds. Here are the other IDs that will be used: +c 81: Tangential winds, earth-relative (m/s) +c 82: Tangential winds, storm-relative (m/s) +c 91: Radial winds, earth-relative (m/s) +c 92: Radial winds, storm-relative (m/s) +c +c Note that in this example, for this 36h forecast hour, there are +c 8 entries. This is so that we can include the wind values for +c the 4 different quadrants, for both the earth relative analyses +c (NEE, SEE, SWE, NWE) and the storm-relative analyses (FRR, BRR, +c BLR, FLR). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + integer ioutwind(numdist) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real outlon,outlat + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,id,intlon100,intlat100,ir + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*2 :: crel(4) = (/'FR','BR','BL','FL'/) + + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + +c Total wind (converted to knots*10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 71, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Total wind (converted to knots*10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 72, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 81, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 82, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 92, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a10,a2,a1,14(', ',i4) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(72) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_ike (outlon,outlat,xsfclon,xsfclat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,ike,sdp,wdp,maxstorm + & ,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the Integrated Kinetic Energy (IKE) and Storm Surge Damage +c Potential (SDP), based on Powell (BAMS, 2007). At this time, we +c are only computing the IKE values for TS threshold (17.5 m/s) and +c above. We are not yet computing wind damage potential (WDP) +c since, per Mark Powell (4/2008), he is currently re-formulating +c an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with WDP, SDP and IKE values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, 340, 560, 212, 174, 42, 93, 12, 0 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, WDP, SDP, I10, ITS, IH ,I2540,I4154, I55 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Values for WDP and SDP are multiplied by 10 in this routine +c before being written out. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c ike integrated kinetic energy, in units of TJ +c sdp storm surge damage potential +c wdp wind damage potential +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,sdp,wdp + real ike(max_ike_cats) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,intlon100,intlat100,maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (74,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, IKE',int((wdp*10)+0.5),int((sdp*10)+0.5) + & ,int(ike(1)+0.5),int(ike(2)+0.5),int(ike(3)+0.5) + & ,int(ike(4)+0.5),int(ike(5)+0.5),int(ike(6)+0.5) + & ,intlat100,clatns,intlon100,clonew +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a14,8(',',i5) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(74) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_phase (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,paramb,vtl_slope + & ,vtu_slope,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the three parameters that comprise Bob Hart's cyclone phase +c space (CPS). These parameters are his "parameter B", which +c assesses the left-right thermal asymmetry, and the upper +c troposphere (300-600 mb) and lower troposphere (900-600 mb) +c thermal wind values. +c +c LOCAL: +c +c Arrays: +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with paramb, vtl_slope and vtu_slope values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, 340, 560, 212 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, B, VTL, VTU +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c paramb thermal asymmetry +c vtl_slope thermal wind value for lower troposphere (900-600 mb) +c vtu_slope thermal wind value for upper troposphere (600-300 mb) +c +c OUTPUT: +c ioiret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + real outlon,outlat,paramb,vtl_slope,vtu_slope + real vmaxwind,conv_ms_knots,xminmslp + integer intlon,intlat,output_fhr + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (71,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 95, CPS',int(paramb+0.5),int(vtl_slope+0.5) + & ,int(vtu_slope+0.5) +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a14,3(',',i6)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(71) + + return + end +c +c----------------------------------------------------------------------- + subroutine output_atcf_gen1 (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different. Here's an example of the +c TPC standard atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c be the one that is pulled from the tcvitals or gen_vitals +c record: +c +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 50, NEQ, 155, 000, 000, 000 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 64, NEQ, 000, 000, 000, 000 +c +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd storm translation speed +c istmdir direction of storm movement +c plastbar pressure of last closed isobar +c rlastbar radius of last closed isobar +c rmax radius of max winds +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals; USE level_parms + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real outlon,outlat,plastbar,rlastbar,rmax + real vmaxwind,conv_ms_knots,xminmslp,mslp_outp_adj + real cps_vals(3) + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar,irmax + integer ivtl,ivtu,iparamb,output_fhr + integer vradius(3,4) + integer imeanzeta(nlevgrzeta),igridzeta(nlevgrzeta) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + + if ( verb .ge. 3) then + print *,'+++ Top of output_atcf_gen, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (rmax > -90.0) then + irmax = int(rmax + 0.5) + else + irmax = -99 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(66) + + return + end +c +c----------------------------------------------------------------------- +cPENG---2018-06-07------------------------------------------------- +c----------------------------------------------------------------------- +c subroutine output_atcf_gen (outlon,outlat,inp,ist +c & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm +c & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax +c & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + subroutine output_atcf_gen (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different. Here's an example of the +c TPC standard atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c be the one that is pulled from the tcvitals or gen_vitals +c record: +c +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 50, NEQ, 155, 000, 000, 000 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 64, NEQ, 000, 000, 000, 000 +c +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd storm translation speed +c istmdir direction of storm movement +c plastbar pressure of last closed isobar +c rlastbar radius of last closed isobar +c rmax radius of max winds +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real outlon,outlat,plastbar,rlastbar,rmax + real vmaxwind,conv_ms_knots,xminmslp + real cps_vals(3) + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar,irmax + integer ivtl,ivtu,iparamb,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + +cJ.Peng----2014-10-01----------------------------- + real wcore_mean_val,wcore_point_max + real imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + + if ( verb .ge. 3) then + print *,'+++ Top of output_atcf_gen, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + + if (xminmslp == 999999.0) xminmslp = 0.0 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/100. + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (rmax > -90.0) then + irmax = int(rmax + 0.5) + else + irmax = -99 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- +c & ,imeanzeta(1),abs(igridzeta(1)),imeanzeta(2),abs(igridzeta(2)) + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- +c & ,imeanzeta(1),abs(igridzeta(1)),imeanzeta(2),abs(igridzeta(2)) + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + endif + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4)) +cJ.Peng----2010-10-01----------------------------------------------- + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4) + & ,8(', ',f8.1)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(66) + + return + end +c +c----------------------------------------------------------------------- + subroutine output_atcf_sink (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta,igridzeta + & ,cps_vals,plastbar,rlastbar,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The "sink" in the subroutine name indicates that this output +c contains the whole kitchen sink of forecast storm info. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different, and the part after the radii +c data is different. Here's an example of the TPC standard +c atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c indicate the lat/lon at which the storm was *first* found in +c the model. The position may be either found within this run +c of the tracker, or that position may have been pulled from the +c tcvitals or gen_vitals record: +c +c 2000092500_F000_206N_0623W_13L, 2000092500, 03, GFSO, 036 +c , 243N, 675W, 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c , PLAS, RLAS, RMX, DIR, SPD, B, VTU, VTL +c , Z8MN, Z8MX, Z7MN, Z7MX +c +c As noted above, there is extra info at the end, after the +c "34, NEQ, 242, 163, 124, 208" radii info. Here is a key +c to indicate what these items are: +c +c PLAS: Pressure (mb) of last closed isobar +c RLAS: Radius of the last closed isobar in nm, 0 - 9999 nm. +c RMX: Radius of max winds, 0 - 999 nm. +c DIR: Direction of storm motion. +c SPD: Speed of storm motion (m/s * 10). +c B: Hart's CPS "Parameter B" thickness asymmetry value (m). +c VTL: Hart's CPS thermal wind (Lower, 900-600) value. +c VTU: Hart's CPS thermal wind (Upper, 600-300) value. +c Z8MN: Mean value of 850 mb zeta surrounding storm. +c Z8MX: Max value of 850 mb zeta near storm. +c Z7MN: Mean value of 700 mb zeta surrounding storm. +c Z7MX: Max value of 700 mb zeta near storm. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd speed of storm translation +c istmdir direction of storm motion +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c plastbar pressure of last closed isobar (pa) +c rlastbar radius of last closed isobar (nm) +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real cps_vals(3) + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar + integer iparamb,ivtl,ivtu,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1 + + if ( verb .ge. 3 ) then + print *,'+++ Top of output_atcf_sink, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4)) + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',2(i4,', '),4(i3,', '),2(i5,', '),4(i4,', '),a9) + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',2(i4,', '),i3,', ',2(i4,', '),3(i6,', '),4(i6,', ') + & ,a9) + +c write (68,87) gstm%gv_gen_date,gstm%gv_gen_lat +c & ,gstm%gv_gen_latns,gstm%gv_gen_lon +c & ,gstm%gv_gen_lonew,gstm%gv_gen_type +c & ,inp%bcc,inp%byy,inp%bmm,inp%bdd,inp%bhh +c & ,adjustr(atcfname),ifcsthour,intlat,clatns,intlon,clonew +c & ,int((vmaxwind*conv_ms_knots) + 0.5) +c & ,int(xminmslp/100.0 + 0.5) +c & ,'XX, 34, NEQ' +c & ,istmspd,istmdir,imeanzeta(1),igridzeta(1) +c & ,imeanzeta(2),igridzeta(2) +c +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,6(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(68) + + return + end + +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_tcvitals (xlon,xlat,inp,ist,iovret) +c +c ABSTRACT: This subroutine outputs a tcvitals record. The +c lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE inparms; USE set_max_parms + USE verbose_output + + type (tcvcard) stm + type (datecard) inp + real xlon,xlat +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "storm" +c components for this storm, then we will change the specific +c components that we need to. + + stm = storm(ist) + + stm%tcv_center = 'AEAR' + + stm%tcv_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + stm%tcv_latns = 'S' + else + stm%tcv_latns = 'N' + endif + + if (xlon >= 180.) then + stm%tcv_lon = 3600 - int(xlon * 10. + 0.5) + stm%tcv_lonew = 'W' + else + stm%tcv_lon = int(xlon * 10. + 0.5) + stm%tcv_lonew = 'E' + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) stm + endif + + write (65,21) stm + + 21 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + +c +c bug fix for IBM: flush the output stream so it actually writes + flush(65) + + return + end + +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_gen_vitals (xlon,xlat,inp,ist,istmspd,istmdir + & ,iovret) +c +c ABSTRACT: This subroutine outputs a modified vitals record. +c The lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c The storm identifier is different than that for a standard +c tcvitals. The storm identifier contains the date/time that +c the storm was first identified, and the lat/lon position at +c which it was first identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE gen_vitals; USE inparms; USE set_max_parms + USE verbose_output + + implicit none + + type (gencard) gstm + type (datecard) inp + real xlon,xlat + integer ist,iovret,istmspd,istmdir +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the vitals record. + + if (gstm%gv_gen_date /= 99999) then + + if (gstm%gv_gen_type /= 'FOF') then + ! If this is not a 'FOF' storm (found on the fly storm), then + ! it must be a TC vitals storm, or a tropical cyclone, and we + ! don't want to create a vitals record for a tropical cyclone, + ! since we will rely on reading them from the TC Vitals + ! database instead. + return + endif + + else + + ! This storm is new in this forecast/analysis and was found on + ! the fly in the first time level for this run and there was no + ! previous vitals record for this system + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = 0 + + gstm%gv_gen_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_gen_latns = 'S' + else + gstm%gv_gen_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_gen_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'W' + else + gstm%gv_gen_lon = int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'E' + endif + + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + gstm%gv_obs_ymd = inp%bcc * 1000000 + & + inp%byy * 10000 + & + inp%bmm * 100 + & + inp%bdd + + gstm%gv_obs_hhmm = inp%bhh * 100 + + gstm%gv_obs_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_obs_latns = 'S' + else + gstm%gv_obs_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_obs_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'W' + else + gstm%gv_obs_lon = int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'E' + endif + + gstm%gv_stdir = istmdir + gstm%gv_stspd = istmspd + + gstm%gv_depth = 'U' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) gstm + endif + + write (67,21) gstm + + 21 format (i10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1,'_',a3,1x,i8,1x + & ,i4.4,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x + & ,i3,4(1x,i4),1x,a1) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(67) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_tracker_mask (masked_outc,lb,ifh,ifcsthour + & ,imax,jmax,iotmret) +c +c ABSTRACT: This subroutine outputs a GRIB record that contains the +c "mask" used to mask out areas surrounding low pressure centers +c that have been found during the search at each forecast hour. This +c mask is written out purely for diagnostic purposes. The GRIB +c identifier given to the mask in the pds is 850 mb height (you can +c make it anything you want). This is only done for the "midlat" +c and "tcgen" cases, since the runs for those cases use a mask while +c the regular "tracker" run (that is, the run which strictly tracks +c only those storms in the TC vitals file) does not. +c +c INPUT: +c masked_outc logical array containing mask +c ifh integer counter for current forecast hour +c ifcsthour integer current forecast hour +c imax num points is i-direction of input grid +c jmax num points is j-direction of input grid +c +c OUTPUT: +c iotmret return code from this subroutine + + implicit none +c + integer ifh,imax,jmax,iotmret,kf,igoret,iix,jjx,ipret + integer ifcsthour + integer kpds(200),kgds(200) + logical(1) masked_outc(imax,jmax),lb(imax,jmax) + real xmask(imax,jmax) +c + if (ifh == 1) then + call baopenw (77,"fort.77",igoret) + print *,'baopenw: igoret= ',igoret + + if (igoret /= 0) then + print *,' ' + print *,'!!! ERROR in sub output_tracker_mask opening' + print *,'!!! **OUTPUT** grib files. baopenw return codes:' + print *,'!!! grib file 1 return code = igoret = ',igoret + STOP 95 + return + endif + endif + + xmask = 0.0 + do jjx = 1,jmax + do iix = 1,imax + if (masked_outc(iix,jjx)) then + xmask(iix,jjx) = 1.0 + else + xmask(iix,jjx) = 0.0 + endif + enddo + enddo + + kf = imax * jmax + +c kpds(5) = 7 +c kpds(6) = 100 +c kpds(7) = 850 +c kpds(22) = 0 + + kpds(1) = 7 ; kpds(2) = 80 + kpds(3) = 255 ; kpds(4) = 192 + kpds(5) = 7 ; kpds(6) = 100 + kpds(7) = 850 ; kpds(8) = 99 + kpds(9) = 7 ; kpds(10) = 20 + kpds(11) = 12 ; kpds(12) = 0 + kpds(13) = 1 ; kpds(14) = ifcsthour + kpds(15) = 0 ; kpds(16) = 10 + kpds(17) = 0 ; kpds(18) = 1 + kpds(19) = 2 ; kpds(20) = 0 + kpds(21) = 20 ; kpds(22) = 0 + kpds(23) = 0 ; kpds(24) = 0 + kpds(25) = 0 + kgds(1) = 0 ; kgds(2) = imax + kgds(3) = jmax ; kgds(4) = -90000 + kgds(5) = 0 ; kgds(6) = 128 + kgds(7) = 90000 ; kgds(8) = 359750 + kgds(9) = 250 ; kgds(10) = 250 + kgds(11) = 64 ; kgds(12) = 0 + kgds(13) = 0 ; kgds(14) = 0 + kgds(15) = 0 ; kgds(16) = 0 + kgds(17) = 0 ; kgds(18) = 0 + kgds(19) = 0 ; kgds(20) = 255 + + write(*,980) kpds(1),kpds(2) + write(*,981) kpds(3),kpds(4) + write(*,982) kpds(5),kpds(6) + write(*,983) kpds(7),kpds(8) + write(*,984) kpds(9),kpds(10) + write(*,985) kpds(11),kpds(12) + write(*,986) kpds(13),kpds(14) + write(*,987) kpds(15),kpds(16) + write(*,988) kpds(17),kpds(18) + write(*,989) kpds(19),kpds(20) + write(*,990) kpds(21),kpds(22) + write(*,991) kpds(23),kpds(24) + write(*,992) kpds(25) + write(*,880) kgds(1),kgds(2) + write(*,881) kgds(3),kgds(4) + write(*,882) kgds(5),kgds(6) + write(*,883) kgds(7),kgds(8) + write(*,884) kgds(9),kgds(10) + write(*,885) kgds(11),kgds(12) + write(*,886) kgds(13),kgds(14) + write(*,887) kgds(15),kgds(16) + write(*,888) kgds(17),kgds(18) + write(*,889) kgds(19),kgds(20) + write(*,890) kgds(21),kgds(22) +c + 980 format('tmow kpds(1) = ',i7,' kpds(2) = ',i7) + 981 format('tmow kpds(3) = ',i7,' kpds(4) = ',i7) + 982 format('tmow kpds(5) = ',i7,' kpds(6) = ',i7) + 983 format('tmow kpds(7) = ',i7,' kpds(8) = ',i7) + 984 format('tmow kpds(9) = ',i7,' kpds(10) = ',i7) + 985 format('tmow kpds(11) = ',i7,' kpds(12) = ',i7) + 986 format('tmow kpds(13) = ',i7,' kpds(14) = ',i7) + 987 format('tmow kpds(15) = ',i7,' kpds(16) = ',i7) + 988 format('tmow kpds(17) = ',i7,' kpds(18) = ',i7) + 989 format('tmow kpds(19) = ',i7,' kpds(20) = ',i7) + 990 format('tmow kpds(21) = ',i7,' kpds(22) = ',i7) + 991 format('tmow kpds(23) = ',i7,' kpds(24) = ',i7) + 992 format('tmow kpds(25) = ',i7) + 880 format('tmow kgds(1) = ',i7,' kgds(2) = ',i7) + 881 format('tmow kgds(3) = ',i7,' kgds(4) = ',i7) + 882 format('tmow kgds(5) = ',i7,' kgds(6) = ',i7) + 883 format('tmow kgds(7) = ',i7,' kgds(8) = ',i7) + 884 format('tmow kgds(9) = ',i7,' kgds(10) = ',i7) + 885 format('tmow kgds(11) = ',i7,' kgds(12) = ',i7) + 886 format('tmow kgds(13) = ',i7,' kgds(14) = ',i7) + 887 format('tmow kgds(15) = ',i7,' kgds(16) = ',i7) + 888 format('tmow kgds(17) = ',i7,' kgds(18) = ',i7) + 889 format('tmow kgds(19) = ',i7,' kgds(20) = ',i7) + 890 format('tmow kgds(20) = ',i7,' kgds(22) = ',i7) +c + print *,'just before call to putgb, kf= ',kf + call putgb (77,kf,kpds,kgds,lb,xmask,ipret) + print *,'just after call to putgb, kf= ',kf + if (ipret == 0) then + print *,' ' + print *,'+++ IPRET = 0 after call to putgb' + print *,' ' + else + print *,' ' + print *,'!!!!!! ERROR: IPRET NE 0 AFTER CALL TO PUTGB !!!' + print *,' ' + endif +c +c bug fix for IBM: flush the output stream so it actually writes + flush(6) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_next_ges (fixlon,fixlat,ist,ifh,imax,jmax + & ,dx,dy,modelid,valid_pt,readflag,maxstorm,istmspd + & ,istmdir,ctype,trkrinfo,ignret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. It does this by using two different +c methods and averaging the results from those two. The +c first method is a simple linear extrapolation made by +c basically drawing a line from the previous position +c through the current fix position and assuming straight +c line motion. The second method is to do a barnes +c smoothing of u & v in the vicinity of the storm at 850, +c 700 & 500 mb to get an average environmental wind +c vector at each level, and then move the storm according +c to the vector at each level. Then a weighted average is +c taken of all these positions from methods 1 & 2 to get +c the consensus for the guess position. NOTE: For a +c regional model and a storm that is relatively close to +c the model boundary, there is a strong possibility that +c the barnes analysis subroutine will fail due to trying +c to access grid points beyond the model's lateral +c boundary. In this case, the redlm & ridlm are halved +c and barnes is called again. If it still fails, then +c just use the result from method 1 as a default. +c +c INPUT: +c fixlon Array with longitudes of fix positions +c fixlat Array with latitudes of fix positions +c ist Storm number currently being processed +c ifh Forecast hour currently being processed +c imax Max number of pts in x-direction for this grid +c jmax Max number of pts in y-direction for this grid +c dx grid-spacing of the model in the i-direction +c dy grid-spacing of the model in the j-direction +c modelid Integer indicating what model's data is being processed +c valid_pt Logical; bitmap indicating if valid data at that pt. +c readflag Logical; Tells whether or not a variable was read in +c for this model +c maxstorm Max # of storms that can be handled in this run +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, eventually +c in the barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c istmspd The speed that the storm would have to move to get from +c the current position to the next guess position +c istmdir The direction in which the storm would have to move to +c get from the current position to the next guess position +c +c LOCAL: +c dt Number of seconds between successive forecast times +c for this particular model. +c dtkm Distance in meters of 1 degree latitude +c icutmax Max number of times to cut the ridlm and redlm in half, +c for use in calling barnes. If you're using a regional +c model and on the first call to barnes you try to access +c a point that's outside the model grid boundary, we'll +c call barnes again, but not before cutting the redlm and +c ridlm in half. icutmax says how many times to allow +c this cutting in half before giving up and just going +c with the extrapolation method. At first writing, we'll +c set icutmax to 2, so that it will allow the ridlm to +c get down to 500 km (originally 2000 km) and the redlm +c to 125 km (originally 500 km). +c *** NOTE: After testing the system, it was found that if +c we cut these radii, the u and v values that are +c calculated from barnes are too strongly influenced by +c the near-storm environment and, especially for asymmetric +c systems, resulted in u and v values being much too strong. +c As such, we will not allow these values to be cut, and if +c we hit the boundaries in barnes, we'll just use the +c extrapolation method, which has seemed to work just fine. +c +c OTHER: (slonfg, slatfg & storm defined in module def_vitals) +c slonfg Array containing first guess longitude positions +c slatfg Array containing first guess latitude positions +c storm Contains tcvitals information +c + USE radii; USE def_vitals; USE set_max_parms; USE grid_bounds + USE tracked_parms; USE level_parms; USE trig_vals; USE trkrparms + USE gen_vitals + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer icutmax,istmspd,istmdir,bskip,ileadtime,ifcsthour + integer ifh,ist,npts,ilonfix,jlatfix,ibeg,jbeg,iend,jend + integer igiret,ignret,icut,iuret,ivret,ibarnct,n,ix1,ix2 + integer icount,imax,jmax,modelid,maxstorm + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real barneswt,extrapwt,dtkm,dt,ucomp,vcomp,xdist,ydist,ydeg + real extraplat,avglat,cosfac,xdeg,extraplon,ylatdegmove_last + real xlondegmove_last,xnumh_last,ylatdegmove_last_perhour + real xlondegmove_last_perhour,xnumh_next,yoldavglat + real yoldcosfac,xdistmove_last,xdistmove_last_perhour + real ynewavglat,ynewcosfac,xdegnew,dx,dy,re,ri,ubar,vbar + real wgttot,uavg,vavg,reold,riold,barnlat,barnlon,wt_total + real tmp_fix_lon_curr,tmp_fix_lon_prev + character*1 :: in_grid, extrap_flag, barnes_flag + character(*) ctype +c logical(1) valid_pt(imax,jmax),readflag(14) +cPENG----2018-06-07 ------------------------ + logical(1) valid_pt(imax,jmax),readflag(19) +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c For updating the first guess, if Method 1 and Method 2 are both +c able to be done, give the following weights to the 2 methods. +c + data barneswt /0.50/, extrapwt /0.50/ +c +c ------------------------------- +c METHOD 1: LINEAR EXTRAPOLATION +c ------------------------------- +c First, just do a simple linear extrapolation from the previous +c fix position through the current fix position. If it's the +c first time (vt=0), then use the storm motion vector and storm +c speed information from the TC Vitals card. +c + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(ist)%tcv_stdir == -99 .or. + & storm(ist)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GET_NEXT_GES, at fcst hour = 0, either ' + print *,'!!! storm motion or storm speed = -99 on TCV card.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(ist)%tcv_stdir + print *,'!!! storm motion speed= ',storm(ist)%tcv_stspd + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + extrap_flag = 'n' + else + ucomp = sin(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + vcomp = cos(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(ist,ifh) + ydeg + avglat = 0.5 * (extraplat + fixlat(ist,ifh)) + if (avglat > 89.5) avglat = 89.0 + if (avglat < -89.5) avglat = -89.0 + cosfac = cos(avglat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(ist,ifh) + xdeg + endif + else + +c Do a simple linear extrapolation of the current motion of the +c storm. Follow a line from the fix position from the last fix +c through the current fix and extrapolate out. To figure out the +c new latitude, just see how many deg lat the storm moved since +c last time and add it to the current fix latitude. To calculate +c the new fix longitude, though, we need to see how many deg lon +c the storm moved since the last time, convert that to the +c distance (km) the storm travelled in the x-direction (at an +c average latitude between the current and previous latitudes), +c and then add that distance on to the current longitude and +c convert that distance to the num of degrees the storm has +c travelled in the x-direction (at an average latitude between +c the current and next(extrap) latitudes). +c +c UPDATE Feb 2009: To account for the possibility of using +c irregularly spaced forecast hours (e.g., 6,10,10.5,...etc), +c I had to modify this linear extrapolation. + + print *,' ' + print *,'xxxx get_next_ges, prev fix lon= ',fixlon(ist,ifh-1) + print *,'xxxx get_next_ges, curr fix lon= ',fixlon(ist,ifh) + print *,' ' + + if (fixlat(ist,ifh-1) > -900.0 .and. + & fixlon(ist,ifh-1) > -900.0) then + + ylatdegmove_last = fixlat(ist,ifh) - fixlat(ist,ifh-1) + + tmp_fix_lon_curr = fixlon(ist,ifh) + tmp_fix_lon_prev = fixlon(ist,ifh-1) + + if (tmp_fix_lon_prev < 0.0 .and. tmp_fix_lon_prev > -25.0) + & then + ! previous lon position is within 25 deg west of the GM + ! and is listed in negative degrees. + if (tmp_fix_lon_curr < 0.0 .and. tmp_fix_lon_curr > -25.0) + & then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 1 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both negative. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr < 25.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 2 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous time' + print *,' is negative, while lon for current time' + print *,' is positive, but within 0-25 deg East.' + print *,' All ok!' + endif + endif + + elseif (tmp_fix_lon_prev > 335.0 .and. + & tmp_fix_lon_prev <= 360.0) then + ! previous lon position is within 25 deg west of the GM + ! and is listed in positive degrees. + if (tmp_fix_lon_curr > 335.0 .and. + & tmp_fix_lon_curr <= 360.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 3 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both positive. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr <= 25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 4 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between 0 & 25. Current tmp_lon' + print *,' has been adjusted to be > 360 for the' + print *,' purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < 0.0 .and. + & tmp_fix_lon_curr >= -25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 5 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is west of the GM and' + print *,' is between 0 & -25. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < -335.0 .and. + & tmp_fix_lon_curr >= -360.0) then + tmp_fix_lon_curr = 720.0 - tmp_fix_lon_curr + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 6 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between -335 & -360. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + + endif + + endif + + xlondegmove_last = tmp_fix_lon_curr - tmp_fix_lon_prev + + xnumh_last = fhreal(ifh) - fhreal(ifh-1) + + ylatdegmove_last_perhour = ylatdegmove_last / xnumh_last + xlondegmove_last_perhour = xlondegmove_last / xnumh_last + + xnumh_next = fhreal(ifh+1) - fhreal(ifh) + + extraplat = fixlat(ist,ifh) + & + (ylatdegmove_last_perhour * xnumh_next) + + yoldavglat = 0.5 * (fixlat(ist,ifh) + fixlat(ist,ifh-1)) + yoldcosfac = cos (dtr * yoldavglat) + xdistmove_last = xlondegmove_last * dtk * yoldcosfac + + xdistmove_last_perhour = xdistmove_last / xnumh_last + + ynewavglat = 0.5 * (extraplat + fixlat(ist,ifh)) + ynewcosfac = cos(dtr * ynewavglat) + xdegnew = (xdistmove_last_perhour * xnumh_next) + & / (dtk * ynewcosfac) + extraplon = tmp_fix_lon_curr + xdegnew + + else + + if ( verb .ge. 3 ) then + print *,' ' + write(6,92) '!!! IN GET_NEXT_GES, at fcst hour = ' + & ,ifhours(ifh),ifclockmins(ifh) + print *,'!!! the lon and lat positions for the previous' + print *,'!!! forecast hour are -999, meaning that this is a' + print *,'!!! new storm, so we cannot use the extrap method.' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + 92 format (1x,a36,i4,':',i2.2) + + extrap_flag = 'n' + + endif + + endif + +c ------------------------------- +c METHOD 2: Barnes analysis +c ------------------------------- +c Do a barnes analysis on the u & v components of the wind near the +c storm to get an average u & v, then advect the storm according to +c the average wind vector obtained. The call to get_ij_bounds is +c needed in order to restrict the number of grid points that are +c searched in the barnes subroutine. See Abstract from this +c subroutine for further details. + + npts = ceiling(ridlm/(dtk*((dx+dy)/2))) + + call get_ij_bounds (npts,0,ridlm,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for ' + print *,'!!! storm number ',ist + endif + + ignret = 92 + return + endif + + if (verb >= 3) then + print *,' ' + print *,' +++ In get_next_ges after call to get_ij_bounds,' + print *,' getting bounds for the barnes analysis...' + print *,' glatmax= ',glatmax,' glatmin= ',glatmin + print *,' glonmax= ',glonmax,' glonmin= ',glonmin + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + print *,' ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + endif + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if ((dx+dy)/2 > 0.20) then + bskip = 1 + else if ((dx+dy)/2 > 0.10 .and. (dx+dy)/2 <= 0.20) then + bskip = 2 + else if ((dx+dy)/2 > 0.05 .and. (dx+dy)/2 <= 0.10) then + bskip = 3 + else if ((dx+dy)/2 > 0.03 .and. (dx+dy)/2 <= 0.05) then + bskip = 5 + else if ((dx+dy)/2 <= 0.03) then + bskip = 10 + endif + +c Calculate average wind at each level (currently: 850, 700 & 500) + + re = redlm + ri = ridlm + icut = 0 + + if (trkrinfo%type == 'midlat') then + icutmax = 2 + else + icutmax = 1 + endif + + radmaxloop: do while (icut <= icutmax .and. in_grid == 'n') + + ubar = 0.0; vbar = 0.0 + iuret = 0; ivret = 0 + wgttot = 0.0 + ibarnct = 0 + barnes_flag = 'n' + + levelloop: do n=1,nlevg + + select case (n) + case (1); ix1=3; ix2=4 ! For 850 mb readflags + case (2); ix1=5; ix2=6 ! For 700 mb readflags + case (3); ix1=12; ix2=13 ! For 500 mb readflags + end select + + if (readflag(ix1) .and. readflag(ix2)) then + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,u(1,1,n),valid_pt + & ,bskip,re,ri,uavg,icount,ctype,trkrinfo,iuret) + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,v(1,1,n),valid_pt + & ,bskip,re,ri,vavg,icount,ctype,trkrinfo,ivret) + + if (iuret /= 0 .or. ivret /= 0) then + +c ...barnes probably tried to access a pt outside the grid +c domain. So, reduce by half the distance from the center +c of the farthest pt that barnes tries to access, exit this +c loop, and try it again with the smaller re and ri. + + iuret = 96; ivret = 96 + reold = re + riold = ri + re = 0.5 * re + ri = 0.5 * ri + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: While attempting to use the barnes ' + print *,'method to update the first guess, the ' + print *,'algorithm tried to access a grid point that ' + print *,'does not have valid data, meaning that too ' + print *,'large a radius is being searched. So, the 2 ' + print *,'radii, re and ri, are being halved and, if the' + print *,'value of icutmax > 0, the algorithm will be ' + print *,'run again. Otherwise, if icutmax = 0, only ' + print *,'the extrapolation method will be used.' + print *,'iuret= ',iuret,' ivret= ',ivret,' icut= ',icut + print *,'Old re = ',reold,' New re = ',re + print *,'Old ri = ',riold,' New ri = ',ri + endif + + exit levelloop + + else + ubar = ubar + wgts(n) * uavg + vbar = vbar + wgts(n) * vavg + wgttot = wgttot + wgts(n) + ibarnct = ibarnct + 1 + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, ix1= ',ix1,' ix2= ',ix2 + print *,' uavg= ',uavg,' vavg= ',vavg + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' n= ',n,' wgts(n)= ',wgts(n),' wgttot= ' + & ,wgttot + print *,' ibarnct= ',ibarnct + print *,' ' + print *,' ' + endif + endif + + endif + + enddo levelloop + + if (ibarnct > 0 .and. wgttot > 0.0) then + barnes_flag = 'y' + in_grid = 'y' + ubar = ubar / wgttot + vbar = vbar / wgttot + barnlat = fixlat(ist,ifh) + (vbar * dt)/dtkm + cosfac = cos (dtr * 0.5 * (fixlat(ist,ifh) + barnlat)) + barnlon = fixlon(ist,ifh) + (ubar * dt)/(dtkm * cosfac) + + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, mean stats follow: ' + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' wgttot= ',wgttot + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' barnlon= ',barnlon,' barnlat= ',barnlat + print *,' dt= ',dt,' dtkm= ',dtkm,' cosfac= ',cosfac + endif + + +c This next if statement says that if we've had to reduce the +c size of the barnes analysis domain twice already, then we've +c only done the analysis on a much smaller area, and this +c doesn't give us as good a picture of the average winds in the +c area of the storm, so reduce the emphasis we place on the +c barnes method. + + if (icut >= 2) barneswt = barneswt / 2. + + else + barnes_flag = 'n' + endif + + icut = icut + 1 + + enddo radmaxloop + +c --------------------- +c Average the results +c --------------------- +c Now do a weighted average of the positions obtained from the +c linear extrapolation and the barnes analysis methods. + + if (extrap_flag == 'y' .and. barnes_flag == 'y') then + wt_total = barneswt + extrapwt + slatfg(ist,ifh+1) = (barneswt * barnlat + extrapwt * extraplat) + & / wt_total + + ! Note that in any of these statements just below, in order for + ! any of these to be > 360, the original fixlon must be close + ! to 360, i.e., in the far eastern part of the grid, as opposed + ! to being in the far western part (e.g., 0-2 deg East or so). + ! Conversely, for any of these to be < 0, the original fixlon + ! must be close to 0, i.e., in the far *western* part of the + ! grid. + +c yyyy + + if (fixlon(ist,ifh) > 330.0) then + + ! In this part of the IF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being 330+, to be consistent with the fixlon for + ! this time. + + if (extraplon > 330. .and. barnlon > 330.) then + + continue ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (extraplon > 330. .and. + & (barnlon >= 0.0 .and. barnlon < 30.)) then + + ! extraplon > 330, but barnlon is in the 0-30 range, so + ! we need to convert the barnlon value to be 360+ + + barnlon = barnlon + 360. + + elseif (extraplon > 330. .and. barnlon < 0.) then + + ! extraplon > 330, but barnlon is < 0, so + ! we need to convert the barnlon value to be positive... + + barnlon = barnlon + 360. + + elseif (barnlon > 330. .and. + & (extraplon >= 0.0 .and. extraplon < 30.)) then + + ! barnlon > 330, but extraplon is in the 0-30 range, so + ! we need to convert the extraplon value to be 360+ + + extraplon = extraplon + 360. + + elseif (barnlon > 330. .and. extraplon < 0.) then + + ! barnlon > 330, but extraplon is < 0, so + ! we need to convert the extraplon value to be positive... + + extraplon = extraplon + 360. + + endif + + elseif (fixlon(ist,ifh) >= 0. and. fixlon(ist,ifh) < 30.0) then + + ! In this part of the ELSEIF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being in the reference of >360 since that is what the + ! code below this is expecting with the computation of + ! slonfg for the next lead time. + + if ((extraplon >= 0. .and. extraplon < 60.) .and. + & (barnlon >= 0. .and. barnlon < 60.)) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon < 0. .and. extraplon > -60.) .and. + & (barnlon < 0. .and. barnlon > -60.)) then + + ! convert extraplon and barnlon to be positive + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon < 0.) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon < 0.) then + + extraplon = extraplon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon > 330.) then + + barnlon = barnlon + 360. + + elseif (barnlon >= 330. .and. extraplon < 60.) then + + extraplon = extraplon + 360. + + elseif (extraplon >= 330. .and. barnlon < 60.) then + + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon > 330.) then + + extraplon = extraplon + 360. + + endif + + else + + continue ! extraplon and barnlon do not need to be modified + ! since there should be no way that a storm + ! currently east of 30E and west of 30W could make + ! it to the Greenwich Mer in one forecast interval + + endif + + print *,' ' + print *,'+++ In get_next_ges, before averaging the 2 methods, ' + print *,' Raw (no conversion for GM wrap) barnlon= ' + & ,barnlon + print *,' Raw (no conversion for GM wrap) extraplon= ' + & ,extraplon + + slonfg(ist,ifh+1) = (barneswt * barnlon + extrapwt * extraplon) + & / wt_total + + if (slonfg(ist,ifh+1) > 360.) then + ! If we've GM-wrapped past 360, adjust it to be 0-360... + slonfg(ist,ifh+1) = slonfg(ist,ifh+1) - 360. + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'y' .and. barnes_flag == 'n') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_next_ges, barnes method was not ' + print *,'!!! done for updating the first guess for this ' + print *,'!!! storm. Only the linear extrapolation method ' + print *,'!!! was used.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + slatfg(ist,ifh+1) = extraplat + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 0.0,0.0,0.0 + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'n' .and. barnes_flag == 'y') then + slatfg(ist,ifh+1) = barnlat + if (barnlon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (barnlon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = barnlon + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 360.-barnlon,barnlat + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges, new position guess not' + print *,'!!! made. Could not get guess using either barnes' + print *,'!!! method or extrapolation method.' + print *,'!!! extrap_flag = ',extrap_flag + print *,'!!! barnes_flag = ',barnes_flag + print *,'!!! Storm number = ',ist,' ifh = ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + write (6,41) 0.0,0.0,0.0 + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 95 + endif + + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| Current fix & updated fix positions |' + print *,'-------------------------------------------------- ' + print *,'| In get_next_ges, current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',ist + print *,'| Return code from get_next_ges = ',ignret + print *,'| Storm Name = ',storm(ist)%tcv_storm_name + print *,'| Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + write (6,21) fixlat(ist,ifh) + write (6,23) 360.-fixlon(ist,ifh),fixlon(ist,ifh) + write (6,25) slatfg(ist,ifh+1) + write (6,27) 360.-slonfg(ist,ifh+1),slonfg(ist,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current fix lat is ',f7.2) + 23 format (' | Current fix lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') +c 41 format (' --- barnlon= ',f7.2,'W barnlat= ',f7.2) +c 43 format (' --- extraplon= ',f7.2,'W extraplat= ',f7.2) + + 41 format (' --- barnlon= ',f7.2,'E (',f7.2 + & ,'W) barnlat= ',f7.2) + 43 format (' --- extraplon= ',f7.2,'E (',f7.2 + & ,'W) extraplat= ',f7.2) + +c Now calculate the speed that the storm would have to move at in +c order to make it to the next forecast position. We will use +c this information in writing out the "gen_vitals" record, if this +c is requested. + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh) + & ,slonfg(ist,ifh+1),slatfg(ist,ifh+1),dist,degrees) + + ! convert distance from km to meters, then get speed in m/s. + + distm = dist * 1000. + stmspd = distm / dt + istmspd = int ((stmspd * 10) + 0.5) + + xincr = slonfg(ist,ifh+1) - fixlon(ist,ifh) + yincr = slatfg(ist,ifh+1) - fixlat(ist,ifh) + + if ( verb .ge. 3 ) then + print *,'iocheck, dist= ',dist,' distm= ',distm + print *,'iocheck, stmspd= ',stmspd,' istmspd= ',istmspd + print *,'iocheck, xincr= ',xincr,' yincr= ',yincr + endif + + if (xincr < 0.0 .and. slonfg(ist,ifh+1) < 30.0 .and. + & fixlon(ist,ifh) > 300.0) then + ! This means we have a storm moving east across the GM, and + ! so we are subtracting, for example, something like + ! 0.5 - 359.5, so redo xincr, but add 360 to slonfg first... + xincr = (slonfg(ist,ifh+1) + 360.0) - fixlon(ist,ifh) + else if (xincr > 300.0) then + ! This means we have a storm moving west across the GM, and + ! so we are subtracting, for example, something like + ! 359.5 - 0.5, so redo xincr, but add 360 to fixlon first... + xincr = slonfg(ist,ifh+1) - (fixlon(ist,ifh) + 360.0) + endif + + if (xincr == 0.0) then + if (yincr == 0.0) then + stmdir = 0.0 + else if (yincr > 0) then + stmdir = 360.0 + else if (yincr < 0) then + stmdir = 180.0 + endif + else if (xincr > 0.0) then + if (yincr == 0.0) then + stmdir = 90.0 + else + arct = atan(yincr/xincr) + stmdir = 90. - arct / dtr + endif + else if (xincr < 0.0) then + if (yincr == 0.0) then + stmdir = 270.0 + else + arct = atan(yincr/xincr) + stmdir = 270. - arct / dtr + endif + endif + + istmdir = int (stmdir + 0.5) + if (istmdir > 360) then + istmdir = 360 + else if (istmdir < 0) then + istmdir = 0 + endif + + if ( verb .ge. 3 ) then + print *,'iocheck, stmdir= ',stmdir,' istmdir= ',istmdir + endif + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine advect_tcvitals_from_hour0 (fixlon,fixlat,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. As of 11/2016, it is called only for the case in +c which we've got NetCDF data and no hour0 data, and so we want to +c simply take the TC Vitals data and advect the current position to +c a position at the next lead time. We can't use the code in +c subroutine get_next_ges because there are certain allocatable +c arrays in that subroutine that need to have been allocated first, +c and at this point prior to the first lead time in hour0, they +c haven't been allocated. +c +c INPUT: +c inctcv Index for storm number currently being processed +c ifh Forecast hour currently being processed +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c iatret Return code from this subroutine +c + USE def_vitals; USE trkrparms; USE tracked_parms + USE verbose_output; USE trig_vals; USE set_max_parms + USE gen_vitals + + type (trackstuff) trkrinfo + integer iatret,inctcv + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real ucomp,vcomp,xdist,ydist,ydeg,dt,extraplat + real cosfac + real dtkm +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c ------------------------------------------------------------------ +c Using the storm motion vector and storm translation speed as read +c from the TC Vitals card, do a simple linear extrapolation from the +c current observed (TC Vitals) position and advect the storm to a +c position at the next lead time. +c ------------------------------------------------------------------ + + iatret = 0 + + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(inctcv)%tcv_stdir == -99 .or. + & storm(inctcv)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In advect_tcvitals_from_hour0, at fcst hour= 0' + print *,'!!! either storm motion or storm speed = -99 on ' + print *,'!!! TCV card, ist= inctcv= ',inctcv,' ifh= ',ifh + print *,'!!! Storm name = ',storm(inctcv)%tcv_storm_name + print *,'!!! Storm ID = ',storm(inctcv)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(inctcv)%tcv_stdir + print *,'!!! storm motion speed= ',storm(inctcv)%tcv_stspd + print *,'... CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS ...' + print *,' ' + print *,'... Instead, we will simply use the current ' + print *,'... observed position from TC Vitals and hope that' + print *,'... it is close enough at the next lead time for ' + print *,'... the tracker to be able to still track it.' + print *,' ' + endif + extraplat = slatfg(inctcv,ifh) + extraplon = slonfg(inctcv,ifh) + else + ucomp = sin(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + vcomp = cos(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(inctcv,ifh) + ydeg + cosfac = cos(extraplat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(inctcv,ifh) + xdeg + endif + else + print *,' ' + print *,'!!! ERROR: In advect_tcvitals_from_hour0, the value of' + print *,' ifh is > 1, and this routine should only be called' + print *,' if ifh=1 (i.e., for hour0). STOPPING....' + print *,' ' + stop 95 + endif + + slatfg(inctcv,ifh+1) = extraplat + + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon >360 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon < 0 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + else + slonfg(inctcv,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| In advect_tcvitals_from_hour0, info on the ' + print *,'| positions for the current and next lead times ' + print *,'| follow: ' + print *,'-------------------------------------------------- ' + print *,'| current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',inctcv + print *,'| Return code from advect_tcvitals_from_hour0= ',iatret + print *,'| Storm Name = ',storm(inctcv)%tcv_storm_name + print *,'| Storm ID = ',storm(inctcv)%tcv_storm_id + write (6,420) gstorm(inctcv)%gv_gen_date + & ,gstorm(inctcv)%gv_gen_fhr + & ,gstorm(inctcv)%gv_gen_lat + & ,gstorm(inctcv)%gv_gen_latns,gstorm(inctcv)%gv_gen_lon + & ,gstorm(inctcv)%gv_gen_lonew,gstorm(inctcv)%gv_gen_type + write (6,21) fixlat(inctcv,ifh) + write (6,23) 360.-fixlon(inctcv,ifh),fixlon(inctcv,ifh) + write (6,25) slatfg(inctcv,ifh+1) + write (6,27) 360.-slonfg(inctcv,ifh+1),slonfg(inctcv,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current TC Vitals lat is ',f7.2) + 23 format (' | Current TC Vitals lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') + + + return + end +c +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getradii (xcenlon,xcenlat,imax,jmax,dx,dy,valid_pt + & ,cstormid,ifcsthr,vmaxwind,vradius,trkrinfo + & ,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) +c +c ABSTRACT: This subroutine looks through the wind data near an +c input storm center (fixlon,fixlat) and gets the radii of various +c surface winds in each of the 4 storm quadrants (NE,NW,SE,SW). +c The wind thresholds that are sought are gale force (34kt|17.5m/s), +c storm force (50kt|25.7m/s), and hurricane force (64kt|32.9m/s). +c This subroutine calls the Cray subroutine orders, which is a +c Cray-optimized sort routine. +c +c UPDATE (AUG 2001): The Cray subroutine orders was ported to the +c SP by NCEP personnel. On the SP version, some changes were +c apparently made so that the size of the arrays for calling +c arguments 2, 3 and 4 (iwork, dtemp and isortix in my calling +c routine) must be the same. This was not the case on the Crays, +c and this was causing the tracker to crash for cases far north +c on fine grids (GFDL 1/3 grid). +c +c UPDATE (AUG 2012): The call to the Cray subroutine orders was +c replaced with a call to qsort, which uses a quicksort sorting +c algorithm. While this is not the fastest sorting routine out +c there, we don't do a lot of sorting here, and qsort is simple +c and it is portable. +c +c UPDATE (April 2013): For the radii, we encountered a problem with +c radmax being too small. It was set at 650 km. Hurricane Sandy +c exceeded this in the models, so the values returned from getradii +c were close to the default radmax value of 650 km (350 nm), instead +c of much higher as they should have been. To fix it, we now use an +c iterative technique, where we start with radmax as a small value +c (450 km). If getradii returns a value for R34 in a quadrant that +c does not exceed 0.97*radmax, then that value is ok. If it does +c exceed 0.97*radmax, then we bump up radmax by 50 km and call +c getradii again, looking to diagnose radii only in those quadrants +c where the need_to_expand_r34 flag = 'n'. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c cstormid 3-character storm ATCF ID (e.g., 03L, 11E, etc) +c ifcsthr integer value for current forecast hour +c trkrinfo derived type containing various info on the storm +c need_to_expand_r34 1-character array that specifies which of the +c 4 quadrants still need to be expanded on this time +c through getradii in order to get an R34 value that is +c not right at the outermost boundary. +c vmaxwind max wind (in m/s) that was reported from the +c get_max_wind subroutine +c radmax input max radius (km) that will be used for this +c iteration of getradii. +c first_time_thru_getradii logical flag. It is used so that any +c checking for 50- or 64-kt radii is only done on the +c first time through getradii. Only the checking for +c 34-kt radii is done on multiple iterations. +c igrct integer that indicates what iteration of getradii this +c call is. +c +c OUTPUT: +c +c igrret return code from this subroutine +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c +c LOCAL: +c +c radmax the maximum radius to look for winds for the various +c thresholds. +c quadinfo This array contains the magnitude of the near-surface +c winds and the distance from the gridpoint to the fix +c position for each point in each quadrant that is within +c the maximum allowed radius, radmax. quadinfo is +c allocated within this subroutine, and is allocated as +c (quadrant, num_pts_in_quadrant, data_type), where +c data_type is either windspeed(1) or distance(2) from +c storm center to grid point. +c quadmax This array contains the max surface wind in each +c quadrant, plus the location of it and the distance from +c the storm center. This information is critical to +c identifying when this subroutine is malfunctioning. + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE level_parms + USE trkrparms + USE verbose_output + +c + type (trackstuff) trkrinfo +c + logical(1) valid_pt(imax,jmax) + logical(1) first_time_thru_getradii +c dimension iwork(257) + real, allocatable :: quadinfo(:,:,:),iwork(:) + real quadmax(4,4) + real exactdistnm,exactdistkm,radmax,degrees,cosarg + real rlonb,rlonc,rlatb,rlatc,vmaxwind + real pt_heading_rad,pt_heading,d + integer, allocatable :: isortix(:) + integer iwindix,ipoint,ifcsthr,igrct + integer quadct(4),vradius(3,4) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: dtemp(:) + real :: windthresh(3) = (/17.5,25.7,32.9/) + character cstormid*3 + character :: need_to_expand_r34(4)*1 + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *************************************************** ' + print *,' AT BEGINNING OF GETRADII, input radmax= ',radmax + print *,' *************************************************** ' + print *,' ' + print *,'xcenlon= ',xcenlon,' xcenlat= ',xcenlat + print *,'imax= ',imax,' jmax= ',jmax,' dx= ',dx,' dy= ',dy + endif + + igrret = 0 + +c ----------------------------------------------------------- +c PART 1: Define the maximum radius for which you'll search +c for the wind values, and then get the beginning and ending +c i and j points for that sub-region to search. Define this +c maximum radius (radmax) in terms of km. +c ----------------------------------------------------------- + +c radmax = 650.0 ! This value is in units of km. With April 2013 +c ! update, this is now defined in calling routine + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-A....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine getradii' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-B....' + stop 98 + endif + + igrret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmax is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmax/(dtk*dx))/cosfac) + numjpts = ceiling(radmax/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (ibeg < 1) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-C...' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jbeg < 1) jbeg = 1 + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in getradii calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Wind radii will not be calculated for this time.' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-D....' + stop 98 + endif + + igrret = 99 + return + endif + + if (iend > imax) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-E....' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getradii, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + +c ----------------------------------------------------------- +c PART 2: Within the area of grid points defined by jbeg, +c jend, ibeg and iend, (1) calculate all the wind speeds at +c each grid point, (2) calculate all of the distances from +c each grid point to the storm center, (3) assign each grid +c point to one of the 4 quadrants (NE,NW,SE,SW), (4) in each +c quadrant, sort the points, based on windspeed. +c ----------------------------------------------------------- + + jnum = jend - jbeg + 1 + inum = iend - ibeg + 1 +c numalloc = ((jnum * inum) / 2) + inum/2 + jnum/2 + numalloc = jnum * inum + inum/2 + jnum/2 + + if ( verb .ge. 3 ) then + print *,'in getradii, numalloc= ',numalloc,' radmax= ',radmax + endif + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + allocate (quadinfo(4,numalloc,2),stat=iqa) + + if (iqa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub getradii allocating quadinfo array.' + print *,'!!! iqa = ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-F....' + stop 98 + endif + + igrret = 94 + return + endif + + quadct = 0 + +c Calculate the distances and wind speeds at each grid point. If +c the distance is < radmax, include that wind info in the +c appropriate quadinfo array location for that quadrant. + + quadmax = 0.0 + + jloop: do j=jbeg,jend + iloop: do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point in question = ',i + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-G...' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub getradii' + print *,'!!! for a non-global grid. i= ',i + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-H....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + if (dist > radmax) cycle iloop + + if (valid_pt(ip,j)) then + + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + +cc print *,'i= ',i,' j= ',j,' dist= ',dist,' vmag= ',vmag + + ! Calculate the angle from the center point to this point + ! and then assign this point to the appropriate quadrant bin + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-xcenlon) * dtr + rlatb = xcenlat * dtr + d = degrees * dtr + +c write (6,59) 360.-xcenlon,xcenlat,360.-glon(ip),glat +c +c write (6,61) d/dtr,rlatc/dtr,360.-(rlonc/dtr),rlatb/dtr +c & ,360.-(rlonb/dtr),sin(rlatc),sin(rlatb),cos(d) +c & ,sin(d),cos(rlatb) +c +c +c 59 format (1x,'+++ gr, xcenlon= ',f8.3,'W xcenlat= ' +c & ,f8.3,' glon= ',f8.3,'W glat= ',f8.3) +c +c 61 format (1x,'+++ gr, d rlatc rlonc rlatb rlonb= ',5f9.4 +c & ,' sin(rlatc)= ',f8.6,' sin(rlatb)= ',f8.6 +c & ,' cos(d)= ',f8.6,' sin(d)= ',f8.6 +c & ,' cos(rlatb)= ',f8.6) + + if (d == 0.0) then + + pt_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d)) / + & (sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_heading_rad = acos(cosarg) + else + pt_heading_rad = 2*pi - acos(cosarg) + endif + + pt_heading = pt_heading_rad / dtr + + endif + + if (pt_heading >= 0.0 .and. pt_heading < 90.) then + ! NE quadrant + iq = 1 + else if (pt_heading >= 90.0 .and. pt_heading < 180.) then + ! SE quadrant + iq = 2 + else if (pt_heading >= 180.0 .and. pt_heading < 270.) then + ! SW quadrant + iq = 3 + else if (pt_heading >= 270.0 .and. pt_heading <= 360.) then + ! NW quadrant + iq = 4 + endif + +c write (6,73) xcenlat,360.-xcenlon,j,i,ip,glat(j) +c & ,360.-glon(ip),pt_heading,iq + + 73 format (1x,'+++ getradii clat clon: ',f6.2,' ',f7.2,'W',3i4 + & ,' plat plon: ',f6.2,' ',f7.2,'W Dir: ',f7.2 + & ,' Quad: ',i2) + + quadct(iq) = quadct(iq) + 1 + quadinfo(iq,quadct(iq),1) = vmag + quadinfo(iq,quadct(iq),2) = dist + if (vmag > quadmax(iq,4)) then + quadmax(iq,1) = glon(ip) + quadmax(iq,2) = glat(j) + quadmax(iq,3) = dist + quadmax(iq,4) = vmag + endif + + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After loop, quadct(1)= ',quadct(1),' quadct(2)= ' + & ,quadct(2) + print *,' quadct(3)= ',quadct(3),' quadct(4)= ' + & ,quadct(4) + print *,' ' + + write (6,110) cstormid,ifcsthr,'NE',quadmax(1,1),quadmax(1,2) + & ,quadmax(1,3)*0.539638,quadmax(1,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SE',quadmax(2,1),quadmax(2,2) + & ,quadmax(2,3)*0.539638,quadmax(2,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SW',quadmax(3,1),quadmax(3,2) + & ,quadmax(3,3)*0.539638,quadmax(3,4)*1.9427 + write (6,110) cstormid,ifcsthr,'NW',quadmax(4,1),quadmax(4,2) + & ,quadmax(4,3)*0.539638,quadmax(4,4)*1.9427 + print *,' ' + + 110 format (' quadmax: ',a3,1x,i3.3,1x,a2,1x,' lon: ',f6.2,'E',1x + & ,' lat: ',f6.2,' radius: ',f7.2,' nm',2x,' vmag: ' + & ,f6.2,' kts') + endif + +c Now go through each quadrant and put the wind speed distance info +c into a temporary array (dtemp), sort that array, and then scan +c through that array to find the various thresholds. + + quadrantloop: do k=1,4 + + if (need_to_expand_r34(k) == 'y') then + print *,'---> R34 search underway for quadrant ',k + & ,' radmax= ',radmax + continue + else + print *,'+ R34 okay for quadrant ',k,'... skipping...' + cycle quadrantloop + endif + + if (allocated(isortix)) deallocate (isortix) + if (allocated(dtemp)) deallocate (dtemp) + if (allocated(iwork)) deallocate (iwork) + allocate (isortix(quadct(k)),stat=iisa) + allocate (dtemp(quadct(k)),stat=idta) + allocate (iwork(quadct(k)),stat=iwa) + if (iisa /= 0 .or. idta /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii allocating isortix, dtemp' + print *,'!!! or iwork array for quadrant= ',k + print *,'!!! iisa = ',iisa,' idta= ',idta,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-I....' + stop 98 + endif + + itret = 94 + return + endif + +c ------------------- + + do m=1,quadct(k) + dtemp(m) = quadinfo(k,m,2) + enddo + + imode = 2 + isortix = 0 + + call qsort (dtemp,isortix,quadct(k)) + +ccccc call orders (imode,iwork,dtemp,isortix,quadct(k),1,8,1) +cccc call orders_4byte (imode,iwork,dtemp,isortix +cccc & ,quadct(k),1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' +c ************************************************************** +c--- mf 20100609 +c CAUSE OF SEG FAULT!!!!!!!! -- not sure still an issue if dtemp +c properly allocated +c + !print *,' dtemp(isortix(1)) = ',dtemp(isortix(1)) + print *,' dtemp(isortix(quadct(k)))= ' + & ,dtemp(isortix(quadct(k))) + print *,' isortix(1) = ',isortix(1) + print *,' isortix(quadct(k)) = ',isortix(quadct(k)) + endif + +c ! Uncomment these next lines to see a listing in the output of +c ! all wind values & distances in this quadrant less than radmax +c do iqq = 1,quadct(k) +c print *,' iqq= ',iqq,' vmag= ',quadinfo(k,isortix(iqq),1) +c & ,' dist= ',quadinfo(k,isortix(iqq),2) +c enddo + +c ------------------- + + if (quadct(k) < 2) then ! not enough members in array + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GETRADII, NOT ENOUGH MEMBERS IN ARRAY FOR' + print *,'!!! QUADRANT #',k,' .... # members = quadct(k)= ' + & ,quadct(k) + print *,'!!! SETTING ALL VRADII = 0 for quadrant = ',k + endif + + vradius(1,k) = 0 + vradius(2,k) = 0 + vradius(3,k) = 0 + cycle quadrantloop + endif + +c Within this quadrant, go through the sorted array of wind +c magnitudes and compare those wind values against the set +c wind thresholds to get the wind radii. The array has +c been sorted by distance from the storm center in order of +c closest (ipoint=1) to farthest (ipoint=quadct(k)). We +c analyze these wind values by starting at the farthest +c point and moving inward until we hit a point that has a +c wind value of at least 34-knot winds (17.5 m/s). When +c we find that point, we interpolate between that point and +c the next farthest out point to get the distance that would +c be for the exact 17.5 m/s value. We then continue searching +c through the wind values down closer to the storm center to +c see if we can find values for the 50- and 64-knot winds. + + iwindix = 1 + ipoint = quadct(k) + 1 + +c print *,'drp: quad= ',k,' quadct= ',quadct(k) + + threshloop: do while (iwindix <= 3 .and. ipoint > 1) + + if (iwindix > 1) then + if (first_time_thru_getradii) then + + ! We are only doing the wind radii for 50 and 64 kts on + ! the first time through subroutine getradii (we only + ! need to do the multiple call iterations for 34 kts). + ! + ! Make sure vmax for this lead time exceeds the radii + ! threshold being diagnosed. The check below avoids, + ! for example, reporting 50-kt wind radii when the max + ! wind diagnosed was only 44 kts. This can happen since + ! the radius for searching for radii is larger than the + ! radius for searching for the max wind. + if (vmaxwind >= windthresh(iwindix)) then + if (verb >= 3) then +c print *,' ' +c print *,' +++ vmaxwind of ',vmaxwind,' m/s exceeds' +c print *,' +++ threshold of ',windthresh(iwindix) +c print *,' +++ (m/s), so radii checking will continue' +c print *,' +++ for this threshold.' +c print *,' +++ igrct= ',igrct,' ipoint= ',ipoint +c & ,' iwindix= ',iwindix + continue + endif + continue + else + if (verb >= 3) then + print *,' ' + print *,' --- vmaxwind of ',vmaxwind,' m/s does NOT' + print *,' - - exceed threshold of ' + & ,windthresh(iwindix) + print *,' - - (m/s), so radii checking will NOT be ' + print *,' - - performed for this threshold.' + endif + iwindix = iwindix + 1 + cycle threshloop + endif + else + iwindix = iwindix + 1 + cycle threshloop + endif + endif + + ipoint = ipoint - 1 + + if (quadinfo(k,isortix(ipoint),1) < windthresh(iwindix)) then + cycle threshloop + else + if (ipoint == quadct(k)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In getradii, a max wind radius was' + print *,'!!! found at the maximum radius checked, so ' + print *,'!!! you may want to make sure that you are' + print *,'!!! checking at a far enough distance from ' + print *,'!!! the fix position, that is, you may want to' + print *,'!!! increase the value of radmax in subroutine' + print *,'!!! getradii. Currently, radmax (km) = ',radmax + print *,'!!! iwindix = ',iwindix,' quadrant= ',k + endif + + vradius(iwindix,k) = int( ((quadinfo(k,isortix(ipoint),2) + & * 0.5396) / 5.0) + 0.5) * 5 + else + +c Interpolate between the 2 closest distances to each wind +c threshold to get "exact" distance to that wind threshold +c radius, convert from km to nm, and then round to the +c nearest 5 nm (since TPC uses this precision). +c 7/23/98 UPDATE: Jim Gross has asked that values not be +c rounded to the nearest 5 nm, but rather only to the +c nearest 1 nm. + + exactdistkm = quadinfo(k,isortix(ipoint),2) + + & ( (quadinfo(k,isortix(ipoint),1) - windthresh(iwindix)) / + & (quadinfo(k,isortix(ipoint),1) - + & quadinfo(k,isortix(ipoint+1),1)) * + & ( (quadinfo(k,isortix(ipoint+1),2) - + & quadinfo(k,isortix(ipoint),2)) ) ) + + exactdistnm = exactdistkm * 0.5396 ! Convert km to nm + vradius(iwindix,k) = int(exactdistnm + 0.5) + +cc vradius(iwindix,k) = int( (exactdistnm / 5.0) + 0.5) * 5 + + + if ( verb .ge. 3 ) then + print *,'iwindix= ',iwindix,' exactdistnm = ' + & ,exactdistnm + print *,'vradius(iwindix,k) =',vradius(iwindix,k) + endif + + endif + +c The possibility exists, especially for coarse output +c grids, that there could be a jump over more than 1 wind- +c thresh category when going from 1 grid point to the next, so +c we need to account for this. For example, if 1 point has +c vmag = 15 m/s and the next point closer in has vmag = 28 +c m/s, then between those 2 points you have the thresholds +c for gale force AND storm force winds, so to be safe, we +c actually need to add 1 to ipoint and re-check the current +c point, if the wind value at that point is found to be +c greater than a wind threshold value (which it has if you've +c gotten to this point in threshloop). + + ipoint = ipoint + 1 + + iwindix = iwindix + 1 + + endif + + enddo threshloop + + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (idta /= 0 .or. iisa /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating isortix or' + print *,'!!! dtemp or work for quadrant= ',k + print *,'!!! idta= ',idta,' iisa= ',iisa,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-J....' + stop 98 + endif + + itret = 94 + return + endif + + enddo quadrantloop + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating quadinfo array.' + print *,'!!! iqa= ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-K....' + stop 98 + endif + + itret = 94 + return + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_max_wind (xcenlon,xcenlat,imax,jmax,dx,dy + & ,valid_pt,levsfc,vmax,trkrinfo,rmax,igmwret) +c +c ABSTRACT: This subroutine looks for the maximum near-surface wind +c near the storm center. This subroutine is only concerned with the +c value of the max wind, NOT where it's located radially with +c respect to the center. The value that's returned in vmax is the +c max wind speed in m/s, which are the units the data are stored in. +c However, when the max wind values are output in output_atcf, they +c will be converted from m/s to knots. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c levsfc integer holding the value of the array member that holds +c the near-surface winds in the u and v arrays (at orig +c writing, it's = 4). +c +c OUTPUT: +c +c vmax value of maximum near-surface wind near the storm ctr +c rmax radius of max winds +c igmwret return code from this subroutine +c +c LOCAL: +c +c radmaxwind the maximum radius to look for a max wind near the +c storm center. You have to allow this to be bigger for +c model grids with coarse resolution (ECMWF 2.5 degree). + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real radmaxwind,degrees,dx,dy,rmax + logical(1) valid_pt(imax,jmax) +c + igmwret = 0 + rmax = -99.0 + + if ((dx+dy)/2. <= 1.25) then + if ((dx+dy)/2. <= 0.25) then + radmaxwind = 300.0 + else + radmaxwind = 300.0 + endif + else + radmaxwind = 500.0 + endif + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmaxwind is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmaxwind/(dtk*dx))/cosfac) + numjpts = ceiling(radmaxwind/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_max_wind calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Value of vmax will be set to 0 for this time.' + endif + + vmax = 0.0 + igmwret = 99 + return + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + + print *,' ' + print *,'In get_max_wind, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + + vmax = 0.0 + do j=jbeg,jend + do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point = ',i + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + + if (dist > radmaxwind) cycle + + if (valid_pt(ip,j)) then + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + if (vmag > vmax) then + vmax = vmag + rmax = dist * 0.539638 ! convert from km to nm + endif + endif + + enddo + enddo + + if ( verb .ge. 3 ) then + print *,'At end of get_max_wind, vmax= ',vmax,' rmax= ',rmax + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine fixcenter (clon,clat,ist,ifh,calcparm,geslon,geslat + & ,inp,stderr,fixlon,fixlat,xvalues,maxstorm,ifret) +c +c ABSTRACT: This subroutine loops through the different parameters +c for the input storm number (ist) and calculates the +c center position of the storm by taking an average of +c the center positions obtained for those parameters. +c First we check to see which parameters are within a +c max error range (errmax), and we discard those that are +c not within that range. Of the remaining parms, we get +c a mean position, and then we re-calculate the position +c by giving more weight to those estimates that are closer +c to this mean first-guess position estimate. +c +c INPUT: +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c geslon Initial guess longitude for this storm at this fcst hour +c geslat Initial guess latitude for this storm at this fcst hour +c inp contains the input date and model number information +c xvalues The actual max or min data values for each parameter +c maxstorm max # of storms to be handled in this run +c +c INPUT/OUTPUT: +c stderr Standard deviation of the position "error" of the parms +c relative to the guess storm position. As long as the +c distance of a parm center to the guess center is <= +c errpmax, it is included in the std dev calculation. +c +c OUTPUT: +c fixlon Best approximation of storm center's longitude +c fixlat Best approximation of storm center's latitude +c ifret Return code from this subroutine +c +c LOCAL: +c storm Contains tcvitals info for the storms (def_vitals) +c trkerr_avg Sum/avg of the track errors for all parms for this +c fcst hour, regardless of whether or not the error was +c > errmax. It's used for getting the std deviation of +c the position error for this forecast time, to be used +c as part of the errmax calculation for the next fcst +c time. +c iclose Number of parameters whose position estimates are +c found to be within a distance errmax of the guess pos +c wtpos The weight given to each position estimate. It's +c based on the distance from the average position. +c errdist The "error" of the parameter center position relative +c to the storm's guess position. +c avgerr Average "error" of the parameter center positions +c relative to the storm's guess position. +c use4next Logical; If a parm center has been calculated but its +c distance from the guess position is > errmax, we don't +c use this center in calculating the new guess position, +c however we will use this position in calculating the +c standard deviation of the current time's guess +c positions, to be used in calculating the new errmax +c for the next forecast time. So in this subroutine, +c calcparm may be set to FALSE if errdist > errmax, but +c use4next will not be set to FALSE (Actually, it is +c only set to FALSE if errdist > errpmax, which is +c defined in error_parms and is roughly 600km). +c stderr_close Standard deviation of position errors for parms that +c have center estimates that are within a distance +c errmax of the guess position. +c clon_fguess These are the first-guess mean position estimates, +c clat_fguess which are the means of the position estimates that +c are within a distance errmax. These first-guess mean +c positions will be refined by giving more weight to +c individual parameter estimates that are closer to +c this first-guess mean position. +c dist_from_mean Contains the "error" distance of each parameter +c from the first-guess mean position (clon_fguess, +c clat_fguess). NOTE: If a parameter is not within +c a distance errmax of the guess position for this +c time (geslon,geslat), then there will be NO +c dist_from_mean calculated for that parm. +c + USE error_parms; USE set_max_parms; USE inparms; USE def_vitals + USE atcf; USE gen_vitals; USE tracked_parms + USE verbose_output + + type (datecard) inp + + real clon(maxstorm,maxtime,maxtp),temp_clon(maxtp) + real clat(maxstorm,maxtime,maxtp),temp_clat(maxtp) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real trkerr(maxtp),errdist(maxtp),xvalues(maxtp) + real stderr(maxstorm,maxtime),devia(maxtp),wtpos(maxtp) + real dist_from_mean(maxtp) + real degrees,errtmp + integer gt345_ct,lt15_ct + logical(1) calcparm(maxtp,maxstorm),use4next(maxtp) + character charparm(maxtp)*8,charmaxmin(maxtp)*8 +c + data charparm /'zeta 850','zeta 700','circ 850','NOT USED' + & ,'circ 700','NOT USED',' gph 850',' gph 700',' MSLP' + & ,'circ sfc','zeta sfc',' thk 5-8',' thk 2-5',' thk 2-8'/ + data charmaxmin /' Max ',' Max ',' Min ','NOT USED' + & ,' Min ','NOT USED',' Min ',' Min ',' Min ' + & ,' Min ',' Max ',' Max ',' Max ',' Max '/ +c + ifret=0 +c +c We need to judge whether each parameter position is reasonable, +c so we'll check to make sure that the dist from each parameter's +c estimate to the guess position is less than a maximum allowable +c error. If it's the first forecast time, use the initial error max +c (defined as errinit in error_parms) as errmax. Otherwise, the +c max error criterion is that the distance error must not exceed 3 +c times the previous forecast time's standard deviation (after a +c small growth factor has been applied). +c UPDATE 3/5/98: During testing, it was found that just using the +c previous time's stdev made errmax too "jumpy" (i.e., at vt=48h, +c errmax could = 380, and then at vt=54h, errmax could jump down +c to 190, so we've changed it so that it uses an average of the +c stdev's from the 3 previous forecast times to maintain some +c continuity between successive forecast times). +c + if (ifh == 1) then + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR' ) then + errmax = err_gfs_init + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errmax = err_ecm_max + errinit = err_ecm_max + else + errmax = err_reg_init + errinit = err_reg_init + endif + else + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR') then + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errinit = err_ecm_max + else + errinit = err_reg_max + endif + + if (ifh >= 4) then + xavg_stderr = (stderr(ist,ifh-3) + stderr(ist,ifh-2) + & + stderr(ist,ifh-1)) / 3.0 + else if (ifh == 3) then + xavg_stderr = (stderr(ist,ifh-2) + stderr(ist,ifh-1)) / 2.0 + else if (ifh == 2) then + xavg_stderr = stderr(ist,ifh-1) + endif + +c The following errmax statement was replaced by the ensuing 4 +c lines due to a compiler bug on some other platforms: +c errmax = amin1(amax1(3.0*xavg_stderr*errpgro,errinit) +c & ,errpmax) + + errtmp = 3.0*xavg_stderr*errpgro + errmax = max(errtmp,errinit) + errtmp = errpmax + errmax = min(errmax,errtmp) + + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (ifh > 1) then + print '(a42,f8.2,a15,f8.2)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = ' + & ,stderr(ist,ifh-1),' xavg_stderr= ',xavg_stderr + else + print '(a45,a18)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = N/A' + & ,' xavg_stderr= N/A' + endif + print *,'At beg of fixcenter, errpgro = ',errpgro + print *,'At beg of fixcenter, errinit = ',errinit + print *,'At beg of fixcenter, errpmax = ',errpmax + print *,'At beg of fixcenter, ifh= ',ifh,' errmax= ',errmax + endif + + trkerr_avg = 0.0 + iclose = 0; itot4next = 0 + clonsum = 0.0; clatsum = 0.0 + errdist = 0.0 + use4next = .FALSE. + gt345_ct = 0 + lt15_ct = 0 + +c For each parm, check to see if the estimated center is within +c distance errmax of the guess center. If it's within errmax, +c then use that parm for locating the center. If it's NOT +c within errmax, but IS within errpmax, then we still use this +c in calculating the standard deviation of the parameters for +c helping to determine the errmax for the next forecast hour. + +c OLD NOTE: For calculating the std dev to be used for the next +c OLD forecast hour, do NOT use vmag 850, vmag 700 or vmag sfc, since +c OLD those parms are always guaranteed to be within a short range of +c OLD the guess, due to the nature of the algorithm (see subroutine +c OLD get_uv_center for further details on that). + + do ip=1,maxtp + + if (ip == 4 .or. ip == 6) then ! Parms 4 & 6 not defined. + calcparm(ip,ist) = .FALSE. + cycle + endif + if (calcparm(ip,ist)) then + call calcdist (geslon,geslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + errdist(ip) = dist + if (dist <= errpmax) then + use4next(ip) = .TRUE. + trkerr_avg = trkerr_avg + dist + itot4next = itot4next + 1 + endif + if (dist <= errmax) then + iclose = iclose + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 + endif + clonsum = clonsum + clon(ist,ifh,ip) + clatsum = clatsum + clat(ist,ifh,ip) + else + calcparm(ip,ist) = .FALSE. + endif + endif + + enddo + + if (iclose > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (clonsum) + clon_fguess = (clonsum + (360.*float(lt15_ct)))/ iclose + else + clon_fguess = clonsum / float(iclose) + endif + if (clon_fguess >= 360.0) then + clon_fguess = clon_fguess - 360. + endif + clat_fguess = clatsum / float(iclose) + endif + +c Print out a table listing of the locations of the fixes for +c the individual parameters. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'--------------------------------------------------' + write (6,95) 'Individual fixes follow..., fhr= ',ifhours(ifh) + & ,ifclockmins(ifh),' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + write (6,97) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,'Model name = ',atcfname + print *,'Values of -99.99 indicate that a fix was unable to be' + print *,'made for that paramater. Parameters 4 & 6 are not' + print *,'used. Vorticity data values are scaled by 1e5.' + print *,'Circulation data values are scaled by 1e-6.' + print *,'errdist is the distance that the position estimate is' + print *,'from the guess position for this time. MSLP value ' + print *,'here may differ from that in the atcfunix file since ' + print *,'the one here is that derived from the area-averaged ' + print *,'barnes analysis, while that in the atcfunix file is ' + print *,'from a specific gridpoint.' + write (6,21) geslon,360.-geslon,geslat + write (6,*) ' ' + write (6,23) + write (6,25) + endif + + if (geslat > 0.0) then + charmaxmin(1) = ' Max ' + charmaxmin(2) = ' Max ' + charmaxmin(3) = ' Max ' + charmaxmin(5) = ' Max ' + charmaxmin(10) = ' Max ' + charmaxmin(11) = ' Max ' + else + charmaxmin(1) = ' Min ' + charmaxmin(2) = ' Min ' + charmaxmin(3) = ' Min ' + charmaxmin(5) = ' Min ' + charmaxmin(10) = ' Min ' + charmaxmin(11) = ' Min ' + endif + + do ip=1,maxtp + if (ip == 1 .or. ip == 2 .or. ip == 11) then + ! This IF block allows vorticity values to be + ! written out and scaled up by 1e5 ... + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + elseif (ip == 3 .or. ip == 5 .or. ip == 10) then + ! This IF block allows circulation values to be + ! written out and scaled down by 1e-6 ... + + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + else + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + endif + enddo + + 21 format (' Guess location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 23 format (' parm# parm Max/Min Lon_fix(E) Lon_fix(W)' + & ,' Lat_fix Max/Min_value calcparm errdist(km)') + 25 format (' ----- ---- ------- ---------- ----------' + & ,' ------- ------------- -------- ----------') + 27 format (2x,i2,4x,a8,2x,a8,3x,f7.2,5x,f7.2,4x,f7.2,7x,f9.2 + & ,6x,L2,7x,f7.2) + 95 format (1x,a33,1x,i4,':',i2.2,a2,a4,a1,a9) + 97 format (' Gen ID (if available): ',i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3) + + +c If number of parameter centers close enough (iclose) > 0, then +c calculate the center by taking an average of all the parameter +c center positions that are within distance errmax from the guess +c position (geslon,geslat). Get a first-guess mean position, and +c then re-calculate the position estimate by giving more weight +c to those positions that are closer to the first-guess mean +c position. + + dist_from_mean = 0.0 + + if (iclose > 0) then + +c Get distances from first-guess mean position.... + + do ip=1,maxtp + if (calcparm(ip,ist)) then + call calcdist (clon_fguess,clat_fguess,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + dist_from_mean(ip) = dist + endif + enddo + +c Get the mean distance of each parameter estimate from +c the first-guess mean position + + call avgcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,iaret) + + if (iaret == 0) then + + call stdevcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,stderr_close,isret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After stdevcalc, xmn_dist_from_mean= ' + & ,xmn_dist_from_mean,' stderr_close= ' + & ,stderr_close,' isret= ',isret + endif + + endif + if (iaret /= 0 .or. isret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER -- Error occurred in either' + print *,'!!! avgcalc or stdevcalc. Storm number = ',ist + print *,'!!! RCC from avgcalc = ',iaret + print *,'!!! RCC from stdevcalc = ',isret + print *,'!!! Center fix will NOT be made, and processing' + print *,'!!! for this storm is ending. The probable cause' + print *,'!!! is that no calcparms were valid for this storm' + print *,'!!! at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + if (calcparm(1,ist) .or. calcparm(2,ist) .or. calcparm(7,ist) + & .or. calcparm(8,ist) .or. calcparm(9,ist) + & .or. calcparm(11,ist) .or. calcparm(3,ist) + & .or. calcparm(10,ist) .or. calcparm(5,ist) + & .or. calcparm(12,ist) .or. calcparm(13,ist) + & .or. calcparm(14,ist)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In fixcenter, STOPPING PROCESSING for this' + print *,'!!! storm. The reason is that none of the fix' + print *,'!!! locations for parms z850, z700, zeta 850,' + print *,'!!! zeta 700, MSLP, wcirc_850, wcirc_700, ' + print *,'!!! wcirc_sfc, sfc zeta or the various levels ' + print *,'!!! of thicknesses were within a ' + print *,'!!! reasonable distance of the guess location.' + print *,'!!! ist= ',ist,' ifh= ',ifh + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'!!! Forecast hour: ',i4,':',i2.2) + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now re-calculate the mean position by giving more weight +c to those position estimates that are closer to the first +c guess mean position. Note that if stderr_close < 5.0, we +c force it to be 5.0; we do this to avoid getting very +c large numbers for devia values, which could make the +c weights (wtpos) equal to 0. This occurred during testing +c when only 2 parameters were valid, and so, of course, the +c standard deviation from the mean of those 2 parameters +c was close to 0, which gave devia values around 6000, and +c then wtpos values of 0, leading to a divide by 0 crash +c later on in subroutine wtavrg. + + kprm=0 + + if (stderr_close > 0.0) then + if (stderr_close < 5.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Since stderr_close had a value less than' + print *,'5, stderr_close has been forced to be equal' + print *,'to 5 in order to avoid dividing by zero later' + print *,'on in subroutine wtavrg.' + endif + + stderr_close = 5.0 + endif + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + devia(kprm) = dist_from_mean(ip) / stderr_close + wtpos(kprm) = exp(-devia(kprm)/3.) + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + + if ( verb .ge. 3 ) then + write (6,113) ip,kprm,dist_from_mean(ip),devia(kprm) + & ,wtpos(kprm),temp_clon(kprm) + & ,360.-temp_clon(kprm),temp_clat(kprm) + endif + + endif + enddo + 113 format (1x,'ip= ',i2,' kprm= ',i2,' dist_from_mean= ',f7.3 + & ,' devia= ',f7.3,' wtpos= ',f8.5,2x,3(2x,f7.2)) + else +c +c This next if statement is for the case in which only 1 +c parameter is valid, for which the stderr_close will = 0 +c (obviously), but as long as we have 1 valid parameter, +c continue processing, and set the weight for that parm = 1. +c The else portion is for the case in which stderr_close +c = 0 with NO parms being close. +c + if (iclose == 1) then + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + wtpos(kprm) = 1 + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + endif + enddo + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, stderr_close not > 0' + print *,'!!! stderr_close = ',stderr_close + print *,'!!! The probable cause is that no calcparms were' + print *,'!!! valid for this storm at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + endif +c + if (kprm > 0) then + call wtavrg_lon (temp_clon,wtpos,kprm,fixlon(ist,ifh),iwtret1) + call wtavrg (temp_clat,wtpos,kprm,fixlat(ist,ifh),iwtret2) + if (iwtret1 == 0 .and. iwtret2 == 0) then + if (verb .ge. 3) then + print *,' ' + write (6,173) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + 173 format ('At end of fixcenter: ',a4,' fhr= ',i4,':',i2.2 + & ,' Fix position= ',f7.2,'E (',f6.2,'W)',2x,f7.2) + print *,' ' + endif + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER in call to wtavrg.' + print *,'!!! Return Codes from wtavrg calls follow: ' + print *,'!!! RCC from wtavrg for long fix: ',iwtret1 + print *,'!!! RCC from wtavrg for lat fix: ',iwtret2 + print *,'!!! This means a divide by zero would have ' + print *,'!!! been attempted, which means that the ' + print *,'!!! weights in wtpos are not > 0. Check in' + print *,'!!! subroutine fixcenter where devia values' + print *,'!!! are calculated to see if something is ' + print *,'!!! wrong there. Values of wtpos array follow:' + print *,'!!! ',wtpos + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + print *,' ' + endif + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, kprm NOT > 0' + print *,'!!! This means that, for whatever reason, the ' + print *,'!!! calcparm logical flag was set to .FALSE. for' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: IN FIXCENTER, No storms are within errmax ' + print *,'!!! OR the calcparm logical flag was set to .FALSE. ' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now calculate the average error of all the parms that are within +c a radius errpmax (defined in error_parms, ~600km), and the std +c dev of those errors. This standard deviation will be used in +c calculating the maximum allowable error for the next forecast +c time. + + if (itot4next > 0 .and. ifret /= 95) then + trkerr_avg = trkerr_avg / float(itot4next) + call stdevcalc (errdist,maxtp,use4next,trkerr_avg + & ,stderr(ist,ifh),isret) + if (isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in FIXCENTER calculating std deviation ' + print *,'!!! for use in next forecast hours errmax.' + print *,'!!! ist= ',ist,' ifh= ',ifh,' itot4next= ' + & ,itot4next + endif + + ifret = 95 + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine avgcalc (xdat,kmax,valid,xavg,iaret) +c +c ABSTRACT: This subroutine just calculates a straight average of +c the parameters in the input array (xdat). The logical array +c (valid) indicates whether or not to include a particular array +c member or not in the calculation. + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) +c + iaret = 0 +c + xsum = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + xsum = xsum + xdat(i) + ict = ict + 1 + endif + enddo +c + if (ict > 0) then + xavg = xsum / float(ict) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in avgcalc, ict NOT > 0' + endif + + xavg = xdat(1) + iaret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg (xdat,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xdat) using the input weights +c in the input array (wt). It is used to calculate the center lat +c and lon fix positions. +c + USE verbose_output + + real xdat(kmax),wt(kmax) +c + iwtret = 0 +c + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xdat(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg, wtot NOT > 0' + endif + + iwtret = 95 + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg_lon (xlon,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xlon) using the input weights +c in the input array (wt). This subroutine is specifically used +c to find the center lon fix positions. It contains code to +c account for wrapping around the Greenwich Meridian. +c + + USE verbose_output + + real xlon(kmax),wt(kmax) + integer gt345_ct,lt15_ct +c + iwtret = 0 + gt345_ct = 0 + lt15_ct = 0 + +c First check to see if we have lons that are both to the left +c and the right of the greenwich meridian + + do i = 1,kmax + if (xlon(i) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (xlon(i) < 15.) then + lt15_ct = lt15_ct + 1 + endif + enddo + + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some lons that are in the 300's (west of the GM), and + ! some that are in the 0's (east of the GM). We need to + ! standardize these if we want to get a meaningful average. + do i = 1,kmax + if (xlon(i) < 15.) then + xlon(i) = xlon(i) + 360.0 + endif + enddo + endif + + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xlon(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg_lon, wtot NOT > 0' + endif + + iwtret = 95 + endif + + if (xwtavg >= 360.0) then + xwtavg = xwtavg - 360.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine stdevcalc (xdat,kmax,valid,xavg,stdx,isret) + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) + + isret = 0 + + stdx = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + stdx = stdx + (xdat(i) - xavg)**2 + ict = ict + 1 + endif + enddo + + if (ict > 0) then + stdx = sqrt(stdx/float(ict)) + if (stdx == 0.0) then +c This can happen if you have just 2 points; The mean position +c will be exactly in the middle of the 2 points and so the +c standard deviation around that mean point will be 0. And +c since the calling routine will quit if the returned standard +c deviation is 0, we must force it to be 1 so the program +c continues running. Theoretically, it could also happen with +c 3 or more points, but the likelihood of the distances working +c out to exactly equidistant for 3 points is not that good. + stdx = 1.0 + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in stdevcalc, ict NOT > 0' + endif + + isret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,fxval,trkrinfo + & ,cmodel_type,maxmin,igwcret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the wind circulation near the storm center. This center fix is +c done differently than for the other parms. With this fix, +c we limit the area that is searched. This subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the +c original guess position for this lead time and the 5 other parm +c fixes that have already been made for this lead time. That +c modified guess position is passed into this subroutine as uvgeslon +c and uvgeslat, and that's where the searching for the wind +c circulation is centered. +c +c This subroutine works by converting the winds to Vt and Vr at +c each grid point evaluated, relative to each candidate center point +c that is being evaluated at the time in the loop. We then compute +c the circulation at each of 24 azimuths surrounding the storm +c center, where circulation = Vt * (length of a 1/24 arc, in meters) +c This process is repeated for 7 successive radii and the results +c are summed up over all radii, approximating a solid disk +c circulation. The point at which the circulation is maximized +c (NHEM) or minimized (SHEM) is the center of circulation. +c +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c + + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + character(*) cmodel_type,maxmin + integer, parameter :: numdist=7,numazim=24 + integer imax,jmax,ist,level,igwcret,icvpret,idist,iazim + real rdist(numdist),vr(numazim,numdist),vt(numazim,numdist) + real vt_mean(numdist),circul_band(numdist) + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + real rads,ri,uvgeslon,uvgeslat,dx,dy,ctlon,ctlat,fxval + real temp_grid_minlon,temp_guesslon,rlatt,rlont,bear + real targlon,targlat,xintrp_u,xintrp_v,vt_azim_sum,degrees + real circ_diff,circ_diff_sum,hemisphere,wind_mag_ctr,dist + real xmin_circ_diff_mean,xmax_circ_diff_mean,tlon,tlat + real dell,fmax,fmin,grid_buffer,circ_diff_mean + real circumference,arclength + real circul_disk,xmax_circul_disk,xmin_circul_disk + integer ibiret1,ibiret2,igvtret,azimuth_ct,igiret,npts + integer igibret + integer circ_diff_ct,ir,nhalf,bskip1,bskip2,iskip,nlev + integer ilonfix,jlatfix,ibeg,iend,jbeg,jend,i,j,k,iix,jix + logical(1) cflag, valid_pt(imax,jmax) + +c---------------- +c + + print *,' ' + print *,'top of get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' cmodel_type= ',cmodel_type + print *,' maxmin= ',maxmin + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' cflag= ',cflag + print *,' ctlon= ',ctlon,' ctlat= ',ctlat + print *,' fxval= ',fxval + print *,' igwcret= ',igwcret + + igwcret = 0 + + grid_maxlat = glatmax + grid_minlat = glatmin + grid_maxlon = glonmax + grid_minlon = glonmin + + rads = rads_wind_circ + ri = ri_wind_circ + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of get_wind_circulation, rads= ',rads + & ,' ri= ',ri,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+15; fmin = 1.0e+15 + ctlon = 0.0; ctlat = 0.0 + +c Distances checked and the radial intervals are a function of +c the grid resolution.... + + if (dell > 0.50) then + rdist(1) = 50. + rdist(2) = 85. + rdist(3) = 120. + rdist(4) = 155. + rdist(5) = 190. + rdist(6) = 225. + rdist(7) = 260. + else + rdist(1) = 35. + rdist(2) = 65. + rdist(3) = 95. + rdist(4) = 125. + rdist(5) = 155. + rdist(6) = 185. + rdist(7) = 215. + endif + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + print *,' in get_wind_circulation, nlev= ',nlev + + if (uvgeslat >= 0.0) then + hemisphere = 1.0 + else + hemisphere = -1.0 + endif + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend + & ,igibret) + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (uvgeslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = uvgeslon - 360. + else + temp_guesslon = uvgeslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = uvgeslon + endif + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + +c For the wind circulation analysis, we will want to speed things +c up for finer resolution grids. We can do this by skipping some +c of the points in the wind circulation analysis. + + if (dell > 0.20) then + bskip1 = 1 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 3 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 5 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 8 + bskip2 = 3 + else if (dell <= 0.03) then + bskip1 = 10 + bskip2 = 4 + endif + +c bskip1 = 1 +c bskip2 = 1 + + jix = 0 + +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to first loop, ' + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop1: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = uvgeslat + dell*float(j) + + iix = 0 + + iloop1: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop1 + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT from call in ' + print *,'!!! get_wind_circulation: icvpret= ',icvpret + endif + cycle iloop1 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist,degrees) + if (dist .gt. rads) cycle iloop1 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop1: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop1: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + ! These calls to bilin_int_uneven pass a variable "level" + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop1 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop1 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop1 +cc and radiusloop1). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,' ' +cc print *,'1st run, wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'1st run, ir= ',ir,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +c print *,'1st run, circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'1st run, circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'1st run, xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif + + enddo iloop1 + + enddo jloop1 + + if (uvgeslat >= 0.0) then + write (6,61) 360.-ctlon,ctlat,xmax_circul_disk + else + write (6,63) 360.-ctlon,ctlat,xmin_circul_disk + endif + + 61 format (' After first run, Wind Circulation (NHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmax_circul_disk = ',f15.1) + 63 format (' After first run, Wind Circulation (SHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmin_circul_disk = ',f15.1) + +c If nhalf is specified as 0, then don't go through any more +c iterations of this routine, just exit with the value that we +c already got the first time through the loop, above. + + if (dell > 0.50) then + nhalf = 4 + else if (dell > 0.20 .and. dell <= 0.50) then + nhalf = 3 + else if (dell > 0.10 .and. dell <= 0.20) then + nhalf = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + nhalf = 1 + else if (dell <= 0.05) then +c nhalf = 0 + nhalf = 1 +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'In get_wind_circulation, dell is < 0.05 deg, so ' +c print *,'nhalf is set to 0 and only the first iteration of' +c print *,'the search loop is done.' +c print *,' dell= ',dell,' nhalf= ',nhalf +c endif + endif + + if (nhalf < 1) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +c npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only do this once +c for this grid-refinement (even though the grid is redefined +c nhalf times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). Cut the value of +c rads in half (only do this once) so that any points beyond +c rads/2 are not considered as potential centers. + + rads = 0.5 * rads + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igibret) + + if (igibret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_wind_circulation from call to ' + print *,'!!! get_ij_bounds just before nhalf loop. ' + print *,'!!! Stopping processing for storm number ',ist + endif + igwcret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + kloop: do k = 1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: get_wind_circ kloop, k= ',i2,' ' + & ,i2.2,':',i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'get_wind_circ nhalf loop, k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip,' rads= ',rads + endif + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to loop k= ',k + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist + & ,degrees) + if (dist .gt. rads) cycle iloop2 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop2: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop2: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop2 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop2 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop2 +cc and radiusloop2). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,'kloop k= ',k,' wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'kloop k= ',k,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +cc print *,'kloop k= ',k,' circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'kloop k=',k,' circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0.0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'kloop k= ',k,' xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean + + enddo iloop2 + + enddo jloop2 + + if ( verb .ge. 3 ) then + if (uvgeslat >= 0.0) then + print *,'---> xmax_circul_disk= ',xmax_circul_disk + write (6,71) k,360.-ctlon,ctlat,xmax_circul_disk + else + print *,'---> xmin_circul_disk= ',xmin_circul_disk + write (6,73) k,360.-ctlon,ctlat,xmin_circul_disk + endif + endif + + enddo kloop + + 71 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (NHEM: Max) = ' + & ,f15.1) + 73 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (SHEM: Min) = ' + & ,f15.1) + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,xval,trkrinfo,igucret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the minimum in the wind speed near the storm center. This center +c fix is done differently than for the other parms. With this fix, +c we severely limit the area that is searched, because we do not +c want to confuse a wind minimum out on the periphery of a storm +c with the center wind minimum. Therefore, this subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the guess +c position for this time and the 5 other parm fixes. That modified +c guess position is passed into this subroutine as uvgeslon and +c uvgeslat, and that's where the searching for the wind minimum +c is done. To get the wind minimum, the u and v data are first +c interpolated down to a fine grid (see details below for exact +c figures), and then a single-pass barnes analysis is done on that +c fine grid. The reason that we first interpolate the data (which +c is different from how we do the other parms) is that if we just +c use the original grid resolution, we may not be able to +c accurately pick out a minimum in the wind field at the center. +c + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real, allocatable :: uold(:,:),vold(:,:),unew(:,:),vnew(:,:) + real, allocatable :: rlonold(:),rlatold(:),rlonnew(:),rlatnew(:) + real, allocatable :: vmag(:,:) + real :: dx,dy + real :: grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + character*1 :: gotlat + logical(1) cflag, valid_pt(imax,jmax) + logical(1), allocatable :: lbi(:,:) +c + gotlat = 'n' +c +c ----------------------------------------------------------------- +c INTERPOLATE INPUT GRID TO SMALLER GRID +c ----------------------------------------------------------------- +c +c Get beginning and ending j points (on the input grid) for a +c smaller array that surrounds the storm. It is this smaller array +c that we will interpolate to a finer grid. +c +c Calculate number of pts to either side of this j to search +c + npts = ceiling(rads_vmag/(dtk*((dx+dy)/2.))) +c + call get_ij_bounds (npts,0,ritrk_vmag,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij D, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij D, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center from call to ' + print *,'!!! get_ij_bounds, stopping processing for ' + print *,'!!! storm number ',ist + endif + + igucret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, and our gridtype is NOT' + print *,'!!! global, so we are going to redefine ibeg to 1.' + print *,' ' + endif + + ibeg = 1 + endif + endif + + if (jbeg < 1) jbeg = 1 + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center calculating ibeg, iend, jbeg' + print *,'or jend. ibeg= ',ibeg,' iend= ',iend,' jbeg= ',jbeg + print *,'jend= ',jend + print *,'uv center will not be calculated for this time.' + endif + + igrret = 99 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, and our gridtype is' + print *,'!!! NOT global, so we will redefine iend to imax.' + print *,' ' + endif + + iend = imax + endif + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif +c + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the grid sizes for +c some of the typical grids that will be used: +c +c Original grid size # of interps Final grid size +c -------------------- ------------ --------------------- +c 1.00 deg (111.19 km) 3 0.125 deg (13.9 km) +c 1.25 deg (138.99 km) 3 0.156 deg (17.4 km) +c 2.50 deg (277.99 km) 4 0.156 deg (17.4 km) + + if ((dx+dy)/2. > 1.2) then + numinterp = 4 + else if ((dx+dy)/2. > 0.50 .and. (dx+dy)/2. <= 1.2) then + numinterp = 3 + else if ((dx+dy)/2. > 0.25 .and. (dx+dy)/2. <= 0.50) then + numinterp = 2 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.25) then + numinterp = 1 + else if ((dx+dy)/2. <= 0.10) then + numinterp = 0 + endif + + dell = (dx+dy)/2. + imxold = iend - ibeg + 1 + jmxold = jend - jbeg + 1 + +c -------------------------------------------------------------- +c Before interpolating, make sure that all the original +c points have valid data. If they don't then exit the +c subroutine. NOTE: This is NOT checking to see if ALL the pts +c on the complete & full input grid have valid data; it only +c checks those points that are within the box returned from +c get_ij_bounds. + + do i=ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_uv_center, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! PROCESSING WILL STOP. ' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + stop 94 + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_uv_center' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + do j=jbeg,jend + if (.not. valid_pt(ip,j)) goto 975 + enddo + + enddo + +c ------------------------------------ +c Now begin the interpolation process + + allocate (uold(imxold,jmxold),stat=iuo) + allocate (vold(imxold,jmxold),stat=ivo) + allocate (rlonold(imxold),stat=iloo) + allocate (rlatold(jmxold),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. iloo /= 0 .or. ilao /= 0) goto 970 + + do intnum = 1,numinterp + + if (intnum == 1) then + + do i=ibeg,iend + + ik = i + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ik = i + imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i < 1' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i + endif + + igucret = 92 + return + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ik = i - imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i > imax' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i,' imax= ',imax + endif + + igucret = 92 + return + endif + endif + + rlonold(i-ibeg+1) = glon(ik) + do j=jbeg,jend + uold(i-ibeg+1,j-jbeg+1) = u(ik,j,nlev) + vold(i-ibeg+1,j-jbeg+1) = v(ik,j,nlev) + if (gotlat == 'n') then + rlatold(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatold once + enddo + + else + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate (rlatold) + allocate (uold(imxnew,jmxnew),stat=iuo) + allocate (vold(imxnew,jmxnew),stat=ivo) + allocate (rlonold(imxnew),stat=iloo) + allocate (rlatold(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 970 + + gotlat = 'n' + do i=1,imxnew + rlonold(i) = rlonnew(i) + do j=1,jmxnew + uold(i,j) = unew(i,j) + vold(i,j) = vnew(i,j) + if (gotlat == 'n') then + rlatold(j) = rlatnew(j) + endif + enddo + gotlat = 'y' + enddo + + imxold = imxnew + jmxold = jmxnew + deallocate (unew); deallocate (vnew) + deallocate (rlonnew); deallocate (rlatnew) + + endif + + dell = 0.5 * dell + imxnew = 2 * imxold - 1 + jmxnew = 2 * jmxold - 1 + + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + + call bilin_int_even (imxold,jmxold,uold + & ,imxnew,jmxnew,unew,ibiret) + call bilin_int_even (imxold,jmxold,vold + & ,imxnew,jmxnew,vnew,ibiret) +c call lin_int (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int_lon (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int (jmxold,jmxnew,rlatold,rlatnew,iliret) + + chk_lonspc_old = rlonold(imxold) - rlonold(imxold - 1) + chk_latspc_old = rlatold(jmxold) - rlatold(jmxold - 1) + chk_lonspc_new = rlonnew(imxnew) - rlonnew(imxnew - 1) + chk_latspc_new = rlatnew(jmxnew) - rlatnew(jmxnew - 1) + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, intnum= ',intnum + print *,'imxold= ',imxold,' imxnew= ',imxnew + print *,'jmxold= ',jmxold,' jmxnew= ',jmxnew + print *,'Grid boundaries of modified uv grid: ' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ' + & ,grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ' + & ,grid_minlon + endif + + enddo + +c ------------------ + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate(rlatold) + + if (numinterp == 0) then + + ! No interpolations were done for this fine mesh grid, but we + ! need to fill some of these arrays and define variables for + ! subsequent subroutine calls just below here that require + ! the variables imxnew, jmxnew, and the arrays unew and vnew. + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional ' + print *,'grid; iend should not > imax here !!!' + endif + + igucret = 99 + return + endif + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional' + print *,'grid; ibeg should not < 1 here !!!' + endif + + igucret = 99 + return + endif + endif + + imxnew = iend - ibeg + 1 + jmxnew = jend - jbeg + 1 + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + gotlat = 'n' + + do i=ibeg,iend + + ip = i + + if (i > imax) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i - imax ! Wrapping past GM + endif + + if (i < 1) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i + imax ! Wrapping past GM + endif + + rlonnew(i-ibeg+1) = glon(ip) + do j=jbeg,jend + unew(i-ibeg+1,j-jbeg+1) = u(i,j,nlev) + vnew(i-ibeg+1,j-jbeg+1) = v(i,j,nlev) + if (gotlat == 'n') then + rlatnew(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatnew once + enddo + + endif + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,'Grid boundaries of modified uv grid in get_uv_center:' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ',grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ',grid_minlon + endif + + allocate (vmag(imxnew,jmxnew),stat=ivm) + allocate (lbi(imxnew,jmxnew),stat=ilb) + if (ivm /= 0 .or. ilb /= 0) goto 972 + call calc_vmag (unew,vnew,imxnew,jmxnew,vmag,icvret) + deallocate (unew); deallocate (vnew) + + lbi = .TRUE. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to find_maxmin, imxnew= ',imxnew + & ,'jmxnew= ',jmxnew,' ist= ',ist + write (6,171) dell,uvgeslon,360.-uvgeslon,uvgeslat + 171 format (' dell= ',f7.3,' uvgeslon= ',f8.3,'E (',f8.3,'W)' + & ,' uvgeslat= ',f8.3) + endif + +c Note that in the next call, I pass the 'global' argument to +c find_maxmin. This defines what type of grid it is, so that the +c proper grid_buffer can be chosen. This grid_buffer is designed +c to avoid having a center be chosen too close to the grid +c boundary. However, in the case of vmag here, we are only using +c a small subgrid, and we want to make sure we use *all* points +c in that subgrid for searching, and that will occur if we set that +c calling argument to 'global' as opposed to 'regional'. + + call find_maxmin (imxnew,jmxnew,dell,dell,'vmag' + & ,vmag,'min',ist,uvgeslon,uvgeslat,rlonnew,rlatnew,lbi + & ,trkrinfo,cflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,'global',ifmret) + deallocate (vmag); deallocate (lbi) + deallocate (rlonnew); deallocate (rlatnew) +c + if (ifmret == 0) then + goto 995 + else + igucret = ifmret + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center in call to find_maxmin' + print *,'!!! storm num = ',ist,' igucret = ',igucret + endif + + goto 998 + endif +c + 970 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either uold, vold,' + print *,'!!! rlonold or rlatold in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 971 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either unew, vnew,' + print *,'!!! rlonnew or rlatnew in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 972 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either vmag or lbi in ' + print *,'!!! subroutine get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! ivm= ',ivm,' ilb= ',ilb + endif + + igucret = 97 + goto 998 + + 975 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Inside get_uv_center, at least one of the points' + print *,'!!! is not a valid data point. This point may be ' + print *,'!!! outside the valid data bounds of a regional grid' + print *,'!!! i= ',i,' j= ',j + print *,'!!! Storm number = ',ist + endif + + igucret = 98 + goto 998 +c + 995 continue + igucret = 0 +c + 998 continue + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_guess (guesslon,guesslat,clon,clat + & ,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) +c +c ABSTRACT: The purpose of this subroutine is to get a modified +c first guess lat/lon position before searching for the +c minimum in the wind field. The reason for doing this is +c to better refine the guess and avoid picking up a wind +c wind minimum far away from the center. So, use the +c first guess position (and give it strong weighting), and +c then also use the fix positions for the current time +c (give the vorticity centers stronger weighting as well), +c and then take the average of these positions. +c +c INPUT: +c guesslon guess longitude for this forecast time +c guesslat guess latitude for this forecast time +c clon array with center longitude fixes for the various parms +c clat array with center latitude fixes for the various parms +c calcparm logical; tells whether or not a parm has a valid fix +c at this forecast hour +c ist index for current storm +c ifh index for current forecast hour +c maxstorm max # of storms that can be handled +c +c OUTPUT: +c uvgeslon contains modified guess longitude position at which to +c look for the wind minimum +c uvgeslat contains modified guess latitude position at which to +c look for the wind minimum +c igugret return code for this subroutine (0=normal) +c---- +c + USE set_max_parms; USE level_parms; USE error_parms + USE verbose_output + + logical(1) calcparm(maxtp,maxstorm) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real uvgeslon, uvgeslat + real guesslon,guesslat,degrees + integer gt345_ct,lt15_ct + + sumlon = 0.0 + sumlat = 0.0 + ict = 0 + gt345_ct = 0 + lt15_ct = 0 + +c NOTE: We need to be careful in this routine when averaging +c the longitudes together, in case we cross the greenwich +c meridian, because then we may be averaging 345+ lons with +c lons that are less than 15, giving incorrect results. +c Therefore, check for this, and if it occurs, add 360 onto +c any of the <15 lons (add it twice for those lons being +c counted twice (guesslon and the vorticity centers)). + +c Weight the uv guess position by counting the storm's guess +c position twice. + + sumlon = sumlon + 2.*guesslon + sumlat = sumlat + 2.*guesslat + ict = ict + 2 + + if (guesslon > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (guesslon < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct.... + endif + + do ip = 1,maxtp + if ((ip > 2 .and. ip < 7) .or. ip == 10) then + cycle ! because 3-6 are for 850 & 700 u & v and 10 is + ! for surface wind magnitude. + else + if (calcparm(ip,ist)) then + call calcdist (guesslon,guesslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + + if (dist < uverrmax) then +c +c Give the vorticity centers 2x weighting as well +c + if (ip == 1 .or. ip == 2 .or. ip == 11) then + sumlon = sumlon + 2.*clon(ist,ifh,ip) + sumlat = sumlat + 2.*clat(ist,ifh,ip) + ict = ict + 2 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct... + endif + else + sumlon = sumlon + clon(ist,ifh,ip) + sumlat = sumlat + clat(ist,ifh,ip) + ict = ict + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 ! Only 1 for non-zeta parms + endif + endif + + endif + + endif + endif + enddo +c + if (ict > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (sumlon) + uvgeslon = (sumlon + (360.*float(lt15_ct)))/ ict + else + uvgeslon = sumlon / ict + endif + if (uvgeslon >= 360.0) then + uvgeslon = uvgeslon - 360. + endif + uvgeslat = sumlat / ict + igugret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_guess, ict not > 0, ict= ',ict + print *,'!!! vmag center will not be calculated for this' + print *,'!!! storm -- at least not at this level' + print *,'!!! Storm number = ',ist + endif + + igugret = 91 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calc_vmag (xu,xv,imx,jmx,wspeed,icvret) +c +c ABSTRACT: This subroutine calculates the magnitude of the wind +c speed for an array of points, given real u and real v arrays. +c + real xu(imx,jmx),xv(imx,jmx),wspeed(imx,jmx) +c + do i=1,imx + do j=1,jmx + wspeed(i,j) = sqrt( xu(i,j)*xu(i,j) + xv(i,j)*xv(i,j) ) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_even (imxold,jmxold,xold + & ,imxnew,jmxnew,xnew,ibiret) +c +c ABSTRACT: This subroutine does a bilinear interpolation on a +c grid of evenly spaced data. Do NOT attempt to use this subroutine +c with data that are not evenly spaced or you will get unpredictable +c results. +c + real xold(imxold,jmxold), xnew(imxnew,jmxnew) +c +c +c --------------------------------------------------------------------- +c Latitude ----> | +c | +c L O e O e O e O e O | O: original point from input array +c o | +c n e 1 2 1 2 1 2 1 e | 1: interpolated, primary inter. pt +c g | +c i O 2 O 2 O 2 O 2 O | e: interpolated edge point +c t | +c u e 1 2 1 2 1 2 1 e | 2: interpolated, secondary inter. pt +c d | +c e O 2 O 2 O 2 O 2 O | Interpolations are done in the order +c | as indicated above; First, the input +c | e 1 2 1 2 1 2 1 e | 'O' pts are placed onto the new, +c | | larger grid. From that, the '1' pts +c | O 2 O 2 O 2 O 2 O | can be interpolated. Next, the edge +c | | (e) pts are interpolated using an +c v e 1 2 1 2 1 2 1 e | interpolation of two 'O' pts and one +c | '1' pt. Finally, the '2' pts are +c O e O e O e O e O | done using the 2 surrounding '0' and +c | '1' pts. Bilinear interpolation is +c | made incredibly easier by the fact +c | that the grid is evenly spaced. +c --------------------------------------------------------------------- +c NOTE: Remember that the arrays that are read in are indexed as +c (lon,lat), so that in the diagram above, pt (1,1) is at the upper +c left and pt (imax,jmax) is at the lower right, and each column is +c a new latitude and each row is a new longitude. +c +c ----------------------------------------------------------------- +c Put original (O) values from input array into new, expanded array +c ----------------------------------------------------------------- +c + do i=1,imxold + do j=1,jmxold + xnew(2*i-1,2*j-1) = xold(i,j) + enddo + enddo +c +c ---------------------------------------------- +c Interpolate to get primary interior (1) points +c ---------------------------------------------- +c + do i=1,imxold-1 + do j=1,jmxold-1 + xnew(2*i,2*j) = 0.25 * (xnew(2*i-1,2*j-1) + xnew(2*i+1,2*j-1) + & + xnew(2*i+1,2*j+1) + xnew(2*i-1,2*j+1)) + enddo + enddo +c +c --------------------------- +c Interpolate edge (e) points +c --------------------------- +c +c ... Northernmost 'e' points ... +c + j=1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,2)) + enddo +c +c ... Southernmost 'e' points ... +c + j = 2*jmxold - 1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,j-1)) + enddo +c +c ... Westernmost 'e' points ... +c + i=1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(2,2*j)) + enddo +c +c ... Easternmost 'e' points ... +c + i = 2*imxold - 1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(i-1,2*j)) + enddo +c +c ------------------------------------------------ +c Interpolate to get secondary interior (2) points +c ------------------------------------------------ +c + do j=2,2*jmxold-2 + istep = mod(j+1,2) + do i=istep+2,2*imxold-2,2 + xnew(i,j) = 0.25 * (xnew(i-1,j) + xnew(i,j-1) + xnew(i+1,j) + & + xnew(i,j+1)) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points +c + do i=1,ioldmax-1 + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int_lon (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. This particular +c routine is specifically used for interpolating +c longitudes, and it factors in the possibility of +c interpolating across the greenwich meridian. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points, and make the +c necessary adjustment when interpolating a longitude between, +c for example, 359.5 and 0.0. +c + do i=1,ioldmax-1 + if (xnew(2*i-1) > 350. .and. xnew(2*i+1) < 10.) then + xnew(2*i) = 0.5 * (xnew(2*i-1) + (360. + xnew(2*i+1))) + else + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + endif + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) +c +c ABSTRACT: This subroutine finds the maximum and mean zeta values +c at 850 & 700 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms + USE trkrparms; USE level_parms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07 ------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanzeta,dx,dy,re,ri,parmlon,parmlat + integer igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) + integer n,ix1,ix2,ilev,npts,imax,jmax,igzvret,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_zeta_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_zeta_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max zeta values at 850 and 700 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_zeta_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_zeta_loop: do n=1,2 + + gridpoint_maxmin = -99.0 + xmeanzeta = -99.0 + compflag = .true. + + select case (n) + case (1); ilev=850 ! For 850 mb + case (2); ilev=700 ! For 700 mb + end select + + if (zeta(ilonfix,jlatfix,n) > -9990.0) then + + ! ------------------------------------------- + ! We have valid zeta data for this level, so + ! we first call barnes now to get the mean zeta + ! surrounding our found center position. + ! ------------------------------------------- + + if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,n),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanzeta + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds + imeanzeta(n) = int ((xmeanzeta * 1e6) + 0.5) + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_zeta_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for zeta values will not be done.') + exit report_zeta_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + exit report_zeta_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) n,ilev,xmeanzeta,imeanzeta(n) + 621 format (1x,'+++ RPT_MEAN_ZETA: n= ',i2,' lev= ',i4 + & ,' xmeanzeta= ',f9.6,' imeanzeta (*1e6)= ',i8) + write (6,*) ' --- mean zeta raw = ',xmeanzeta + endif + + ! ----------------------------------------------- + ! Call fix_latlon_to_ij to get the nearest actual + ! raw (grid) zeta data value, not the mean value. + ! ----------------------------------------------- + + call fix_latlon_to_ij (imax,jmax,dx,dy + & ,zeta(1,1,n),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanzeta,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + igridzeta(n) = int ((gridpoint_maxmin * 1e6) + 0.5) + else + igridzeta(n) = -99 + endif + + if ( verb .ge. 3 ) then + write (6,623) n,ilev,gridpoint_maxmin,igridzeta(n),ifilret + 623 format (1x,'+++ RPT_GRID_ZETA: n= ',i2,' lev= ',i4 + & ,' grid zeta= ',f9.6,' igrid zeta (*1e6)= ',i8 + & ,' ifilret= ',i3) + write (6,*) ' --- grid zeta raw= ',gridpoint_maxmin + endif + + enddo report_zeta_loop + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get 850 & 700 zeta for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine find_maxmin (imax,jmax,dx,dy,cparm,fxy,maxmin,ist + & ,guesslon,guesslat,rlonv,rlatv,valid_pt,trkrinfo + & ,compflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,cmodel_type,ifmret) +c +c This routine finds the location (clon,clat) of and value of the +c the max or min of fxy in the vicinity of slon,slat. The value of +c the input flag maxmin determines whether to look for a max or a +c min value. The max/min is determined by finding the point which +c gives the max/min value of a single point barnes analysis of fxy +c with e-folding radius re (km) and influence radius ri (km). The +c initial search is restricted to a radius rads around the point +c (slon,slat) on a grid with lon,lat spacing dx and dy. The location +c is refined by reducing the spacing of the search grid by a factor +c of two, nhalf times. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c maxmin Char string indicating whether to search for a max or min +c ist Number of the storm being processed +c guesslon Guess longitude of the storm +c guesslat Guess latitude of the storm +c rlonv Array containing longitude values of input grid points +c rlatv Array containing latitude values of input grid points +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c trkrinfo derived type detailing user-specified grid info +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c +c INPUT/OUTPUT: +c compflag Logical; continue processing this storm or not (would be +c set to FALSE if, for example, the guess position is +c outside the domain of a regional grid) +c +c OUTPUT: +c ctlon Center longitude of storm found for this parameter +c ctlat Center latitude of storm found for this parameter +c xval Max or Min value found at the (ctlon,ctlat) +c ifmret Return code from this subroutine +c +c UPDATE DEC 2009: For the HFIP HRH testing, it was found that +c due to the very limited domain size of some of the models, the +c barnes scheme was allowing points close to the grid boundaries +c to erroneously be selected as the center point. We add in a +c buffer (grid_buffer) here to prevent this from occurring. + + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + character(*) maxmin,cparm,cmodel_type + logical(1) compflag, valid_pt(imax,jmax) + real fxy(imax,jmax),rlonv(imax),rlatv(jmax) + real ctlon,ctlat,degrees,dx,dy,guesslon,guesslat,xval + real rads,re,ri,dell,fmax,fmin,rlatt,rlont,dist,ftemp,ritmp + real vmag_latmax,vmag_latmin,vmag_lonmax,vmag_lonmin,retmp + real tlon,tlat,grid_buffer,temp_grid_minlon,temp_guesslon + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + integer imax,jmax,ist,bskip1,bskip2,iskip,ifmret,npts,maxvgrid + integer ibeg,iend,jbeg,jend,ilonfix,jlatfix,igiret,icount,iret + integer ibct,ibarnes_loopct,i,j,k,iix,jix,jvlatfix,ivlonfix + integer nhalf,icvpret + integer date_time(8) + character (len=10) big_ben(3) +c + ifmret = 0 + nhalf = 5 +c +c ----------------------------------------------------------- +c Set initial parms for use in find_maxmin. +c Different radii used for V magnitude than for other parms, +c see discussion in module radii for more details. +c + if (cparm == 'vmag') then + +c NOTE: The maxvgrid variable determines what size grid to send +c to subroutine barnes. e.g., maxvgrid = 8 means send an +c 8x8 grid; maxvgrid = 12 means send a 12x12 grid. For +c ultra-fine mesh grids (finer than 0.04 deg, or 1/25 deg), +c we expand to 12 in order to sample a few more points +c around each grid point. + + if ((dx+dy)/2. > 0.04) then + maxvgrid = 8 + else + maxvgrid = 12 + endif + + rads = rads_vmag; re = retrk_vmag; ri = ritrk_vmag + re = (float(maxvgrid)/4) * ((dx+dy)/2. * dtk) ! Basically, this +c sets re equal to half the distance from the gridpoint +c in question to the farthest point that will be +c sampled when the (maxvgrid x maxvgrid) grid is passed +c on to subroutine barnes. Thus, just ignore the +c parameter retrk_vmag, and use this instead. + else if ((dx+dy)/2. < 1.26 .and. (dx+dy)/2. >= 0.40) then + rads = rads_most; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.40 .and. (dx+dy)/2. >= 0.10) then + rads = rads_fine; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.10) then + rads = rads_hres; re = retrk_hres; ri = ritrk_most + else + rads = rads_coarse; re = retrk_coarse; ri = ritrk_coarse + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of find_maxmin, rads= ',rads,' re= ',re + & ,' ri= ',ri,' cparm= ',cparm,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+12; fmin = 1.0e+12 + ctlon = 0.0; ctlat = 0.0 + + if (npts == 0) npts = 1 + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if (dell > 0.20) then + bskip1 = 2 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 4 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 6 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 10 + bskip2 = 5 + else if (dell <= 0.03) then + bskip1 = 15 + bskip2 = 5 + endif + + if (cparm == 'vmag') then + bskip1 = 1 + bskip2 = 1 + endif + +c If input parm is vmag, we've already done the minimizing by +c interpolating to the fine mesh grid, so we'll simply send the +c bounds that were input to this subroutine to barnes +c as boundaries for the array to search. For all other parms, +c however, no minimizing has been done yet, so we need to call +c get_ij_bounds to set the boundaries for a much smaller grid that +c surrounds the storm (as opposed to having subroutine barnes +c search the entire global grid). + + if (cparm == 'vmag') then + + if ( verb .ge. 3 ) then + print *,'In find_maxmin, jmax= ',jmax,' imax= ',imax + endif + + ibeg=1; jbeg=1; iend=imax; jend=jmax + vmag_latmax = rlatv(1) ! N-most lat of vmag subgrid + vmag_latmin = rlatv(jmax) ! S-most lat of vmag subgrid + vmag_lonmin = rlonv(1) ! W-most lon of vmag subgrid + vmag_lonmax = rlonv(imax) ! E-most lon of vmag subgrid + + if ( verb .ge. 3 ) then + write (6,44) vmag_latmax,vmag_lonmin,360.-vmag_lonmin + & ,imax,jmax + write (6,46) vmag_latmin,vmag_lonmax,360.-vmag_lonmax + endif + + 44 format (' vmag_latmax= ',f8.3,' vmag_lonmin= ',f8.3 + & ,'E (',f8.3,'W) imax= ',i4,' jmax= ',i4) + 46 format (' vmag_latmin= ',f8.3,' vmag_lonmax= ',f8.3 + & ,'E (',f8.3,'W)') + + if (vmag_lonmin > 330. .and. vmag_lonmax < 30.) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: For a case of find_maxmin, our vmag' + print *,'!!! subgrid is straddling the GM. The code should' + print *,'!!! be able to handle this, but if strange errors' + print *,'!!! are occurring, check into the code either here' + print *,'!!! in find_maxmin or get_uv_ctr.' + print *,' ' + endif + endif + + npts = ceiling(rads/(dtk*dell)) + + else + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,guesslon,guesslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to ' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + ifmret = 92 + return + endif + + endif + +c +c --------------------------------------------------------------- +c + if ( verb .ge. 3 ) then + print *,' ' + write (6,39) guesslon,360.-guesslon,guesslat + 39 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + if (cparm == 'vmag') then + print *,'ilonfix= (unused) jlatfix= (unused)' + & ,' npts= ',npts + print *,'ilonfix and jlatfix are meaningless for computing' + print *,'vmag, so ignore the large values you see for them.' + else + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + endif + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + + if ( verb .ge. 3 ) then + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: find_maxmin 1 ',i2.2,':',i2.2,':',i2.2) + endif + + ibct=0 + ibarnes_loopct = 0 + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (guesslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = guesslon - 360. + else + temp_guesslon = guesslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = guesslon + endif + + jix = 0 + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + jloop: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = guesslat + dell*float(j) + + iix = 0 + +c vlat(jix) = rlatt + + iloop: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c if (cparm == 'vmag') then +c print *,' ' +c print '(a16,i6,a4,i6,2(a8,f8.3),a12,f8.3)' +c & ,'in find_max, i= ',i +c & ,' j= ',j,' rlatt= ',rlatt,' rlont= ',rlont +c & ,' 360-rlont= ',360.-rlont +c endif + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT: icvpret= ',icvpret + endif + + cycle iloop + endif + + call calcdist(rlont,rlatt,temp_guesslon,guesslat,dist,degrees) + if (dist .gt. rads) cycle iloop + + if (cparm == 'vmag') then + +c This next bit of code gets the ij coordinates for an 8x8 +c box around the current point under consideration. These ij +c coordinates are sent to barnes so that barnes only loops +c 64 times, as opposed to nearly 10,000 if the whole 97x97 +c array were sent. So, fix rlatt to the grid point just +c northward of rlatt and fix rlont to the grid point just +c eastward of rlont. Note that this makes for a modified +c barnes analysis in that we're sort of specifying ahead of +c time exactly which grid points will be included and we'll +c be excluding some points that would be near the periphery +c of each (rlont,rlatt)'s range, but as long as we're consis- +c tent and do it this way for each point, it's well worth the +c trade-off in cpu time. Parameter maxvgrid determines what +c size array to send to barnes (maxvgrid=8 means 8x8) + + jvlatfix = int((vmag_latmax - rlatt)/dy + 1.) + ivlonfix = int((rlont - temp_grid_minlon)/dx + 2.) +c ivlonfix = int((rlont - vmag_lonmin)/dx + 2.) + + ibeg = ivlonfix - (maxvgrid/2) + iend = ivlonfix + (maxvgrid/2 - 1) + jbeg = jvlatfix - (maxvgrid/2 - 1) + jend = jvlatfix + (maxvgrid/2) + + if (ibeg < 1 .or. jbeg < 1 .or. + & iend > imax .or. jend > jmax) then + + ! DO NOT quit if we find a boundary outside the grid + ! bounds. Rather, just set the J violating bound(s) to + ! the min or max limit, and for I bounds, allow the + ! program to continue down to subsequent code below, + ! provided it's a global grid. + +c print *,'!!! ' +c print *,'!!! Before vmag adjustments, boundaries are: ' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt,' dx= ',dx +c print *,'!!! temp_grid_minlon= ',temp_grid_minlon +c print *,'!!! vmag_latmax= ',vmag_latmax +c print *,'!!! ivlonfix = ',ivlonfix,' jvlatfix = ',jvlatfix +c print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax +c print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Vmag will not be computed for' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Vmag will not be computed for ' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,'!!! ' + print *,'!!! *AFTER* vmag adjustments, boundaries are: ' + print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax + print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + endif + + endif + + endif + + if (cparm == 'vmag') then + ri = re * 3 +c print '(a36,f10.4,a6,f10.4)' +c & ,' + before call to vmag barnes, re= ',re,' ri= ',ri + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,bskip1,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes...' + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After 1st findmax loop, # calls to barnes = ',ibct + print *,'Total # of barnes loop iterations = ',ibarnes_loopct + endif + +c + 55 format ('i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ',f7.3 + & ,' barnval= ',f11.5) + 56 format ('k= ',i3,' i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ' + & ,f7.3,' barnval= ',f11.5) + + if (ctlon < 0.) then + ! We have grid-wrapped to find the ctlon, which was found to be + ! < 0, so for reporting purposes and for the start of the next + ! loop, set ctlon to positive degress east. + ctlon = ctlon + 360. + endif + + if (cparm == 'zeta') then + + if ( verb .ge. 3 ) then + print *,'!!! Zeta check, fmax= ',fmax,' fmin= ',fmin + write (6,61) 360.-ctlon,ctlat,fmax*100000.,fmin*100000. + endif + + else + + if ( verb .ge. 3 ) then + write (6,63) 360.-ctlon,ctlat,fmax,fmin + endif + + endif + 61 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 63 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax = ',e16.3,' fmin = ',e16.3) + 111 format (i2,' rlont= ',f7.2,'W rlatt= ',f7.2,' zeta= ',f13.8) + +c Through interpolation, the grid for vmag has already been +c minimized considerably, we don't need to go through the 2nd part +c of this subroutine, which halves the grid spacing. + + if (nhalf < 1 .or. cparm == 'vmag') then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c ------------------------------------------------------------- +c If the grid spacing is +c fine enough (I've chosen 0.2-deg as a min threshold), there is +c no need to halve the grid more than 3 times, as halving a +c 0.2-deg grid 3 times gives a resolution of 0.025-deg (2.7 km), +c or a max error in the position estimate of 2.7/2 = 1.35 km. + + if ((dx+dy)/2. <= 0.2) then + if ((dx+dy)/2. <= 0.05) then + nhalf = 1 + else + nhalf = 2 + endif + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +ctpm npts = 3 + npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only +c do this once for this grid-refinement (even though the grid is +c redefined 3 times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to get_ij_bounds' + print *,'!!! just before nhalf loop. Stopping processing' + print *,'!!! for storm number ',ist + endif + + ifmret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + + if ( verb .ge. 3 ) then + print *,' ' + endif + + if ((dx+dy)/2. <= 1.25 .and. ri >= 300 .and. re >= 150) then + retmp = re + ritmp = ri + re = re * 0.5 + ri = ri * 0.5 + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re has been reduced' + print *,'from ',retmp,' to ',re,', and ri has been reduced ' + print *,'from ',ritmp,' to ',ri + endif + + else + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re and ri have NOT ' + print *,'been changed. re = ',re,' ri = ',ri + endif + + endif + + ibct=0 + ibarnes_loopct = 0 + do k=1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: find_maxmin kloop, k= ',i2,' ',i2.2,':' + & ,i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat + fmax = -1.0e+15; fmin = 1.0e+15 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'find_maxmin nhalf loop, cparm= ',cparm,' k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,iskip,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes, k= ',k + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop2 + enddo jloop2 + + if ( verb .ge. 3 ) then + if (cparm == 'zeta') then + write (6,71) k,360.-ctlon,ctlat,fmax*100000.,fmin*100000. + else + write (6,73) k,360.-ctlon,ctlat,fmax,fmin + endif + endif + + enddo + + 71 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 73 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax = ',e16.3,' fmin = ',e16.3) + + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ppp after 2nd findmax loop, # calls to barnes = ' + & ,ibct + print *,'ppp Total # of barnes loop iterations = ' + & ,ibarnes_loopct + endif + + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine barnes(flon,flat,rlon,rlat,iimax,jjmax,iibeg,jjbeg + & ,iiend,jjend,fxy,defined_pt,bskip,re,ri,favg,icount,ctype + & ,trkrinfo,iret) +c +c ABSTRACT: This routine performs a single-pass barnes anaylsis +c of fxy at the point (flon,flat). The e-folding radius (km) +c and influence radius (km) are re and ri, respectively. +c +c NOTE: The input grid that is searched in this subroutine is most +c likely NOT the model's full, original grid. Instead, a smaller +c subgrid of the original grid is searched. The upper left and +c lower right grid point indices are passed into this subroutine +c (iibeg, jjbeg, iiend, jjend) for this subgrid. These indices are +c determined in the subroutine get_ij_bounds, and the purpose of +c doing it this way is to limit the number of points for which the +c subroutine has to calculate distances (for a global 1 deg grid, +c the number of loop iterations is reduced from 65160 to somewhere +c around 600). +c +c NOTE: This subroutine will immediately exit with a non-zero +c return code if it tries to access a grid point that does not have +c valid data. This would happen in the case of a regional grid, if +c you try to access a point near the edge of the grid (remember that +c because of the interpolation for the regional grids, there will be +c areas around the edges that have no valid data). +c +c INPUT: +c flon Lon value for center point about which barnes anl is done +c flat Lat value for center point about which barnes anl is done +c rlon Array of lon values for each grid point +c rlat Array of lat values for each grid point +c iimax Max number of pts in x-direction on input grid +c jjmax Max number of pts in y-direction on input grid +c iibeg i index for grid point to start barnes anlysis (upp left) +c jjbeg j index for grid point to start barnes anlysis (upp left) +c iiend i index for last grid point in barnes anlysis (low right) +c jjend j index for last grid point in barnes anlysis (low right) +c fxy Real array of data on which to perform barnes analysis +c defined_pt Logical; bitmap array used for regional grids +c bskip integer to indicate number of grid points to skip during +c a barnes loop, in order to speed processing +c re input e-folding radius for barnes analysis +c ri input influence radius for searching for min/max +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, in +c this barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c favg Average value about the point (flon,flat) +c iret Return code from this subroutine +c + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real fxy(iimax,jjmax), rlon(iimax), rlat(jjmax) + real degrees + integer bskip + logical(1) defined_pt(iimax,jjmax) + character(*) ctype + +c -------------------------- + + res = re*re + wts = 0.0 + favg = 0.0 + + icount = 0 + + do jix=jjbeg,jjend,bskip + do iix=iibeg,iiend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > iimax) then + if (trkrinfo%gridtype == 'global') then + i = iix - iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i,' imax= ',iimax + print *,' ' + endif + + stop 97 + endif + endif + + icount = icount + 1 + + call calcdist(flon,flat,rlon(i),rlat(j),dist,degrees) + + if (dist .gt. ri) cycle + + if (defined_pt(i,j)) then + if (fxy(i,j) >-999.01 .and. fxy(i,j) <-998.99) then + ! This is a patch. Even though this (i,j) is a valid + ! point, its zeta value has been set to -999 because a + ! neighboring point in subroutine rvcal was found + ! to be out of the grid boundaries. + cycle + endif + wt = exp(-1.0*dist*dist/res) + wts = wts + wt + favg = favg + wt*fxy(i,j) + else + if (ctype == 'vitals') then + continue + else +carw print *,' ' +carw print *,'!!! UNDEFINED PT OUTSIDE OF GRID IN BARNES....' +carw print *,'!!! i= ',i,' j= ',j +carw print *,'!!! flon= ',flon,' flat= ',flat +carw print *,'!!! rlon= ',rlon(i),' rlat= ',rlat(j) +carw print *,'!!! re= ',re,' ri= ',ri +carw print *,'!!! EXITING BARNES....' +carw print *,' ' +carw iret = 95 +carw return + endif + endif + + enddo + enddo + + if (wts > 1.0E-5) then + favg = favg/wts + else + favg = 0.0 + endif + iret = 0 +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,rglatmax,rglatmin,rglonmax,rglonmin,geslon,geslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) +c +c ----------------------------------------------------------- +c ABSTRACT: This subroutine figures out, based on ri, dx and dy and +c the guess latitude and longitude positions, the farthest reaching +c grid points that are searchable by an analysis subroutine. The +c purpose is to return indices for a subgrid that is much smaller +c than the original, full grid. This smaller subgrid can then be +c passed to a subsequent analysis or interpolation subroutine, and +c work can be done on this smaller array. This can help save time, +c especially in the barnes analysis subroutine, as work will only +c be done on, say, a 20 x 20 array (400 pts) instead of on a +c 360 x 181 array (65160 pts). It's crucial, however, to make sure +c that the ibeg, jbeg, iend and jend are extended far enough out to +c fully encompass any search that would be done. Below is a +c diagram showing the different grids.... +c +c Full Global or Regional Model Grid (Grid F) -----------> +c ---------------------------------------------------------------- +c | | (ibeg,jbeg) | +c | | x = ij position that the | (Grid R) | +c | | geslat/geslon is fixed to. ._______________. | +c | | | | | +c | | Even though only the points | (Grid B) | | +c | | within Grid B will be checked | . . . . k | | +c v | later on for a max/min (in the | . . . . . | | +c | case of a subsequent call to | . . x . e | | +c | find_maxmin), the barnes anal- | . . . . . | | +c | ysis will include all pts sur- | . . . . . | | +c | rounding these Grid B points | | | +c | that are within a radius of ri. ._______________. | +c | So in the case of pt. k, that ri | +c | radius may extend all the way to the Grid R | | +c | boundary, thus we need to include those (iend,jend) | +c | points within our ibeg-jbeg-iend-jend bounds. | +c | | +c ---------------------------------------------------------------- +c +c Remember that the grids we deal with start north and increase +c south, so the northernmost latitude on the input grid will have +c a j index of 1. +c +c INPUT: +c npts Num pts from x to edge of max/min search grid (Grid B) +c (i.e., You define the size of Grid B by the value of +c npts that you pass into this subroutine). +c nhalf Number of times the grid spacing will be halved +c ri Radius of influence (for use in barnes analysis) +c imax Number of points in x-direction on original grid +c jmax Number of points in y-direction on original grid +c dx Input grid spacing in i-direction on original grid +c dy Input grid spacing in j-direction on original grid +c rglatmax Value of northern-most latitude on original grid +c rglatmin Value of southern-most latitude on original grid +c rglonmax Value of eastern-most longitude on original grid +c rglonmin Value of western-most longitude on original grid +c geslat Value of latitude of guess position of storm +c geslon Value of longitude of guess position of storm +c +c OUTPUT: +c ilonfix i index on full, input grid that storm is fixed to +c jlatfix j index on full, input grid that storm is fixed to +c ibeg i index for top left of sub-array (Grid R) of input grid +c iend i index for bot right of sub-array (Grid R) of input grid +c jbeg j index for top left of sub-array (Grid R) of input grid +c jend j index for bot right of sub-array (Grid R) of input grid +c igiret Return code from this subroutine +c + USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + real tmpangle +c + igiret = 0 +c +c -------------------------------------- +c GET BEGINNING AND ENDING J POINTS.... +c +c (1) Calculate number of searchable, max/min pts, that is, the pts +c from x to the edge of Grid B. +c (2) Calculate number of pts beyond the last search point in Grid +c B, but are within the bounds of Grid R and thus can be +c included in the barnes analysis. +c (3) Add (1) and (2) to get the max number of pts to subtract/add +c to x to get jbeg and jend. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Beginning of get_ij_bounds...' + print *,' geslat= ',geslat,' geslon= ',geslon + print *,' ' + endif + + +c If nhalf > 0: This occurs in the case of a call from fmax, when +c the grid spacing is halved nhalf times. In this case, we have to +c do extra work to figure out the maximum possible grid point. For +c this case: +c jhlatpts = # of grid pts to last possible search pt (from x to +c edge of Grid B in above diagram), plus a cushion. +c jripts = # of searchable grid points within radius ri of last +c possible search pt (num pts between edge of Grid B +c and edge of Grid R in above diagram), plus a cushion +c jbmaxlatpts = # of pts (in j direction) from x to the edge of +c Grid R to include in this subgrid. +c +c If nhalf = 0: In this case, the grid spacing will not be reduced, +c so the number of pts in j direction from x to the edge of Grid +c B will be the input parameter npts, and just multiply it by 2, +c and add 2 for a cushion to get jmaxlatpts. Typically, this sub +c is called from find_maxmin, and in that sub, the first time that +c this sub is called, nhalf will = 0. Then, after a first-shot +c center is found, the grid spacing will be cut in order to rerun +c barnes on a smaller grid, and that's when nhalf will be sent +c here as 3. +c + if (nhalf > 0) then + rdeg = 0.0 + do i = 1,nhalf + rdeg = rdeg + float(npts) * (1./(float(i)*2)) * (dx+dy)/2 + enddo + jhlatpts = ceiling(rdeg/dy) + 1 + jripts = ceiling((ri + 1.)/(dtk*dx)) + 1 + jbmaxlatpts = jhlatpts + jripts + else + jbmaxlatpts = npts * 2 + 2 + endif +c +c +c Roughly fix geslat to the grid point just poleward of geslat. +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' +++ Near top of get_ij_bounds, ' + print *,' +++ geslat= ',geslat,' geslon= ',geslon + print *,' +++ rglatmax= ',rglatmax,' rglatmin= ',rglatmin + print *,' +++ rglonmax= ',rglonmax,' rglonmin= ',rglonmin + print *,' +++ imax= ',imax,' jmax= ',jmax + print *,' +++ dx= ',dx,' dy= ',dy,' nhalf= ',nhalf + print *,' +++ npts= ',npts + if(nhalf>0) then + print *,' +++ jhlatpts= ',jhlatpts,' jripts= ',jripts + else + print *,' +++ nhalf<=0 so jhlatpts and jripts unused' + endif + print *,' +++ jbmaxlatpts= ',jbmaxlatpts + endif + + if (geslat >= 0.0) then + jlatfix = int((rglatmax - geslat)/dy + 1.) + else + jlatfix = ceiling((rglatmax - geslat)/dy + 1.) + endif + + if ( verb .ge. 3 ) then + print *,' +++ jlatfix= ',jlatfix + endif + + jbeg = jlatfix - jbmaxlatpts + jend = jlatfix + jbmaxlatpts + if (jbeg > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jbeg > jmax' + print *,'!!! jbeg = ',jbeg,' jmax= ',jmax + endif + + igiret = igiret + 1 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jend < 1, jend = ',jend + endif + + igiret = igiret + 1 + return + endif + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' +++ jbeg= ',jbeg,' jend= ',jend + endif + + ! If using a global grid, avoid using the pole points, or else + ! you'll get a cosfac = 0 and then a divide by zero!!! + + if (jend == jmax .and. rglatmin == -90.0) then + jend = jmax - 2 + endif + if (jbeg == 1 .and. rglatmax == 90.0) then + jbeg = 3 + endif + +c ----------------------------------------- +c NOW GET BEGINNING AND ENDING I POINTS.... +c +c Using the map factor (cos lat), figure out, based on ri, the +c max distance beyond the last search point in x-direction (in +c degrees) that could be searched at this guess latitude (geslat) +c (i.e., in the diagram above, the max num pts from pt. e eastward +c to the edge of Grid R). Calculate how many grid points that is, +c add 2 to it for a cushion, & add the number of points (npts) +c within the defined search grid (Grid B) to get ibmaxlonpts. +c +c April, 2007: A min statement was put on the calculation to +c derive dlon, since with that cosine in there, the values of +c of dlon could get pretty ridiculous as you approach the poles. +c Also, the cosine factor (cosfac) used to be computed at the +c most poleward latitude possible given the jend here. For +c similar concerns with cosines near the poles, I've scrapped +c this to instead compute the cosine factor at the input +c guess latitude. - tpm + + cosfac = cos (geslat * dtr) + tmpangle = cosfac * dtk + dlon = min((ri /tmpangle ),20.0) +c dlon = min((ri / (cosfac * dtk)),20.0) +c + if (nhalf > 0) then + ihlonpts = ceiling(rdeg/dx) + 1 + ibmaxlonpts = ihlonpts + ceiling(dlon/dx) + 2 + else + ibmaxlonpts = npts + ceiling(dlon/dx) + 2 + endif + + if ( verb .ge. 3 ) then + if(nhalf>0) then + print *,' +++ rdeg= ',rdeg,' ri= ',ri,' cosfac= ',cosfac + print *,' +++ dtr= ',dtr,' dtk= ',dtk,' dlon= ',dlon + else + print*,' +++ nhalf<=0 so rdeg,ri,cosfac,dtr,dtk,dlon unused' + endif + print *,' +++ ibmaxlonpts= ',ibmaxlonpts,' dx= ',dx,' dy= ',dy + endif + +c Roughly fix geslon to the grid point just EASTward of geslon. + + ilonfix = int((geslon - rglonmin)/dx + 2.) + + ibeg = ilonfix - ibmaxlonpts + iend = ilonfix + ibmaxlonpts + + if ( verb .ge. 3 ) then + print *,' +++ (orig) ilonfix= ',ilonfix + print *,' +++ (orig) ibeg= ',ibeg,' iend= ',iend + print *,' +++ ' + endif + + if (ibeg > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 1 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, ibeg > imax' + print *,'!!! for a non-global grid' + print *,'!!! ibeg = ',ibeg,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + ! For a regional grid, just set iend to be imax + iend = imax + endif + endif + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + ! For a regional grid, just set ibeg to be 1 + ibeg = 1 + endif + endif + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + + if ( verb .ge. 3 ) then + print *,'!!! ERROR in get_ij_bounds, iend < 1' + print *,'!!! for a non-global grid' + print *,'!!! iend = ',iend,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine check_bounds (guesslon,guesslat,ist,ifh,trkrinfo + & ,icbret) +c +c ABSTRACT: This subroutine checks to make sure that the requested +c storm is in fact within the model's grid boundaries; +c this is only a concern for the regional models. +c + USE def_vitals; USE grid_bounds; USE set_max_parms + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + if (trkrinfo%gridtype == 'regional') then + if (guesslon > glonmax .or. guesslon < glonmin .or. + & guesslat > glatmax .or. guesslat < glatmin) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is outside of grid' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + goto 125 + else + icbret = 0 + endif + endif + + ! We have encountered problems with global grids where we + ! continue tracking almost the whole way to the pole. While + ! that's nice to do that, it creates problems for array + ! indices, especially in subroutine getradii. So we will cut + ! our losses and eliminate tracking of storms within + ! 5 degrees of the pole for global grids. + + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'global')then + if (guesslat > 85.0 .or. guesslat < -85.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is too close to the' + print *,'!!! N or S Pole for global tracking.' + print *,'!!! STOPPING TRACKING FOR THIS STORM DUE TO POLE' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + else + icbret = 0 + endif + endif + + 125 continue +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calcdist(rlonb,rlatb,rlonc,rlatc,xdist,degrees) +c +c ABSTRACT: This subroutine computes the distance between two +c lat/lon points by using spherical coordinates to +c calculate the great circle distance between the points. +c Figure out the angle (a) between pt.B and pt.C, +c N. Pole then figure out how much of a % of a great +c x circle distance that angle represents. +c / \ +c b/ \ cos(a) = (cos b)(cos c) + (sin b)(sin c)(cos A) +c / \ +c pt./<--A-->\c NOTE: The latitude arguments passed to the +c B / \ subr are the actual lat vals, but in +c \ the calculation we use 90-lat. +c a \ +c \pt. NOTE: You may get strange results if you: +c C (1) use positive values for SH lats AND +c you try computing distances across the +c equator, or (2) use lon values of 0 to +c -180 for WH lons AND you try computing +c distances across the 180E meridian. +c +c NOTE: In the diagram above, (a) is the angle between pt. B and +c pt. C (with pt. x as the vertex), and (A) is the difference in +c longitude (in degrees, absolute value) between pt. B and pt. C. +c +c !!! NOTE !!! -- THE PARAMETER ecircum IS DEFINED (AS OF THE +c ORIGINAL WRITING OF THIS SYSTEM) IN KM, NOT M, SO BE AWARE THAT +c THE DISTANCE RETURNED FROM THIS SUBROUTINE IS ALSO IN KM. +c + USE trig_vals + + real degrees +c + if (rlatb < 0.0 .or. rlatc < 0.0) then + pole = -90. + else + pole = 90. + endif +c + distlatb = (pole - rlatb) * dtr + distlatc = (pole - rlatc) * dtr + difflon = abs( (rlonb - rlonc)*dtr ) +c + cosanga = ( cos(distlatb) * cos(distlatc) + + & sin(distlatb) * sin(distlatc) * cos(difflon)) + +c This next check of cosanga is needed since I have had ACOS crash +c when calculating the distance between 2 identical points (should +c = 0), but the input for ACOS was just slightly over 1 +c (e.g., 1.00000000007), due to (I'm guessing) rounding errors. + + if (cosanga > 1.0) then + cosanga = 1.0 + endif +cPENG added bug fixed on 04/28/2016-------------------------- + if (cosanga < -1.0) then + cosanga = -1.0 + endif +cPENG added on 04/28/2016-------------------------- + + degrees = acos(cosanga) / dtr + circ_fract = degrees / 360. + xdist = circ_fract * ecircum +c +c NOTE: whether this subroutine returns the value of the distance +c in km or m depends on the scale of the parameter ecircum. +c At the original writing of this subroutine (7/97), ecircum +c was given in km. +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine subtract_cor (imax,jmax,dy,level) +c +c ABSTRACT: This subroutine subtracts out the coriolis parameter +c from the vorticity values. It is needed because at the original +c writing of this system, all of the forecast centers who included +c vorticity included only absolute vorticity. +c + USE tracked_parms; USE trig_vals; USE grid_bounds + + implicit none + + integer :: i,j,imax,jmax,level + real :: dy,coriolis,rlat +c + do j=1,jmax + rlat = glatmax - ((j-1) * dy) + coriolis = 2. * omega * sin(rlat*dtr) + do i=1,imax + zeta(i,j,level) = zeta(i,j,level) - coriolis + enddo + enddo +c + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_grib_file_name (ifh,gfilename,ifilename) + +c ABSTRACT: This subroutine uses various input regarding the model +c and forecast hour and generates the name of the input grib file +c for this particular forecast hour. Remember that the lead time +c is in minutes and that 5 spaces must be reserved for the lead +c time (e.g., f00360). File name should be something that looks +c like either, e.g., "gfdl.6thdeg.katrina12l.2005082818.f00720", +c or "gfdl.6thdeg.2005082818.f00720" (the part in there with the +c storm name & ID is optional). The grib index file name should +c be exactly the same as the grib data file itself, but with the +c character string ".ix" added onto the end of the name. +c +c NOTE: Array iftotalmins is brought in via module tracked_parms. +c +C INPUT: +c ifh integer array index for current lead time +c +c OUTPUT: +c gfilename GRIB file name +c ifilename GRIB index file name + + USE gfilename_info; USE tracked_parms; USE atcf + USE verbose_output + + implicit none + + character(*) gfilename,ifilename + character cfmin*5,cymdh*10 + integer ifh,nlen1,nlen2,nlen3,nlen4,nlen5 + +c Convert integer minutes to 5-position character, with +c leading zeroes, and convert 10-digit integer date into +c 10-position character. Then trim the various input variables +c and combine all into the file name. + + write (cfmin,'(i5.5)') iftotalmins(ifh) + write (cymdh,'(i10.10)') atcfymdh + + nlen1 = len_trim(gmodname) + gfilename = trim(gmodname(1:nlen1)) + + nlen2 = len_trim(rundescr) + + gfilename = trim(gfilename(1:nlen1))//'.'//trim(rundescr(1:nlen2)) + + nlen3 = len_trim(atcfdescr) + nlen4 = len_trim(gfilename) + +c If an extension to the name with the ATCF or storm name descriptor +c was included, then add it to the name now. Otherwise, just add +c the starting date and the lead time in minutes. + + if (nlen3 > 0) then + gfilename = trim(gfilename(1:nlen4))//'.' + & //trim(atcfdescr(1:nlen3))//'.'//cymdh//'.f'//cfmin + else + gfilename = trim(gfilename(1:nlen4))//'.'//cymdh//'.f'//cfmin + endif + +c Create the name for the grib index file, which is just the name of +c the grib file, with "ix" added to the end of it. + + nlen5 = len_trim(gfilename) + ifilename = trim(gfilename(1:nlen5))//'.ix' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,72) 'gfilename',gfilename + write (6,72) 'ifilename',ifilename + endif + + 72 format (1x,'In get_grib_file_name, file name for ',a9 + & ,' is ',a120) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) +c +c ABSTRACT: This subroutine reads the input GRIB file for the +c tracked parameters. It then calls subroutines to convert the +c data from a 1-d array into a 2-d array if the read was successful. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature (I jerry-rigged this by storing +c the data as being at the 401 mb level.) +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +cPENG----2018-06-07 ------------------------ +c 18. Ushear 200-850hPa (501hPa level) +c 19. 500hPa relative humidity +c +c INPUT: +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c inp of a derived type, contains user-input info +c lugb integer unit number of input grib file +c lugi integer unit number of input grib index file +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE verbose_output; USE params; USE grib_mod; USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + type (datecard) inp + type (gribfield) :: gfld,prevfld,holdgfld +c + integer, parameter :: jf=40000000 +c integer, parameter :: nreadparms=17 +cPENG----2018-06-07 ------------------------ + integer, parameter :: nreadparms=19 + + real, allocatable :: f(:) + real :: dmin,dmax,firstval,lastval + logical(1), allocatable :: lb(:) + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1) file_open + logical :: unpack=.true. + logical :: open_grb=.false. + character*1 :: lbrdflag + character*8 :: chparm(nreadparms) + CHARACTER(len=8) :: pabbrev + character (len=10) big_ben(3) + integer date_time(8) + integer,dimension(200) :: jids,jpdt,jgdt + integer :: listsec1(13), enable_timing + integer, intent(in) :: imax,jmax + integer igparm(nreadparms),iglev(nreadparms) + integer iglevtyp(nreadparms) + integer ig2_parm_cat(nreadparms),ig2_parm_num(nreadparms) +cPENG----2018-06-07 ------------------------ + integer ig2_parm_cat_cmcd(nreadparms) + & ,ig2_parm_num_cmcd(nreadparms) + +c integer ig2_lev_val(nreadparms),ig2_lev_typ(nreadparms) +cPENG----2018-06-07 ------------------------ + integer ig2_lev_10(nreadparms) + integer ig2_lev_11(nreadparms),ig2_lev_12(nreadparms) + +cPENG 04/18/2018 for CMC Det. and CMC ensemble data +cPENG----2018-06-07 ------------------------ +c integer ig2_lev_11_cmc(nreadparms),ig2_lev_val_cmc(nreadparms) +c integer ig2_lev_11_cmcd(nreadparms),ig2_lev_val_cmcd(nreadparms) + integer ig2_lev_11_cmc(nreadparms),ig2_lev_12_cmc(nreadparms) + integer ig2_lev_11_cmcd(nreadparms),ig2_lev_12_cmcd(nreadparms) + + integer cpsig2_parm_cat(nlevs_cps),cpsig2_parm_num(nlevs_cps) +c integer cpsig2_lev_typ(nlevs_cps),cpsig2_lev_val(nlevs_cps) +cPENG-04/18/2018 for CMC Det. and CMC ensemble data + integer cpsig2_lev_10(nlevs_cps) + integer cpsig2_lev_11(nlevs_cps),cpsig2_lev_12(nlevs_cps) + + integer ec_igparm(nreadparms),ec_iglev(nreadparms) + integer ec_iglevtyp(nreadparms) + integer cpsgparm(nlevs_cps),cpsglev(nlevs_cps) + integer cpsglevtyp(nlevs_cps) + integer ec_cpsgparm(nlevs_cps) + integer jpds(200),jgds(200),kpds(200),kgds(200) + integer igvret,ifa,ila,ip,ifh,i,j,k,kj,iret,kf,lugb,lugi + integer jskp,jdisc,np + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer pdt_4p0_vert_level,pdt_4p0_vtime + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 +c + lbrdflag = 'n' + enable_timing=trkrinfo%enable_timing +c The following data statements contain the parameters that will be +c used to search the grib files. The first 9 parameters will all be +c used to locate the storm position. The last 4 parameters (500 mb +c u- and v-components and 10 m u- and v- components) will not be +c used for tracking, but only for helping to estimate the next first +c guess position (500 mb winds) and for estimating the max near- +c surface wind speeds in the vicinity of the storm (10 m winds). +c +c ** NOTE: iglevtyp(12 & 13) and iglev(12 & 13) are initialized to +c 0 just to satisfy the IBM xlf compiler, which barks about +c there being too few initial values in the list when I +c only had 11 values there -- even though the real +c initialization for these variables is done just about +c 10 lines below. +c +c ** NOTE: The new ECMWF hi-res data uses the ECMWF GRIB parameter +c ID table, which has different values than the NCEP +c table. Therefore, we needed to add the variables and +c data values for ec_igparm, ec_iglevtyp and ec_iglev. +c +c July 2007: Read statements added for GP height for cyclone +c phase space (CPS) algorithm. + +c data igparm /41,41,33,34,33,34,7,7,1,33,34,33,34,11,7,7,81/ +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7,81/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 +c & ,100,100,100,1/ +c data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500,401 +c & ,500,200,0/ +cPENG----2018-06-07 ------------------------ + data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7,81 + & ,33,52/ + data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 + & ,100,100,100,1,100,100/ + data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500,401 + & ,500,200,0,501,500/ + + data cpsgparm /13*7/ + data ec_cpsgparm /13*156/ + data cpsglevtyp /13*100/ + data cpsglev /900,850,800,750,700,650,600,550,500,450,400 + & ,350,300/ + +c data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 +c & ,131,132,130,156,156,999/ +c data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 +c & ,100,100,100,999/ +c data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 +c & ,401,500,200,999/ +c +c data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' +c & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' +c & ,'vgrid','temp','gphgt','gphgt','lmask'/ +cPENG----2018-06-07 ------------------------ + data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 + & ,131,132,130,156,156,999,131,157/ + data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 + & ,100,100,100,999,100,100/ + data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 + & ,401,500,200,999,501,500/ + + data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' + & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' + & ,'vgrid','temp','gphgt','gphgt','lmask' + & ,'ushe','rhum'/ + +c data ig2_parm_cat /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2/ +c data ig2_parm_num /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8/ +c data ig2_lev_typ /100,100,100,100,100,100,100,100,101,103,103 +c & ,100,100,100,100,100,-9999/ +c data ig2_lev_val /850,700,850,850,700,700,850,700,0,10,10,500,500 +c & ,401,500,200,-9999/ +cPENG----2018-06-07 ------------------------ + data ig2_parm_cat /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2,2,1/ + data ig2_parm_num /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8,2,1/ + data ig2_parm_cat_cmcd /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2,2,1/ + data ig2_parm_num_cmcd /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8,2,1/ + + data ig2_lev_10 /100,100,100,100,100,100,100,100,101,103,103 + & ,100,100,100,100,100,-9999,100,100/ + +cPENG 04/18/2018 for CMC Det. and CMC ensemble data +c data ig2_lev_11_cmc /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 +c & ,-4,-4,0/ +c data ig2_lev_val_cmc/85,7,85,85,7,7,85,7,0,10,10,5,5,40100 +c & ,5,2,-9999/ +c data ig2_lev_11_cmcd /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 +c & ,-4,-4,0/ +c data ig2_lev_val_cmcd /85,7,85,85,7,7,85,7,0,10,10,5,5,40100 +c & ,5,2,-9999/ +cPENG----2018-06-07 ------------------------ +cPENG---for gfs/gefs NEVGEM/FENS ensemble---------- + data ig2_lev_11 /0,0,0,0,0,0,0,0,0,0,0 + & ,0,0,0,0,0,0,0,0/ + data ig2_lev_12 /85000,70000,85000,85000,70000,70000,85000,70000 + & ,0,10,10,50000,50000 + & ,40100,50000,20000,-9999,50100,50000/ +cPENG---for CMC ensemble--------------------- + data ig2_lev_11_cmc /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0,0,-4/ + data ig2_lev_12_cmc/85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999,50100,5/ +cPENG---for CMC deterministic--------- + data ig2_lev_11_cmcd /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0,0,0/ + data ig2_lev_12_cmcd /85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999,50100,50000/ + + + data cpsig2_parm_cat /13*3/ + data cpsig2_parm_num /13*5/ +c data cpsig2_lev_typ /13*100/ +c data cpsig2_lev_val /900,850,800,750,700,650,600,550,500,450,400 +c & ,350,300/ +cPENG---------------------------------- + data cpsig2_lev_10 /13*100/ + data cpsig2_lev_11 /13*0/ + data cpsig2_lev_12 /90000,85000,80000,75000,70000,65000 + & ,60000,55000,50000,45000,40000 + & ,35000,30000/ + +c Model numbers used: (1) AVN, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) Early Eta, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble, +c (13) SREF Ensemble, +c (14) NCEP Ensemble (from ensstat mean fields), +c (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) NCEP Ensemble RELOCATION +c (21) UKMET hi-res (from NHC) +c (23) FNMOC Ensemble +c (24) HWRF Basin-scale + + if (trkrinfo%gribver == 2) then + +c For GRIB2, we will check to see if the MSLP being searched for +c is the standard MSLP (MSLP parm ID = 1) or if it is the +c so-called "Eta" or "Membrane" MSLP reduction that is included +c in the output for some models (like GFS and GDAS). Note that +c for 10m winds, with GRIB2, so far with all of the GRIB2 model +c data we've seen to this point, they all have the same IDs for +c 10m winds for all models, so no need to break out by model +c like we do for GRIB v1 in the else portion of this if statement. + + ig2_parm_num(9) = trkrinfo%g2_mslp_parm_id ! 1 = standard MSLP + ! reduction, 192 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB2 read, MSLP ID = ig2_parm_num(9) = ' + & ,ig2_parm_num(9) + + endif + + else + +c For GRIB1, do the same check as done just above in the IF part +c of this IF statement, but note that we need to also check to +c see what the GRIB1 parm IDs are for the sfc wind level type +c and value. Most models list the level type as 105 (which means +c height above the ground) and then a level value of 10. But +c ECMWF and UKMET use a level type of 1 (which means ground or +c water surface) and a level value of 0. + + igparm(9) = trkrinfo%g1_mslp_parm_id ! 102 = standard MSLP + ! reduction, 130 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglev(10) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + iglev(11) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + + ec_iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglev(10) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + ec_iglev(11) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB1 read, MSLP ID = igparm(9) = ' + & ,igparm(9) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev type = ' + & ,iglevtyp(10) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev value = ' + & ,iglev(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev type = ' + & ,ec_iglevtyp(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev value = ' + & ,ec_iglev(10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata. A return' + print *,'code (iret) not equal to zero indicates that ' + print *,'subroutine getgb was unable to find the requested ' + print *,'parameter. This could be simply because the parm is ' + print *,'not included in the grib file (this is likely for ' + print *,'ECMWF data, as they limit what they send us), or it ' + print *,'could indicate a problem with the grib index file.' + endif + + + if (allocated(f)) deallocate(f) + if (allocated(lb)) deallocate(lb) + allocate (f(imax*jmax),stat=ifa) + allocate (lb(imax*jmax),stat=ila) + if (ifa /= 0 .or. ila /= 0) then + print *,' ' + print *,'!!! ERROR in getdata allocating f or lb array.' + print *,'!!! ifa = ',ifa,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + if (trkrinfo%gribver == 2) then + + ! Reading from a GRIB v2 file.... + + grib2_standard_parm_read_loop: do ip = 1,nreadparms + + if (ip == 17) then + if (trkrinfo%use_land_mask == 'y' .or. + & trkrinfo%use_land_mask == 'Y') then + continue + else + if (verb .ge. 3) then + print *,' ' + print *,'The use_land_mask flag has not been set to ' + print *,'y or Y, so we will not try to read it in... ' + print *,' ' + cycle grib2_standard_parm_read_loop + endif + endif + endif + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + if (ip == 17) then + jdisc=1 ! hydrological products. At this point, used only + ! for the land-sea mask. + else + jdisc=0 ! meteorological products + endif + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for input parameter by production template 4.0. This +c tave program is used primarily for temperature, but still we +c will leave that as a variable and not-hard wire it in case we +c choose to average something else in the future. + + ! We are looking for Temperature or GP Height here. This + ! block of code, or even the smaller subset block of code that + ! contains the JPDT(1) and JPDT(2) assignments, can of course + ! be modified if this program is to be used for interpolating + ! other variables.... + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = ig2_parm_cat(ip) + JPDT(2) = ig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + +c JPDT(10) = ig2_lev_typ(ip) +cPENG----2018-06-07 ------------------------ + JPDT(10) = ig2_lev_10(ip) + +cPENG 04/18/2018 CMC Det. and CMC ensemble data +c if (inp%model == 16 ) then +c JPDT(11) = ig2_lev_11_cmc(ip) +c JPDT(12) = ig2_lev_val_cmc(ip) +c elseif (inp%model == 15 ) then +c JPDT(11) = ig2_lev_11_cmcd(ip) +c JPDT(12) = ig2_lev_val_cmcd(ip) +c else +c JPDT(11) = 0 +c if (JPDT(10) == 100) then ! isobaric surface +c JPDT(12) = ig2_lev_val(ip) * 100 ! GRIB2 levels are in Pa +c else +c JPDT(12) = ig2_lev_val(ip) ! This is going to be either mslp +c & ! or 10m winds. +c endif +c endif +cPENG----2018-06-07 ------------------------ + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 8 ) then + JPDT(11) = ig2_lev_11(ip) + JPDT(12) = ig2_lev_12(ip) + else if(inp%model == 16) then + JPDT(11) = ig2_lev_11_cmc(ip) + JPDT(12) = ig2_lev_12_cmc(ip) + else if(inp%model == 15) then + JPDT(11) = ig2_lev_11_cmcd(ip) + JPDT(12) = ig2_lev_12_cmcd(ip) + endif + + if ( verb_g2 .ge. 1 ) then + print *,'before getgb2 call, value of unpack = ',unpack + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is CLOSED' + endif + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is CLOSED' + endif + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,531) date_time(5),date_time(6),date_time(7) + 531 format (1x,'TIMING: before getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,532) date_time(5),date_time(6),date_time(7) + 532 format (1x,'TIMING: after getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call, value of unpacked = ' + & ,gfld%unpacked + print *,'after getgb2 call, gfld%ngrdpts = ',gfld%ngrdpts + print *,'after getgb2 call, gfld%ibmap = ',gfld%ibmap + endif + + if ( iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb2 found parm: ',chparm(ip) +c print *,'+++ at level = ',ig2_lev_val(ip) +cPENG---2018-06-07------------------------------------------- + print *,'+++ at level = ',ig2_lev_12(ip) + + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + select case (chparm(ip)) + case ('absv') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpdt(12) == 20000 .or. jpdt(12) == 2) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) +cPENG----2018-06-07 ------------------------ + case ('ushe') + call conv1d2d_real (imax,jmax,f,ushear + & ,need_to_flip_lats) + case ('rhum') + call conv1d2d_real (imax,jmax,f,rhumid + & ,need_to_flip_lats) + + + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb2 could not find parm: ' + & ,chparm(ip) +c print *,'!!! at level = ',ig2_lev_val(ip) +cPENG---2018-06-07------------------------------------------- + print *,'+++ at level = ',ig2_lev_12(ip) + + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + +c J.Peng---07/29/2019 to free the memory in reading GRIB2 data + call gf_free (gfld) + + enddo grib2_standard_parm_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c This is the GRIB2 reading section. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + grib2_cps_parm_lev_loop: do ip = 1,nlevs_cps + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + + jpds = -1 + jgds = -1 + j=0 + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = cpsig2_parm_cat(ip) + JPDT(2) = cpsig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + +c JPDT(10) = cpsig2_lev_typ(ip) + JPDT(10) = cpsig2_lev_10(ip) +cPENG-------------------------------------------- + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + JPDT(11) = cpsig2_lev_11(ip) + JPDT(12) = cpsig2_lev_12(ip) + endif + +c if (JPDT(10) == 100) then ! isobaric surface +c JPDT(12) = cpsig2_lev_val(ip) * 100 ! GRIB2 levels +c ! are in Pa +c else +c if (verb .ge. 3) then +c print *,' ' +c print *,'ERROR in getdata: JPDT(10) array value' +c print *,'should only be 100 in this CPS section' +c print *,'for GRIB2 data.' +c endif +c endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,731) date_time(5),date_time(6),date_time(7) + 731 format (1x,'TIMING: before getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn + & ,jgdt,unpack,krec,gfld,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,732) date_time(5),date_time(6),date_time(7) + 732 format (1x,'TIMING: after getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 (PHASE) in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call(PHASE),' + & ,' value of unpacked = ',gfld%unpacked + print *,'after getgb2 (PHASE) call, gfld%ngrdpts = ' + & ,gfld%ngrdpts + print *,'after getgb2 (PHASE) call, gfld%ibmap = ' + & ,gfld%ibmap + endif + + if (verb .ge. 3) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + +c Determine packing information from GRIB2 file. +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) + & then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ' + & ,gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ' + & ,gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned + ! from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do +c this once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) + & then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.' + & ,gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ' + & ,gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ' + & ,gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.' + & ,gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline + & ,gfld%ipdtmpl(1),gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,231) + 231 format (' rec# param level byy bmm bdd ' + & ,'bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin + & ,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo grib2_cps_parm_lev_loop + + endif + + endif + + else + + !---------------------------------- + ! Reading from a GRIB v1 file.... + !---------------------------------- + + grib1_read_loop: do ip = 1,nreadparms + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then ! ECMWF hi-res data uses ECMWF table + print *,' ' + print *,'WARNING: From the namelist, inp%model is set to a' + print *,' value of 4, which is for ECMWF, so in routine' + print *,' getdata_grib, the input jpds(5,6,7) parms are' + print *,' going to have values that are specific for' + print *,' ECMWF GRIB1 data.' + print *,' ' + jpds(5) = ec_igparm(ip) + jpds(6) = ec_iglevtyp(ip) + jpds(7) = ec_iglev(ip) + else ! All other models use NCEP-standard GRIB table + jpds(5) = igparm(ip) + jpds(6) = iglevtyp(ip) + jpds(7) = iglev(ip) + endif + + print *,' ' + print *,' --- Before getgb, jpds(5)= ',jpds(5) + print *,' --- , jpds(6)= ',jpds(6) + print *,' --- , jpds(7)= ',jpds(7) + + if (jpds(5) == 999) then + cycle + endif + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,831) date_time(5),date_time(6),date_time(7) + 831 format (1x,'TIMING: before getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + + if (enable_timing /= 0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,832) date_time(5),date_time(6),date_time(7) + 832 format (1x,'TIMING: after getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb call, j= ',j,' k= ',k + & ,' iftotalmins= ' + & ,iftotalmins(ifh),' parm # (ip) = ',ip,' iret= ',iret + else + print *,'After getgb call, j= ',j,' k= ',k,' ifhours= ' + & ,ifhours(ifh),' parm # (ip) = ',ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb found parm: ',chparm(ip) + print *,'+++ at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,29) + else + write (6,31) + endif + 29 format (' rec# parm# levt lev byy bmm bdd bhh fmin' + & ,' npts minval maxval') + 31 format (' rec# parm# levt lev byy bmm bdd bhh fhr ' + & ,' npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + + select case (chparm(ip)) + case ('absv') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpds(7) == 200) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb could not find parm: ',chparm(ip) + print *,'!!! at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib1_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + cps_grib1_lev_loop: do ip = 1,nlevs_cps + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then + ! Use different grib parm id for ECMWF GP height + jpds(5) = ec_cpsgparm(ip) + else + jpds(5) = cpsgparm(ip) + endif + jpds(6) = cpsglevtyp(ip) + jpds(7) = cpsglev(ip) + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,841) date_time(5),date_time(6),date_time(7) + 841 format (1x,'TIMING: before getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,842) date_time(5),date_time(6),date_time(7) + 842 format (1x,'TIMING: after getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,39) + else + write (6,41) + endif + 39 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fmin npts minval maxval') + 41 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fhr npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo cps_grib1_lev_loop + + endif + + endif + + endif +c + deallocate (f) + deallocate (lb) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) +c +c ABSTRACT: This subroutine reads the input NetCDF file for the +c tracked parameters for one lead time. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +c +c If the user has requested to check the cyclone phase space for +c this run (phaseflag set to 'y' and phasescheme set to 'cps'), +c then we need to have gp height data for 900-300 mb at every 50 +c mb. Some of those levels for gp height data were already read +c in with the read of the initial 17 parameters, but we will be +c sure to read in the others, if requested. +c +c INPUT: +c ncfile_id integer ID associated with the NetCDF file +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file +c itself in subroutine read_netcdf_fhours. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE netcdf_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo +c integer, parameter :: nreadparms=17,nreadparms_cps=13 +cPENG---2018-06-07-------------------------------- + integer, parameter :: nreadparms=19,nreadparms_cps=13 + + real, allocatable :: f(:) + real :: dmin,dmax,xmissing_value,xfill_value + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + character*1 :: lbrdflag,match_check + character*30 :: chparm(nreadparms) + character*30 :: chparm_cps(nreadparms_cps) + integer, intent(in) :: ncfile_id,imax,jmax + integer :: igvret,ifa,ip,ifh,i,j,k,m,n,ncfile_tmax,nf_get_att_real + integer :: nf_get_att_double,nf_inq_attlen,imvlen,ifvlen + integer :: usertime,ncix,missing_val_length,nf_status + integer :: nf_inq_varid,varid +c + lbrdflag = 'n' + +cnc data cpsgparm /13*7/ +cnc data cpsglevtyp /13*100/ +cnc data cpsglev /900,850,800,750,700,650,600,550,500,450,400 +cnc & ,350,300/ + +c data chparm /'vort850','vort700' +c & ,'u850','v850','u700','v700' +c & ,'h850','h700','slp','u_ref','v_ref' +c & ,'u500','v500','tm'/ + +c Load the names of the NetCDF variables for the standard +c variables into the chparm array... + + chparm(1) = netcdfinfo%rv850name + chparm(2) = netcdfinfo%rv700name + chparm(3) = netcdfinfo%u850name + chparm(4) = netcdfinfo%v850name + chparm(5) = netcdfinfo%u700name + chparm(6) = netcdfinfo%v700name + chparm(7) = netcdfinfo%z850name + chparm(8) = netcdfinfo%z700name + chparm(9) = netcdfinfo%mslpname + chparm(10) = netcdfinfo%usfcname + chparm(11) = netcdfinfo%vsfcname + chparm(12) = netcdfinfo%u500name + chparm(13) = netcdfinfo%v500name + chparm(14) = netcdfinfo%tmean_300_500_name + chparm(15) = netcdfinfo%z500name + chparm(16) = netcdfinfo%z200name + chparm(17) = netcdfinfo%lmaskname + + + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata_netcdf.' + endif + + if (allocated(f)) deallocate(f) + allocate (f(imax*jmax),stat=ifa) + if (ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getdata_netcdf allocating f data array.' + print *,'!!! ifa = ',ifa + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + !--------------------------------------------------------------- + ! First go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match up + ! the lead times that were read in with the lead times that + ! we read in directly from the NetCDF file. Get the index from + ! the NetCDF file for that lead time and use that in the call to + ! the read routine (get_var3_tlev_double). + !--------------------------------------------------------------- + + usertime = iftotalmins(ifh) + + match_check = 'n' + + find_index_loop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + ncix = m + if (verb .ge. 1) then + print *,'+++ Time match in getdata_netcdf for usertime= ' + & ,usertime,' netcdf file index= ncix= ',ncix + endif + match_check = 'y' + exit find_index_loop + endif + + enddo find_index_loop + + if (match_check == 'n') then + print *,' ' + print *,'!!! ERROR in getdata_netcdf: ' + print *,' For a NetCDF file, the user has ' + print *,' requested to process a lead time, and that lead' + print *,' time does not exist in the NetCDF list of time' + print *,' values. ' + print *,' ifh= ',ifh + print *,' usertime= iftotalmins(ifh)= ',iftotalmins(ifh) + print *,' STOPPING....' + stop 99 + endif + + !--------------------------------------------------------------- + ! Now go through the read loop for the list of parameters + !--------------------------------------------------------------- + + netcdf_standard_parm_read_loop: do ip = 1,nreadparms + + if (chparm(ip) == 'X' .or. chparm(ip) == 'x') then + if (verb .ge. 3) then + print *,' ' + print *,'!!! NetCDF read NOT requested for parm # ',ip + endif + cycle netcdf_standard_parm_read_loop + else + if (verb .ge. 3) then + print *,' ' + print *,'+++ NetCDF read requested for parm # ',ip + & ,' ... parm= ',chparm(ip) + endif + endif + + ! Note that I am sending a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which we + ! want), depending on the model & grid, we may need to flip the + ! grid in the north-south direction. I already have a routine + ! for converting data from a 1-d to a 2-d array, and it has + ! the functionality for flipping a grid, so I programmed it as + ! getting a 1-d array from the netcdf read routine and send that + ! 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm(ip),imax,jmax,ncix + & ,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + + ! Need to get the value of the "missing_value" attribute for + ! this variable from the list of attributes in the NetCDF + ! file. Only do this for the first lead time, since the + ! value of the "missing_value" obviously will not change + ! with lead time. + +c nf_status = nf_inq_attlen (ncfile_id,varid,"missing_value" +c & ,imvlen) +c nf_status = nf_inq_attlen (ncfile_id,varid,"_FillValue" +c & ,ifvlen) + + ! These next two nf function calls retrieve the value of the + ! "missing_value" attribute from the list of attributes for + ! the given variable being read in. This is needed in order + ! to know if a non-valid point is being accessed, as for a + ! regional grid, like the nested fvGFS. In GRIB1/GRIB2 files, + ! such regions would be bitmapped out, but in a NetCDF file, + ! no such bitmap exists, so we have to check for missing + ! values. In case it's a moving grid, we need to do this + ! for every lead time, since the "map of missing values" + ! will shift with lead time. Once we have those missing + ! values, we can loop through them and fill the valid_pt + ! logical array so that, in the end, we will have the same + ! logical bitmap for masking out missing data that we have + ! with GRIB1/GRIB2 data. + + nf_status = nf_inq_varid (ncfile_id,chparm(ip),varid) + + print *,'nf_status from nf_inq_varid call = ',nf_status + + nf_status = nf_get_att_real (ncfile_id,varid,"missing_value" + & ,xmissing_value) + + print *,'nf_status from nf_get_att_real call = ',nf_status + +c nf_status = nf_get_att_real (ncfile_id,varid,"_FillValue" +c & ,xfill_value) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"missing_value",len=imvlen) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"_FillValue",len=ifvlen) +c +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + if (verb .ge. 3) then + write (6,31) + 31 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,33) ifhours(ifh),ifclockmins(ifh),ip,chparm(ip) + & ,dmin,dmax + 33 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,35) chparm(ip),xmissing_value + 35 format (' --- ',a30,' missing value = ',g12.4) + endif + + ! This call to conv1d2d_logic_netcdf creates + ! a logical bitmap, so that in case we have + ! regional (non-global) data and an irregular grid (e.g., + ! the FV3 nested grid), we can mask out grid points that + ! have missing values as their data values. There is not + ! actually a native logical bitmap in NetCDF, so we will + ! create one by examining the real data values and masking + ! out grid points that have missing values. + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic_netcdf (imax,jmax,f,valid_pt + & ,xmissing_value,need_to_flip_lats) + lbrdflag = 'y' + endif + + if (ip == 1) then ! 850 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else if (ip == 2) then ! 700 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + else if (ip == 3) then ! 850 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (ip == 4) then ! 850 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (ip == 5) then ! 700 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (ip == 6) then ! 700 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (ip == 7) then ! 850 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (ip == 8) then ! 700 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (ip == 9) then ! MSLP + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + else if (ip == 10) then ! Near-sfc (10m) u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + else if (ip == 11) then ! Near-sfc (10m) v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + else if (ip == 12) then ! 500 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else if (ip == 13) then ! 500 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else if (ip == 14) then ! 300-500 mb mean Temp + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + else if (ip == 15) then ! 500 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (ip == 16) then ! 200 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + else if (ip == 17) then ! Land-sea mask + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + else + + print *,'!!! NOTE: Parm not recognized. ' + print *,'!!! ip is > 17.... ip= ',ip + print *,'!!! Forecast time level = ',ifh + + endif + + endif + + enddo netcdf_standard_parm_read_loop + +c *--------------------------------------------------------------* +c If we are attempting to determine the cyclone structure using +c Hart's cyclone phase space, then read in data now that will +c allow us to do that. If we are instead just using the +c mid-level (300-500 mb) mean temperature to do that with a +c simple warm-core check, then that mean temperature field was +c already read in above in the read loop for the standard +c variables. The variables needed here for CPS are pretty +c straightforward: gp height every 50 mb from 300 to 900 mb. +c keep in mind that we have already read in a few of these +c gp height records for selected levels above. +c *--------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + chparm_cps(1) = netcdfinfo%z900name + chparm_cps(2) = netcdfinfo%z850name + chparm_cps(3) = netcdfinfo%z800name + chparm_cps(4) = netcdfinfo%z750name + chparm_cps(5) = netcdfinfo%z700name + chparm_cps(6) = netcdfinfo%z650name + chparm_cps(7) = netcdfinfo%z600name + chparm_cps(8) = netcdfinfo%z550name + chparm_cps(9) = netcdfinfo%z500name + chparm_cps(10) = netcdfinfo%z450name + chparm_cps(11) = netcdfinfo%z400name + chparm_cps(12) = netcdfinfo%z350name + chparm_cps(13) = netcdfinfo%z300name + + ! Read in GP Height levels for cyclone phase space... + + if (verb .ge. 3) then + print *,' ' + print *,'--- Reads for CPS parms follow...' + print *,' ' + endif + + netcdf_cps_parm_read_loop: do ip = 1,nreadparms_cps + + if (chparm_cps(ip) == 'X' .or. chparm_cps(ip) == 'x') then + if (verb .ge. 3) then + print *,'!!! ERROR: NetCDF read NOT requested for' + print *,'!!! CPS parm # ',ip + print *,'!!! You must have an error in your namelist.' + print *,'!!! You have requested to do cyclone phase' + print *,'!!! checking, so you need to include the ' + print *,'!!! NetCDF names for ALL requested gp height' + print *,'!!! variables from 900 to 300 mb, every 50 ' + print *,'!!! mb,in the namelist.' + print *,'!!! phaseflag is being set to NO (n), and ' + print *,'!!! phase-checking will NOT take place.' + print *,'!!! If you want to run again and just do ' + print *,'!!! phase-checking with a simple warm-core' + print *,'!!! check, then in the namelist set phaseflag' + print *,'!!! to y and set phasescheme to vtt.' + phaseflag = 'n' + endif + exit netcdf_cps_parm_read_loop + else + if (verb .ge. 3) then + print *,'+++ NetCDF read requested for cps parm # ',ip + & ,' ... parm= ',chparm_cps(ip) + endif + endif + + ! As above, we send a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which + ! we want), depending on the model & grid, we may need to + ! flip the grid in the north-south direction. I already + ! have a routine for converting data from a 1-d to a 2-d + ! array, and it has the functionality for flipping a grid, + ! so I programmed it as getting a 1-d array from the netcdf + ! read routine and send that 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm_cps(ip),imax + & ,jmax,ncix,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm_cps(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + nf_status = nf_inq_varid (ncfile_id,chparm_cps(ip),varid) + nf_status = nf_get_att_real (ncfile_id,varid + & ,"missing_value",xmissing_value) + + if (verb .ge. 3) then + write (6,231) + 231 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,233) ifhours(ifh),ifclockmins(ifh),ip + & ,chparm_cps(ip),dmin,dmax + 233 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,235) chparm_cps(ip),xmissing_value + 235 format (' --- ',a30,' missing value = ',g12.4) + endif + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo netcdf_cps_parm_read_loop + + endif + + endif + + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_ncdim1 (ncid,var1_name,nmax) +c +c ABSTRACT: This routine queries a netcdf file to get the +c value of a requested file dimension (e.g., imax, jmax) +c + implicit none + + include "netcdf.inc" + + integer, intent(in) :: ncid + character*(*), intent(in) :: var1_name + integer, intent(out) :: nmax + integer :: status, var1id + + status = nf_inq_dimid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + status = nf_inq_dimlen (ncid,var1id,nmax) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_ncdim1 +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_var1_double (ncid,var1_name,nmax,var1) +c +c ABSTRACT: This routine reads a netcdf file in order to return +c a 1-dimensional array of data. + + implicit none + + include "netcdf.inc" + + integer, intent(in):: ncid + character*(*), intent(in):: var1_name + integer, intent(in):: nmax + real, intent(out):: var1(nmax) + + integer :: status, var1id + + status = nf_inq_varid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) +! write(*,*) 'Got var1id', var1id + + status = nf_get_var_real (ncid,var1id,var1) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var1_double +c +c--------------------------------------------------------- +c +c--------------------------------------------------------- + subroutine get_var3_tlev_double (ncid,var3_name,imax,jmax,ncix + & ,var3,igvret) +c +c ABSTRACT: This routine reads a netcdf file and returns a +c 2-dimensional synoptic variable at a particular lead time. +c The lead time is specified by the ltix array, which is +c included in module tracked_parms and defined in subroutine +c read_fhours. +c +c PARAMETERS +c +c INPUT: +c ncid integer that contains the NetCDF file ID +c var3_name character name of NetCDF input file +c imax integer x-dimension of input data +c jmax integer y-dimension of input data +c ncix integer index of time level for where this time level +c actually is inside the NetCDF data. Do NOT confuse this +c with the index of where this forecast hour is in the +c user's list of input forecast hours, as they may be +c different. For example, the user may request times that +c are every 6 hours, but the NetCDF file might have times +c that are every hour, so the indices for those two arrays +c will be different. Be sure to use the one (ncix) that +c indicates where the data actually starts in the +c NetCDF file. +c +c OUTPUT: +c var3 real array with real values returned from NetCDF read +c igvret integer return code from this routine + + USE tracked_parms; USE verbose_output; USE netcdf_parms + + implicit none + + include "netcdf.inc" +c + integer, intent(in) :: ncid,ncix + character*(*), intent(in) :: var3_name + integer, intent(in) :: imax,jmax + real, intent(out) :: var3(imax,jmax) + integer :: istart(3),ilength(3) + integer :: status,var3id,igvret + + if (verb .ge. 3) then + print *,' ' + print *,'In get_var3_tlev_double, ncix= ',ncix + print *,' nctotalmins(ncix)= ',nctotalmins(ncix) + endif + + istart(1) = 1 + istart(2) = 1 + istart(3) = ncix + + ilength(1) = imax + ilength(2) = jmax + ilength(3) = 1 + + igvret = 0 + + status = nf_inq_varid (ncid,var3_name,var3id) + + if (status /= NF_NOERR) then + print *,' ' + print *,'NOTE: Could not find variable ',var3_name,' at time' + & ,' index ncix= ',ncix + & ,' nctotalmins(ncix)= ',nctotalmins(ncix) + + igvret = 92 + return + endif + + status = nf_get_vara_real (ncid,var3id,istart,ilength,var3) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var3_tlev_double +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine handle_netcdf_err (status) +c +c ABSTRACT: This subroutine is an error handling routine for NetCDF- +c related functions. + + implicit none + + include "netcdf.inc" + + integer status +c + if (status /= nf_noerr) then + print *,' ' + print *,'Tracker NetCDF error: ' + print *, nf_strerror(status) + stop 'Stopped' + endif + + end subroutine handle_netcdf_err +c +c------------------------------------------------------------------- +c +c------------------------------------------------------------------- + subroutine bitmapchk (n,ld,d,dmin,dmax) +c +c This subroutine checks the bitmap for non-existent data values. +c Since the data from the regional models have been interpolated +c from either a polar stereographic or lambert conformal grid +c onto a lat/lon grid, there will be some gridpoints around the +c edges of this lat/lon grid that have no data; these grid +c points have been bitmapped out by Mark Iredell's interpolater. +c To provide another means of checking for invalid data points +c later in the program, set these bitmapped data values to a +c value of -999.0. The min and max of this array are also +c returned if a user wants to check for reasonable values. +c + logical(1) ld + dimension ld(n),d(n) +c + dmin=1.E15 + dmax=-1.E15 +c + do i=1,n + if (ld(i)) then + dmin=min(dmin,d(i)) + dmax=max(dmax,d(i)) + else + d(i) = -999.0 + endif + enddo +c + return + end +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic (imax,jmax,lb1d,lb2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of logical data (lb1d) into a 2-dimensional output +c array (dimension imax,jmax) of logical data (lb2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c lb1d 1-d array containing logical bitmap values +c iscanflag This is kgds(11), an integer value in the GDS, +c which holds the scanning mode for the data values +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + logical(1) lb1d(imax*jmax),lb2d(imax,jmax) + logical(1) :: need_to_flip_lats + integer :: ilat,ilatix,ilon,imax,jmax +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + lb2d(ilon,ilatix) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + lb2d(ilon,ilat) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic_netcdf (imax,jmax,dat1d,lb2d + & ,xmissing_val,need_to_flip_lats) +c +c ABSTRACT: The purpose of this routine is to create a 2-d logical +c bitmap to be used for masking out regions with missing data, +c such as for a regional grid with irregular boundaries (such as +c we've seen for the regional / nested FV3). This bitmap will +c have the same functionality as a GRIB1/GRIB2 bitmap. The trick +c is that NetCDF does not have a logical bitmap within its +c definition, so we need to make one. We do this by reading in +c the "missing_value" attribute for any variable, then here we +c scan through all the data values retrieved from the NetCDF read, +c and then for all grid points with missing values we set the +c valid_pt flag to .false. +c +c Note the use of the need_to_flip_lats flag. This is in order to +c handle grids that are flipped. Most grids -- NCEP, UKMET, ECMWF +c -- have point (1,1) as the uppermost left point on the grid, and +c the data goes from north to south. Some grids -- GFDL and the +c new NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the need_to_flip_lats flag was set to TRUE in getgridinfo, meaning +c that we have northward scanning data, we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d array containing floating point data values +c xmissing_val real value of missing value for the given variable +c that was read in for the calling routine +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + USE verbose_output + + implicit none + + logical(1) lb2d(imax,jmax) + logical(1) need_to_flip_lats + integer ilat,ilatix,ilon,imax,jmax,tct,fct,mct + real dat1d(imax*jmax) + real xmissing_val +c + tct = 0 + fct = 0 + mct = 0 + + if (verb >= 3) then + print *,' ' + print *,'TOP of conv1d2d_logic_netcdf, xmissing_val= ' + & ,xmissing_val + print *,' ' + endif +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then + lb2d(ilon,ilatix) = .false. +c print *,'LBSF FLIP: ilon= ',ilon,' ilatix= ',ilatix +c fct = fct + 1 + else + lb2d(ilon,ilatix) = .true. +c tct = tct + 1 + endif + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then +c print *,'LBSF no-flip: ilon= ',ilon,' ilat= ',ilat + lb2d(ilon,ilat) = .false. +c fct = fct + 1 + else + lb2d(ilon,ilat) = .true. +c tct = tct + 1 + endif + enddo + enddo + + endif + +c print *,' ' +c print *,' LB STATS: tct= ',tct,' fct= ',fct,' mct= ',mct +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine conv1d2d_real (imax,jmax,dat1d,dat2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of real data (dat1d) into a 2-dimensional output +c array (dimension imax,jmax) of real data (dat2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d real array of data +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c dat2d 2-d real array of data +c + logical(1) :: need_to_flip_lats + real dat1d(imax*jmax),dat2d(imax,jmax) +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + dat2d(ilon,ilatix) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + dat2d(ilon,ilat) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_nlists (inp,trkrinfo,netcdfinfo) +c +c ABSTRACT: This subroutine simply reads in the namelists that are +c created in the shell script. Namelist datein contains the +c starting date information, plus the model identifier. Namelist +c stswitch contains the flags for processing for each storm. +c + USE inparms; USE set_max_parms; USE atcf; USE trkrparms; USE phase + USE structure; USE gfilename_info + USE verbose_output; USE waitfor_parms; USE netcdf_parms + USE tracking_parm_prefs + + implicit none + + integer ifh + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo +c + namelist/datein/inp + namelist/atcfinfo/atcfnum,atcfname,atcfymdh,atcffreq + namelist/trackerinfo/trkrinfo + namelist/phaseinfo/phaseflag,phasescheme,wcore_depth + namelist/structinfo/structflag,ikeflag + namelist/fnameinfo/gmodname,rundescr,atcfdescr + namelist/verbose/verb,verb_g2 + namelist/waitinfo/use_waitfor,wait_min_age,wait_min_size + & ,wait_max_wait,wait_sleeptime + & ,use_per_fcst_command,per_fcst_command + namelist/netcdflist/netcdfinfo + namelist/parmpreflist/user_wants_to_track_zeta850 + & ,user_wants_to_track_zeta700,user_wants_to_track_wcirc850 + & ,user_wants_to_track_wcirc700,user_wants_to_track_gph850 + & ,user_wants_to_track_gph700,user_wants_to_track_mslp + & ,user_wants_to_track_wcircsfc,user_wants_to_track_zetasfc + & ,user_wants_to_track_thick500850 + & ,user_wants_to_track_thick200500 + & ,user_wants_to_track_thick200850 + +c Set namelist default values: + use_per_fcst_command='t' + per_fcst_command=' ' + atcffreq=600 + trkrinfo%enable_timing=1 + trkrinfo%want_oci=.false. + trkrinfo%gribver=1 ! Set to GRIB1 as default, can be set to + ! something else in the namelist input. + + read (5,NML=datein,END=801) + 801 continue + read (5,NML=atcfinfo,END=807) + 807 continue + print *,'just before trackerinfo read namelist' + read (5,NML=trackerinfo,END=809) + 809 continue + print *,'just after trackerinfo read namelist' + read (5,NML=phaseinfo,END=811) + 811 continue + read (5,NML=structinfo,END=815) + 815 continue + read (5,NML=fnameinfo,END=817) + 817 continue + read (5,NML=waitinfo,END=821) + 821 continue + read (5,NML=netcdflist,END=823) + 823 continue + read (5,NML=parmpreflist,END=825) + 825 continue + read (5,NML=verbose,END=819,ERR=833) + 819 continue + goto 837 + 833 continue + verb = 1 + 837 continue + + print *,'in read_nlists, verb= ',verb + + if ( verb .ge. 0 ) then + print *,' ' + print *,'After datein namelist in trak.f, namelist ' + & ,'parms follow:' + print *,'Forecast initial year = byy = ',inp%byy + print *,'Forecast initial month = bmm = ',inp%bmm + print *,'Forecast initial day = bdd = ',inp%bdd + print *,'Forecast initial hour = bhh = ',inp%bhh + print *,'Forecast model identifier = model= ',inp%model + print *,'Forecast model type = modtyp= ',inp%modtyp + print *,'Forecast model data lead time units= lt_units= ' + & ,inp%lt_units + print *,'Forecast model data sequencing setup= file_seq= ' + & ,inp%file_seq + print *,'Forecast model nest type = ',inp%nesttyp +c + print *,' ' + print *,'Values read in from atcfinfo namelist: ' + write (6,89) atcfnum,atcfname + write (6,90) atcfymdh + write (6,92) atcffreq + 89 format ('ATCF ID = ',i2,' ATCF Name = ',a4) + 90 format ('ATCF date (initial date on output atcf records) = ' + & ,i10) + 92 format ('ATCF output frequency (in hours*100) = atcffreq = ',i6) +c + print *,' ' + print *,'Values read in from trackerinfo namelist follow: ' + write (6,101) ' western boundary = westbd = ',trkrinfo%westbd + write (6,101) ' eastern boundary = eastbd = ',trkrinfo%eastbd + write (6,101) ' northern boundary = northbd = ',trkrinfo%northbd + write (6,101) ' southern boundary = southbd = ',trkrinfo%southbd + write (6,102) ' tracker type = ',trkrinfo%type + write (6,103) ' mslp threshold = mslpthresh = ' + & ,trkrinfo%mslpthresh + write (6,120) ' Flag for using backup mslp gradient check= ' + & ,'use_backup_mslp_grad_check = ' + & ,trkrinfo%use_backup_mslp_grad_check + write (6,103) ' v850 threshold = v850thresh = ' + & ,trkrinfo%v850thresh + write (6,122) ' Flag for using backup 850 mb Vt check= ' + & ,'use_backup_850_vt_check = ' + & ,trkrinfo%use_backup_850_vt_check + write (6,123) ' Max allowable distance between the ' + & ,'tracker-found fixes for mslp and 850 zeta = ' + & ,trkrinfo%max_mslp_850 + write (6,104) ' model grid type = ',trkrinfo%gridtype + write (6,101) ' Contour interval to be used = ',trkrinfo%contint + write (6,106) ' Flag for whether or not roci will be computed' + & ,' and written out for tracker-type case = ' + & ,trkrinfo%want_oci + write (6,105) ' Flag for whether or not vitals will be written ' + & ,'out = ',trkrinfo%out_vit + write (6,109) ' Flag for whether or not a land mask will be ' + & ,'used for tcgen candidate low filtering = ' + & ,trkrinfo%use_land_mask + write (6,110) ' Flag for input data type (grib or netcdf) = ' + & ,trkrinfo%inp_data_type + write (6,107) ' Flag for which GRIB version (1 or 2) the input' + & ,' data will be in = ',trkrinfo%gribver + write (6,108) ' Flag for input GRIB2 JPDTN (0 or 1) = ' + & ,trkrinfo%g2_jpdtn + write (6,112) ' Flag for input GRIB2 MSLP ID (1 or 192) = ' + & ,trkrinfo%g2_mslp_parm_id + write (6,114) ' Flag for input GRIB1 MSLP ID (102 or 130) = ' + & ,trkrinfo%g1_mslp_parm_id + write (6,116) ' Flag for input GRIB1 sfcwind level type ' + & ,'(PDS Octet 10... should be 1 or 105) = ' + & ,trkrinfo%g1_sfcwind_lev_typ + write (6,118) ' Flag for input GRIB1 sfcwind level value ' + & ,'(PDS Octets 11 & 12... usually 0 or 10) = ' + & ,trkrinfo%g1_sfcwind_lev_val + + 101 format (a31,f7.2) + 102 format (a16,a7) + 103 format (a31,f7.4) + 104 format (a19,a8) + 106 format (a46,a41,L1) + 105 format (a48,a6,a1) + 109 format (a45,a41,a1) + 110 format (a45,a6) + 107 format (a47,a19,i1) + 108 format (a39,i2) + 112 format (a43,i4) + 114 format (a45,i4) + 116 format (a41,a39,i4) + 118 format (a42,a43,i4) + 120 format (a44,a29,a1) + 122 format (a40,a26,a1) + 123 format (a36,a44,f7.1) + + print *,' ' + print *,' ' + print *,'Values read in from netcdflist namelist: ' + print *,' ' + write (6,300) netcdfinfo%num_netcdf_vars ! Total *possible* + ! number of input NetCDF variables, + ! including those that are included + ! in the input file and those that + ! are not. + write (6,370) netcdfinfo%netcdf_filename ! full path filename + write (6,301) + write (6,302) netcdfinfo%rv850name ! 850 mb rel vort + write (6,304) netcdfinfo%rv700name ! 700 mb rel vort + write (6,306) netcdfinfo%u850name ! 850 mb u-comp + write (6,308) netcdfinfo%v850name ! 850 mb v-comp + write (6,310) netcdfinfo%u700name ! 700 mb u-comp + write (6,312) netcdfinfo%v700name ! 700 mb v-comp + write (6,314) netcdfinfo%z850name ! 850 mb gp height + write (6,316) netcdfinfo%z700name ! 700 mb gp height + write (6,318) netcdfinfo%mslpname ! mslp + write (6,320) netcdfinfo%usfcname ! near-sfc u-comp + write (6,322) netcdfinfo%vsfcname ! near-sfc v-comp + write (6,324) netcdfinfo%u500name ! 500 mb u-comp + write (6,326) netcdfinfo%v500name ! 500 mb v-comp + write (6,328) netcdfinfo%tmean_300_500_name !Mean T @ 300-500 mb + write (6,330) netcdfinfo%z500name ! 500 mb gp height + write (6,332) netcdfinfo%z200name ! 200 mb gp height + write (6,334) netcdfinfo%lmaskname ! Land mask + write (6,336) netcdfinfo%z900name ! 900 mb gp height + write (6,338) netcdfinfo%z800name ! 800 mb gp height + write (6,340) netcdfinfo%z750name ! 750 mb gp height + write (6,342) netcdfinfo%z650name ! 650 mb gp height + write (6,344) netcdfinfo%z600name ! 600 mb gp height + write (6,346) netcdfinfo%z550name ! 550 mb gp height + write (6,348) netcdfinfo%z450name ! 450 mb gp height + write (6,350) netcdfinfo%z400name ! 400 mb gp height + write (6,352) netcdfinfo%z350name ! 350 mb gp height + write (6,354) netcdfinfo%z300name ! 300 mb gp height + write (6,355) netcdfinfo%time_name ! Name of time variable + ! (usually it is "time") + write (6,356) netcdfinfo%lon_name ! longitudes + write (6,358) netcdfinfo%lat_name ! latitudes + write (6,359) netcdfinfo%time_units ! This will be either "days" + ! or "hours". If it's "hours", + ! then all the time data values + ! are for hours since the initial + ! time. Same thing for "days", + ! however if it is "days", then + ! know that a value of 0.25 will + ! be the same as a 6-hour lead + ! time. + + 300 format ('Total *possible* number of input NetCDF variables,' + & ,/,' including those that are included in the input' + & ,/,' NetCDF file and those that are not = ',i4) + 370 format ('Input NetCDF filename = ',a180) + 301 format (' ',/ + & ,'List of NetCDF variables follows. A value of X ',/ + & ,'indicates the variable is not included in the ',/ + & ,'input file and no attempt will be made to read in ',/ + & ,'that variable: ',/,' ') + 302 format ('NetCDF variable name for 850 mb vort = ',a30) + 304 format ('NetCDF variable name for 700 mb vort = ',a30) + 306 format ('NetCDF variable name for 850 mb u-comp = ',a30) + 308 format ('NetCDF variable name for 850 mb v-comp = ',a30) + 310 format ('NetCDF variable name for 700 mb u-comp = ',a30) + 312 format ('NetCDF variable name for 700 mb v-comp = ',a30) + 314 format ('NetCDF variable name for 850 mb gp height = ',a30) + 316 format ('NetCDF variable name for 700 mb gp height = ',a30) + 318 format ('NetCDF variable name for MSLP = ',a30) + 320 format ('NetCDF variable name for near-sfc u-comp = ',a30) + 322 format ('NetCDF variable name for near-sfc v-comp = ',a30) + 324 format ('NetCDF variable name for 500 mb u-comp = ',a30) + 326 format ('NetCDF variable name for 500 mb v-comp = ',a30) + 328 format ('NetCDF variable name for 300-500 mb Mean T = ',a30) + 330 format ('NetCDF variable name for 500 mb gp height = ',a30) + 332 format ('NetCDF variable name for 200 mb gp height = ',a30) + 334 format ('NetCDF variable name for land-sea mask = ',a30) + 336 format ('NetCDF variable name for 900 mb gp height = ',a30) + 338 format ('NetCDF variable name for 800 mb gp height = ',a30) + 340 format ('NetCDF variable name for 750 mb gp height = ',a30) + 342 format ('NetCDF variable name for 650 mb gp height = ',a30) + 344 format ('NetCDF variable name for 600 mb gp height = ',a30) + 346 format ('NetCDF variable name for 550 mb gp height = ',a30) + 348 format ('NetCDF variable name for 450 mb gp height = ',a30) + 350 format ('NetCDF variable name for 400 mb gp height = ',a30) + 352 format ('NetCDF variable name for 350 mb gp height = ',a30) + 354 format ('NetCDF variable name for 300 mb gp height = ',a30) + 355 format ('NetCDF variable name for time = ',a30) + 356 format ('NetCDF variable name for longitudes = ',a30) + 358 format ('NetCDF variable name for latitudes = ',a30) + 359 format ('NetCDF time value (hours|days) = ',a30) + + print *,' ' + print *,' ' + print *,'Values read in from parmpreflist namelist: ' + print *,' ' + write (6,402) user_wants_to_track_zeta850 + write (6,404) user_wants_to_track_zeta700 + write (6,406) user_wants_to_track_wcirc850 + write (6,408) user_wants_to_track_wcirc700 + write (6,410) user_wants_to_track_gph850 + write (6,412) user_wants_to_track_gph700 + write (6,414) user_wants_to_track_mslp + write (6,416) user_wants_to_track_wcircsfc + write (6,418) user_wants_to_track_zetasfc + write (6,420) user_wants_to_track_thick500850 + write (6,422) user_wants_to_track_thick200500 + write (6,424) user_wants_to_track_thick200850 + + 402 format ('user_wants_to_track_zeta850= ',a2) + 404 format ('user_wants_to_track_zeta700= ',a2) + 406 format ('user_wants_to_track_wcirc850= ',a2) + 408 format ('user_wants_to_track_wcirc700= ',a2) + 410 format ('user_wants_to_track_gph850= ',a2) + 412 format ('user_wants_to_track_gph700= ',a2) + 414 format ('user_wants_to_track_mslp= ',a2) + 416 format ('user_wants_to_track_wcircsfc= ',a2) + 418 format ('user_wants_to_track_zetasfc= ',a2) + 420 format ('user_wants_to_track_thick500850= ',a2) + 422 format ('user_wants_to_track_thick200500= ',a2) + 424 format ('user_wants_to_track_thick200850= ',a2) + + print *,' ' + print *,'Values read in from phaseinfo namelist: ' + write (6,211) phaseflag,phasescheme + write (6,212) wcore_depth + 211 format ('Storm phase flag = ',a1,' Phase scheme = ',a4) + 212 format ('Storm phase, warm core depth (wcore_depth) = ',f7.2) + + print *,' ' + print *,'Values read in from structinfo namelist: ' + write (6,93) structflag + write (6,95) ikeflag + 93 format ('Structure flag = ',a1) + 95 format ('IKE flag = ',a1) + + print *,' ' + print *,'Values read in for grib file name from fnameinfo' + & ,' namelist: ' + write (6,131) gmodname + write (6,133) rundescr + write (6,135) atcfdescr + 131 format ('Model name description = gmodname = ',a4) + 133 format ('Forecast run description = rundescr = ',a40) + 135 format ('Optional ATCF / Storm name description = atcfdescr = ' + & ,a40) + + print *,' ' + print *,'Value read in for verbose output for most output:' + write (6,141) verb + 141 format ('Value read in for verbose flag = verb = ',i2) + + print *,' ' + print *,'Value read in for verbose output for grib2 output:' + write (6,142) verb_g2 + 142 format ('Value read in for GRIB2 verbose flag = verb_g2 = ',i2) + + print *,' ' + print *,'Values read in from waitinfo namelist:' + write (6,151) use_waitfor + write (6,152) wait_min_age + write (6,153) wait_min_size + write (6,154) wait_max_wait + write (6,155) wait_sleeptime + if(len_trim(per_fcst_command)>0) then + write (6,156) trim(per_fcst_command) + else +c No command specified, so disable the feature + use_per_fcst_command='n' + endif + 151 format ('Flag for input file waiting = use_waitfor = ',a1) + 152 format ('min age (time in seconds since last mod) = ' + & ,'wait_min_age = ',i8) + 153 format ('min file size in bytes = wait_min_size = ',i12) + 154 format ('max number of seconds to wait for each file = ' + & ,'wait_max_wait = ',i6) + 155 format ('number of seconds to sleep between checks = ' + & ,'wait_sleeptime = ',i6) + 156 format ('command to run after every forecast time = "',A,'"') +c + if (use_waitfor == 'y') then + if (inp%file_seq == 'multi') then + continue + else + print *,' ' + print *,'!!! ERROR: The use_waitfor flag is set to "y".' + print *,' This requires that the inp%file_seq flag be' + print *,' set to "multi", but you have specified ' + print *,' something else. ' + print *,' inp%file_seq = ',inp%file_seq + print *,' STOPPING....' + print *,' ' + STOP 95 + endif + endif +c + endif + return + end +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_fhours (ifhmax) +c +c ABSTRACT: This subroutine reads in a text file that contains the +c forecast times that will be read in. The format of the file is +c in "MMMMM", i.e., minutes, for example, for a forecast going out +c to 120h, the file would look like this: +c +c For reference, here +c are the times that +c match up with the +c minutes on the left: +c +c 1 0 0:00 +c 2 240 4:00 +c 3 270 4:30 +c 4 300 5:00 +c 5 330 5:30 +c 6 360 6:00 +c 7 600 10:00 +c 8 630 10:30 +c 9 660 11:00 +c 10 690 11:30 +c 11 720 12:00 +c 12 960 16:00 +c 13 990 16:30 +c . . . +c . . . +c . . . +c 87 7200 120:00 +c +c Note that we are now allowing for sub-hourly time intervals. +c + USE tracked_parms + USE verbose_output + + implicit none +c + integer, parameter :: iunit_fh=15 + integer itmphrs(750),itmpmins(750),input_mins(750),itmpltix(750) + integer ifhmax,inphr,inpmin,ict,i,ifa,ifma,icma,ira,inpltix,ila + real xminfract + + itmphrs = -99 + itmpmins = -99 + + if (allocated(ifhours)) deallocate (ifhours) + if (allocated(iftotalmins)) deallocate (iftotalmins) + if (allocated(ifclockmins)) deallocate (ifclockmins) + if (allocated(fhreal)) deallocate (fhreal) + if (allocated(ltix)) deallocate (ltix) + + ict = 0 + do while (.true.) + + if ( verb .ge. 3 ) then + print *,'Top of while loop in read_fhours' + endif + + read (iunit_fh,85,end=130) inpltix,inpmin + write (6,85) inpltix,inpmin + + if (inpmin >= 0 .and. inpmin < 150000) then + ict = ict + 1 + itmpltix(ict) = inpltix + itmphrs(ict) = inpmin / 60 + itmpmins(ict) = mod(inpmin,60) + input_mins(ict) = inpmin + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Input minutes not between 0 and 150000' + print *,'!!! inpmin= ',inpmin + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + if ( verb .ge. 3 ) then + print *,'readloop, ict= ',ict,' inpmin= ',inpmin + endif + + enddo + + 130 continue + + ifhmax = ict + + 85 format (i4,1x,i5) + + if ( verb .ge. 3 ) then + print *,' ' + endif + + allocate (ifhours(ifhmax),stat=ifa) + allocate (iftotalmins(ifhmax),stat=ifma) + allocate (ifclockmins(ifhmax),stat=icma) + allocate (fhreal(ifhmax),stat=ira) + allocate (ltix(ifhmax),stat=ila) + if (ifa /= 0 .or. ifma /= 0 .or. icma /= 0 .or. ira /= 0 .or. + & ila /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_fhours allocating either ifhours,' + print *,'!!! iftotalmins, ifclockmins or fhreal.' + print *,'!!! ifa = ',ifa,' ifma= ',ifma,' ira= ',ira + print *,'!!! icma= ',icma,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + do i = 1,ifhmax + + ltix(i) = itmpltix(i) + xminfract = float(itmpmins(i)) / 60. + fhreal(i) = float(itmphrs(i)) + xminfract + ifhours(i) = itmphrs(i) + ifclockmins(i) = itmpmins(i) + iftotalmins(i) = input_mins(i) + + if (i > 1) then + if (fhreal(i) > fhreal(i-1)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: In read_fhours, the time read in ' + print *,'!!! is not greater than the previous time.' + print *,'!!! i= ',i + print *,'!!! fhreal(i)= ',fhreal(i) + print *,'!!! fhreal(i-1)= ',fhreal(i-1) + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + endif + + if ( verb .ge. 3 ) then + write (6,87) i,ltix(i),iftotalmins(i),fhreal(i),ifhours(i) + & ,ifclockmins(i) + endif + + enddo + + 87 format (1x,'i= ',i3,' input lead time index= ',i4,' minutes= ' + & ,i5,' real_lead_time= ',f6.2,' clock_lead_time= ',i3,':' + & ,i2) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_tcv_card1 (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + logical(1) :: vit_file_exists + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + + ! Check to see if the TC Vitals file exists. If so, then open it + ! using the unit specified in lucard. + + inquire (file="tcvit_rsmc_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for existing, RSMC-numbered' + & ,' storms exists and will be opened with ' + & ,' unit= lucard= ',lucard + endif + + open (unit=lucard,file="tcvit_rsmc_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_rsmc_storms.txt has ' + print *,' been opened with unit= lucard= ',lucard + endif + + else + + if (trkrinfo%type == 'tracker') then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card. The fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. This TC Vitals file is needed for' + print *,'!!! a tracker case. Check to see that the ' + print *,'!!! TC Vitals file exists in this directory and' + print *,'!!! is named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING....' + print *,'!!! ' + iret=99 + return + endif + + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! NOTE: In read_tcv_card, the fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. While this TC Vitals file is ' + print *,'!!! needed for tracker cases, you are running' + print *,'!!! either a midlat or tcgen case here, and so ' + print *,'!!! that file is not needed... although you can ' + print *,'!!! run with using tc vitals for those genesis' + print *,'!!! cases if you want to. You may want to check' + print *,'!!! and make sure this is what you intend. If ' + print *,'!!! you do want to use it, the TC Vitals file ' + print *,'!!! should be in this directory and it should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! ' + endif + + endif + + endif + + ii=1 + + if (vit_file_exists) then + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + 801 continue + endif + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + + maxstorm = numtcv + + if (maxstorm > 0) then + continue + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING...' + print *,'!!! ' + iret=99 + return + endif + endif + + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! Stopping....' + print *,' ' + endif + + iret = 99 + return + + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card opening rsmc TC vitals' + print *,'!!! file named tcvit_rsmc_storms.txt. A file with' + print *,'!!! that name needs to be in your working directory.' + print *,'!!! It should contain the input TC vitals for ' + print *,'!!! already-existing storms that have rsmc-issued' + print *,'!!! storm IDs.' + endif + + iret = 97 + return + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine read_gen_vitals1(lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + logical(1) :: vit_file_exists + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + + ! Check to see if the genesis TC Vitals file exists. If so, then + ! open it using the unit specified in lgvcard. + + inquire (file="tcvit_genesis_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for genesis' + & ,' storms exists and will be opened with ' + & ,' unit= lgvcard= ',lgvcard + endif + + open (unit=lgvcard,file="tcvit_genesis_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_genesis_storms.txt has ' + print *,' been opened with unit= lgvcard= ',lgvcard + endif + + endif + + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + + ii = numtcv + 1 + + if (vit_file_exists) then + + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1 + & ,1x,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + endif + + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals opening genesis vitals' + print *,'!!! file named tcvit_genesis_storms.txt' + endif + + iret = 97 + return + + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just read the +c grib file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE tracked_parms; USE inparms + USE verbose_output; USE params; USE grib_mod + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + + type(gribfield) :: gfld,prevfld,holdgfld + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1), allocatable :: lb(:) + logical :: unpack=.true. + logical :: open_grb=.false. + CHARACTER(len=8) :: pabbrev + integer,dimension(200) :: jids,jpdt,jgdt + integer, parameter :: jf=40000000 + integer :: listsec1(13) + integer pdt_4p0_vert_level,pdt_4p0_vtime + real xhold,xlondiff,xlatdiff,temp,firstval,lastval + real, allocatable :: f(:) + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer jpds(200),jgds(200),igetpds(200),igetgds(200) + integer, intent(in) :: ifh + integer, intent(out) :: imax,jmax + integer iia,ija,ila,midi,midj,i,j,iix,jix,ifa,iret + integer iscanflag,iggret,kf,k,lugb,lugi,jskp,jdisc + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 + + iggret = 0 + + allocate (lb(jf),stat=ila); allocate (f(jf),stat=ifa) + if (ila /= 0 .or. ifa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating either lb or f' + print *,'!!! ila = ',ila,' ifa= ',ifa + endif + iggret = 97 + return + endif + + if (trkrinfo%gribver == 2) then + + ! Search for a record from a GRIB2 file + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 ! meteorological products + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for Temperature or GP Height by production template.... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + ! Request a record on a lat/lon grid. + + jgdtn = 0 + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpdt(8) = 0 + jpdt(9) = iftotalmins(ifh) + else + jpdt(8) = 1 + jpdt(9) = ifhours(ifh) + endif + + if (verb >= 3) then + print *,'before getgb2 call, lugb= ',lugb,' lugi= ',lugi + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + if ( iret.ne.0) then + print *,' ' + print *,' ERROR: getgb2 error in getgridinfo = ',iret + print *,' FATAL ERROR: cannot proceed without info ' + print *,' from getgridinfo. STOPPING....' + stop 95 + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if ( verb_g2 .ge. 1 ) then + print *,' ' + print *,' -- BEGIN getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'PDT num= gfld%ipdtnum= ',gfld%ipdtnum + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + imax = gfld%igdtmpl(8) + jmax = gfld%igdtmpl(9) + dx = float(gfld%igdtmpl(17))/1.e6 + dy = float(gfld%igdtmpl(17))/1.e6 + kf = gfld%ngrdpts + + holdgfld = gfld + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + + endif + + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' -- END getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' ' + print *,' ' + + endif + + need_to_flip_lons = .false. + + iscanflag = gfld%igdtmpl(19) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(gfld%igdtmpl(12))/1.e6 + glatmax = float(gfld%igdtmpl(15))/1.e6 + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(gfld%igdtmpl(15))/1.e6 + glatmax = float(gfld%igdtmpl(12))/1.e6 + need_to_flip_lats = .false. + endif + + glonmin = float(gfld%igdtmpl(13))/1.e6 + glonmax = float(gfld%igdtmpl(16))/1.e6 + + if (verb .ge. 3) then + print *,'In getgridinfo: glatmin= ',glatmin + print *,' glatmax= ',glatmax + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + + else + + !------------------------------------------ + ! Search for a record from a GRIB1 file + !------------------------------------------ + + jpds = -1 + jgds = -1 + + jgds(1) = 0 ! Request a record that's on a lat/lon grid + + if ( verb .ge. 3 ) then + print *,'before getgb in getgridinfo, ifh= ',ifh + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* Forecast hour: ',i4,':',i2.2) + print *,' ifhours(ifh)= ',ifhours(ifh) + print *,' iftotalmins(ifh)= ',iftotalmins(ifh) + endif + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + j=0 + +c jpds(14) = 0 ! test +c + write(*,980) jpds(1),jpds(2) + write(*,981) jpds(3),jpds(4) + write(*,982) jpds(5),jpds(6) + write(*,983) jpds(7),jpds(8) + write(*,984) jpds(9),jpds(10) + write(*,985) jpds(11),jpds(12) + write(*,986) jpds(13),jpds(14) + write(*,987) jpds(15),jpds(16) + write(*,988) jpds(17),jpds(18) + write(*,989) jpds(19),jpds(20) + write(*,990) jpds(21),jpds(22) + write(*,991) jpds(23),jpds(24) + write(*,992) jpds(25) + write(*,880) jgds(1),jgds(2) + write(*,881) jgds(3),jgds(4) + write(*,882) jgds(5),jgds(6) + write(*,883) jgds(7),jgds(8) + write(*,884) jgds(9),jgds(10) + write(*,885) jgds(11),jgds(12) + write(*,886) jgds(13),jgds(14) + write(*,887) jgds(15),jgds(16) + write(*,888) jgds(17),jgds(18) + write(*,889) jgds(19),jgds(20) + write(*,890) jgds(21),jgds(22) + + 980 format(' jpds(1) = ',i7,' jpds(2) = ',i7) + 981 format(' jpds(3) = ',i7,' jpds(4) = ',i7) + 982 format(' jpds(5) = ',i7,' jpds(6) = ',i7) + 983 format(' jpds(7) = ',i7,' jpds(8) = ',i7) + 984 format(' jpds(9) = ',i7,' jpds(10) = ',i7) + 985 format(' jpds(11) = ',i7,' jpds(12) = ',i7) + 986 format(' jpds(13) = ',i7,' jpds(14) = ',i7) + 987 format(' jpds(15) = ',i7,' jpds(16) = ',i7) + 988 format(' jpds(17) = ',i7,' jpds(18) = ',i7) + 989 format(' jpds(19) = ',i7,' jpds(20) = ',i7) + 990 format(' jpds(21) = ',i7,' jpds(22) = ',i7) + 991 format(' jpds(23) = ',i7,' jpds(24) = ',i7) + 992 format(' jpds(25) = ',i7) + 880 format(' jgds(1) = ',i7,' jgds(2) = ',i7) + 881 format(' jgds(3) = ',i7,' jgds(4) = ',i7) + 882 format(' jgds(5) = ',i7,' jgds(6) = ',i7) + 883 format(' jgds(7) = ',i7,' jgds(8) = ',i7) + 884 format(' jgds(9) = ',i7,' jgds(10) = ',i7) + 885 format(' jgds(11) = ',i7,' jgds(12) = ',i7) + 886 format(' jgds(13) = ',i7,' jgds(14) = ',i7) + 887 format(' jgds(15) = ',i7,' jgds(16) = ',i7) + 888 format(' jgds(17) = ',i7,' jgds(18) = ',i7) + 889 format(' jgds(19) = ',i7,' jgds(20) = ',i7) + 890 format(' jgds(20) = ',i7,' jgds(22) = ',i7) + + print *,'lugb= ',lugb,' lugi= ',lugi + print *,'before ggi getgb jpds(14) = ',jpds(14) + print *,'before ggi getgb jgds(1) = ',jgds(1) + + call getgb(lugb,lugi,jf,j,jpds,jgds, + & kf,k,igetpds,igetgds,lb,f,iret) + + if (iret.ne.0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo calling getgb' + print *,'!!! Return code from getgb = iret = ',iret + endif + + iggret = iret + else + iggret=0 + imax = igetgds(2) + jmax = igetgds(3) + dx = float(igetgds(9))/1000. + dy = float(igetgds(10))/1000. + endif + +c write(*,780) igetpds(1),igetpds(2) +c write(*,781) igetpds(3),igetpds(4) +c write(*,782) igetpds(5),igetpds(6) +c write(*,783) igetpds(7),igetpds(8) +c write(*,784) igetpds(9),igetpds(10) +c write(*,785) igetpds(11),igetpds(12) +c write(*,786) igetpds(13),igetpds(14) +c write(*,787) igetpds(15),igetpds(16) +c write(*,788) igetpds(17),igetpds(18) +c write(*,789) igetpds(19),igetpds(20) +c write(*,790) igetpds(21),igetpds(22) +c write(*,791) igetpds(23),igetpds(24) +c write(*,792) igetpds(25) +c write(*,680) igetgds(1),igetgds(2) +c write(*,681) igetgds(3),igetgds(4) +c write(*,682) igetgds(5),igetgds(6) +c write(*,683) igetgds(7),igetgds(8) +c write(*,684) igetgds(9),igetgds(10) +c write(*,685) igetgds(11),igetgds(12) +c write(*,686) igetgds(13),igetgds(14) +c write(*,687) igetgds(15),igetgds(16) +c write(*,688) igetgds(17),igetgds(18) +c write(*,689) igetgds(19),igetgds(20) +c write(*,690) igetgds(21),igetgds(22) +c +c 780 format(' kpds(1) = ',i7,' kpds(2) = ',i7) +c 781 format(' kpds(3) = ',i7,' kpds(4) = ',i7) +c 782 format(' kpds(5) = ',i7,' kpds(6) = ',i7) +c 783 format(' kpds(7) = ',i7,' kpds(8) = ',i7) +c 784 format(' kpds(9) = ',i7,' kpds(10) = ',i7) +c 785 format(' kpds(11) = ',i7,' kpds(12) = ',i7) +c 786 format(' kpds(13) = ',i7,' kpds(14) = ',i7) +c 787 format(' kpds(15) = ',i7,' kpds(16) = ',i7) +c 788 format(' kpds(17) = ',i7,' kpds(18) = ',i7) +c 789 format(' kpds(19) = ',i7,' kpds(20) = ',i7) +c 790 format(' kpds(21) = ',i7,' kpds(22) = ',i7) +c 791 format(' kpds(23) = ',i7,' kpds(24) = ',i7) +c 792 format(' kpds(25) = ',i7) +c 680 format(' kgds(1) = ',i7,' kgds(2) = ',i7) +c 681 format(' kgds(3) = ',i7,' kgds(4) = ',i7) +c 682 format(' kgds(5) = ',i7,' kgds(6) = ',i7) +c 683 format(' kgds(7) = ',i7,' kgds(8) = ',i7) +c 684 format(' kgds(9) = ',i7,' kgds(10) = ',i7) +c 685 format(' kgds(11) = ',i7,' kgds(12) = ',i7) +c 686 format(' kgds(13) = ',i7,' kgds(14) = ',i7) +c 687 format(' kgds(15) = ',i7,' kgds(16) = ',i7) +c 688 format(' kgds(17) = ',i7,' kgds(18) = ',i7) +c 689 format(' kgds(19) = ',i7,' kgds(20) = ',i7) +c 690 format(' kgds(20) = ',i7,' kgds(22) = ',i7) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,' dx= ',dx,' dy= ',dy + endif + + +c ---------------------------------------------------------------- +c Get boundaries of the data grid. NOTE: gds(4) is referred to in +c GRIB documenatation as the "Latitude of origin", which might +c imply "minimum Latitude". However, for the grids that we'll be +c using in this program, the "Latitude of origin" will be listed +c under gds(4) as the northernmost point (eg., in MRF, +c gds(4) = 90), so for this program, use gds(4) as your max lat, +c and gds(7) as your min lat. However, in case NCEP, UKMET or +c ECMWF change their convention and begin flipping their grids, a +c check is made to make sure that the max lat is not less than the +c min lat. +c +c BUGFIX (August, 2001): It is possible to have an input grid +c which goes from south to north (such as NAVGEM). In this case, +c we flip the data in subroutine conv1d2d_real. However, the max +c and min latitudes listed in the GRIB GDS will be confused, so we +c need to check the value of the GRIB scanning mode flag here. + + need_to_flip_lons = .false. + + iscanflag = igetgds(11) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(igetgds(4))/1000. + glatmax = float(igetgds(7))/1000. + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(igetgds(7))/1000. + glatmax = float(igetgds(4))/1000. + need_to_flip_lats = .false. + endif + + glonmin = float(igetgds(5))/1000. + glonmax = float(igetgds(8))/1000. + + endif + +c After this point in this subroutine, nothing is GRIB1 / GRIB2 +c specific, so it does not need to be within the if/then +c statement above that differentiated between GRIB / GRIB2. + +c17Jul2014 if (glonmin < 0.0) glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax < 0.0) glonmax = 360. - abs(glonmax) + + if (glonmin >= 0.0 .and. glonmax >= 0.0) then + if (glonmin > glonmax) then + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Badly notated longitude boundaries in ' + print *,' GRIB PDS, because the min longitude ' + print *,' (glonmin) is greater than the max ' + print *,' longitude (glonmax) where both longitudes' + print *,' are greater than 0.' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + print *,' !!! STOPPING....' + stop 98 + endif + endif + elseif (glonmin < 0.0 .and. glonmax >= 0.0) then + ! An example of this is the MPAS data, which starts and ends + ! at the dateline and is specified as glonmin=-179.875, + ! glonmax=179.875. Convert to be positive and go from + ! 180.125 to 539.875. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0, glonmax > 0, so glonmin' + print *,' will be converted to be > 0 and 360 will' + print *,' be added to glonmax.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. + abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin < 0.0 .and. glonmax < 0.0) then + ! Examples of this are GFDL and HWRF. In this case, make + ! both glonmin and glonmax positive. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0 and glonmax < 0, so both' + print *,' will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin >= 0.0 .and. glonmax < 0.0) then + ! An example of this is the GFS data, which goes from + ! glonmin=0.0 to glonmax=-0.5. Convert it here to go + ! from glonmin=0.0 to glonmax=359.5 + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is >= 0 and glonmax < 0, so' + print *,' glonmax will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + endif + +c17Jul2014 if (glonmin < 0.0) then +c17Jul2014 glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax <= 0.0) then +c17Jul2014 glonmax = 360. - abs(glonmax) +c17Jul2014 else +c17Jul2014 glonmax = 360 + abs(glonmax) +c17Jul2014 endif +c17Jul2014 endif + + if (glatmax < glatmin) then + temp = glatmax + glatmax = glatmin + glatmin = temp + endif + + if (glonmin > 200.0 .and. glonmin <= 360.) then + if (glonmax < 50.) then + ! Likely GM-wrapping in current record + glonmax = glonmax + 360. + endif + endif +c + if ( verb .ge. 3 ) then + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + print *,' ' + print *,'NOTE: For regional grids, valid data points might' + print *,'NOT extend all the way to the gds-defined grid ' + print *,'boundary, due to the fact that data have been ' + print *,'interpolated from a NPS or Lamb-Conf grid onto a ' + print *,'lat/lon grid. This program checks the logical ' + print *,'bitmap for valid data points, but just keep this in' + print *,'mind if trying to debug errors that occur near the' + print *,'grid boundaries for regional models.' + endif + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glat)) deallocate(glat) + if (allocated(glon)) deallocate(glon) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + endif + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + + iggret = 96 + return + endif + + do j=1,jmax + glat(j) = glatmax - (j-1)*dy + enddo + do i=1,imax + glon(i) = glonmin + (i-1)*dx + enddo + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + +c -------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have +c forgotten to change the input grid bounds from a global grid +c run). Modify the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just query the +c netcdf file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE inparms + USE verbose_output; USE netcdf_parms + + implicit none +c + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (datecard) inp + + logical(1) :: need_to_flip_lats,need_to_flip_lons + real xhold,xlondiff,xlatdiff + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer iscanflag,iggret + integer, intent(in) :: ncfile_id + integer, intent(out) :: imax,jmax + integer :: iia,ija,midi,midj,i,j,iix,jix +c + + iggret = 0 + + call get_ncdim1(ncfile_id,netcdfinfo%lon_name,imax) + call get_ncdim1(ncfile_id,netcdfinfo%lat_name,jmax) + + if (allocated(tmplon)) deallocate (tmplon) + if (allocated(tmplat)) deallocate (tmplat) + allocate (tmplon(imax),stat=iia) + allocate (tmplat(jmax),stat=ija) + if (iia /= 0 .or. ija /= 0) then + print *,' ' + print *,'!!! ERROR in sub getgridinfo_netcdf allocating arrays.' + print *,'!!! iia = ',iia,' ija= ',ija + iggret = 94 + return + endif + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + endif + + call get_var1_double (ncfile_id,netcdfinfo%lon_name,imax,tmplon) + call get_var1_double (ncfile_id,netcdfinfo%lat_name,jmax,tmplat) + +c Compute the dx and dy by picking values out of the middle of +c the lat and lon arrays.... + + midi = imax/2 + midj = jmax/2 + + dx = abs(tmplon(midi) - tmplon(midi-1)) + dy = abs(tmplat(midj) - tmplat(midj-1)) + + if (verb .ge. 1) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,'dx= ',dx,' dy= ',dy + print *,' ' + write (6,112) midi,dx + write (6,113) midj,dy + + 112 format(1x,' DX: midi= ',i4,' dx= ',f8.4) + 113 format(1x,' DY: midj= ',i4,' dy= ',f8.4) + endif + + +c ------------------------------------------------------------------ +c Get boundaries of the data grid. Note that it is possible to have +c an input grid which goes from south to north (in fact, it appears +c that many NetCDF files are constructed this way). Keep in mind, +c however, that the tracker has been written such that point (1,1) +c should be the upper-leftmost point on the grid, while point +c (imax,jmax) should be the lower-rightmost point. If we check and +c find that we're dealing with data that instead starts from the +c south and increases northward, we flip the data in subroutine +c conv1d2d_real. Similarly here, we make sure to test so that when +c we are done in this routine, glatmax refers to the northernmost +c latitude and glatmin the southernmost latitude. + + if (tmplon(imax) > tmplon(1)) then + glonmin = tmplon(1) + glonmax = tmplon(imax) + else + glonmin = tmplon(imax) + glonmax = tmplon(1) + endif + + if (tmplat(1) > tmplon(jmax)) then + glatmax = tmplat(1) + glatmin = tmplat(jmax) + else + glatmax = tmplat(jmax) + glatmin = tmplat(1) + endif + + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glon)) deallocate (glon) + if (allocated(glat)) deallocate (glat) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + iggret = 96 + return + endif + + ! If the lat or lon grids are flipped (i.e., the lats increase + ! from south to north, or the lons increase westward), then we + ! will need to flip both the data arrays as well as the arrays + ! that are holding the values of the lats and lons.... + + need_to_flip_lats = .false. + need_to_flip_lons = .false. + + if (tmplat(1) > tmplon(jmax)) then + do j=1,jmax + glat(j) = tmplat(j) + enddo + else + do j=1,jmax + jix = jmax - j + 1 + glat(jix) = tmplat(j) + enddo + need_to_flip_lats = .true. + endif + + if (tmplon(imax) > tmplon(1)) then + do i=1,imax + glon(i) = tmplon(i) + enddo + else + do i=1,imax + iix = imax - i + 1 + glon(iix) = tmplon(i) + enddo + need_to_flip_lons = .true. + endif + +c do i = 1,imax +c print *,'i= ',i,' glon(i)= ',glon(i) +c enddo +c do j = 1,jmax +c print *,'j= ',j,' glat(j)= ',glat(j) +c enddo + +c --------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have forgotten +c to change the input grid bounds from a global grid run). Modify +c the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) +c +c ABSTRACT: The purpose of this subroutine is to read the "time" +c dimension and "time data" from the NetCDF file so that we know +c how many time levels there are and what those time levels are. +c One reason for doing this is that some models, like the GFDL +c FV3, do not output hour 0 data, so we need to check this first +c before running through the tracking processing for the various +c hours. We will take the list of hours read in here directly from +c the NetCDF file and compare that against the *requested* list of +c forecast hours that the user has entered. The user might not be +c aware that there is no hour 0 data for a given model. We compare +c these two lists of forecast hours and then write a message if +c there is a lead time that is not in the NetCDF file. +c +c INPUT: +c ncfile character name of NetCDF file +c ncfile_id integer id associated with NetCDF file after open +c ifhmax integer max number of lead times that the user has +c requested on the input lead times data file. This +c value was set in subroutine read_fhours. +c netcdfinfo variable of user-defined type netcdfstuff (from +c module netcdf_parms). +c +c OUTPUT: +c ncfile_tmax integer max number of lead times that are in the +c NetCDF file, as read in from this subroutine +c ncfile_has_hour0 character flag (y|n) that tells whether or not +c the input NetCDF data file actually has an hour0 +c record in it or not. +c + USE netcdf_parms; USE tracked_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo + + character :: ncfile*180,ncfile_has_hour0*1,match_check*1 + integer, intent(in) :: ncfile_id + integer, intent(out) :: ncfile_tmax + integer :: infta,k,m,n,ifhmax,irnhret,usertime +c + + irnhret = 0 + ncfile_has_hour0 = 'n' + + !----------------------------------------------------------- + ! First read the NetCDF file to get the number of time levels, + ! which will be returned in "ncfile_tmax".... + !----------------------------------------------------------- + + print *,' ' + print *,'in read_netcdf_hours...' + print *,'ncfile_id= ',ncfile_id + print *,'netcdfinfo%time_name= ',netcdfinfo%time_name + print *,'ncfile_tmax= ',ncfile_tmax + + call get_ncdim1(ncfile_id,netcdfinfo%time_name,ncfile_tmax) + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + print *,'Num netcdf time levs= ncfile_tmax= ',ncfile_tmax + endif + + if (allocated(netcdf_file_time_values)) then + deallocate (netcdf_file_time_values) + endif + + allocate (netcdf_file_time_values(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating' + print *,'!!! netcdf_file_time_values array. infta = ',infta + irnhret = 94 + return + endif + + + !----------------------------------------------------------- + ! Now read in the actual time values that are stored in the + ! NetCDF file.... + !----------------------------------------------------------- + + call get_var1_double (ncfile_id,netcdfinfo%time_name,ncfile_tmax + & ,netcdf_file_time_values) + + if (verb .ge. 1) then + do k = 1,ncfile_tmax + print *,'k= ',k,' netcdf_file_time_values(k)= ' + & ,netcdf_file_time_values(k) + enddo + endif + + !------------------------------------------------------------ + ! Now convert the NetCDF time values into minutes in order to + ! be able to compare with the user-requested list of lead + ! times. Remember that the NetCDF lead times will be listed + ! either as hours or as fractions of days. + !------------------------------------------------------------ + + if (allocated(nctotalmins)) then + deallocate (nctotalmins) + endif + + allocate (nctotalmins(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating ' + print *,'!!! nctotalmins array. infta = ',infta + irnhret = 94 + return + endif + + do k = 1,ncfile_tmax + + if (netcdfinfo%time_units == 'hours') then + nctotalmins(k) = int(netcdf_file_time_values(k)) * 60 + elseif (netcdfinfo%time_units == 'days') then + nctotalmins(k) = int(netcdf_file_time_values(k) * 60. * 24.) + else + print *,' ' + print *,'!!! ERROR: In read_netcdf_hours, the value of' + print *,' netcdfinfo%time_units is neither hours nor days.' + print *,' netcdfinfo%time_units= ',netcdfinfo%time_units + print *,' STOPPING....' + print *,' ' + stop 99 + endif + + if (verb .ge. 1) then + write (6,71) k,netcdf_file_time_values(k),nctotalmins(k) + endif + + enddo + + 71 format (1x,i5,' netcdf_file_time_values(k)= ',f8.4 + & ,' nctotalmins(k)= ',i10) + + !------------------------------------------------------------ + ! Now go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match + ! the two lists up. The big one to watch out for is whether + ! or not the NetCDF file actually has an hour 0 lead time. + !------------------------------------------------------------ + + userloop: do n = 1,ifhmax + + usertime = iftotalmins(n) + + match_check = 'n' + + netcdfloop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + if (verb .ge. 1) then + print *,'+++ Time match for usertime= ',usertime + endif + match_check = 'y' + endif + + enddo netcdfloop + + if (match_check == 'n') then + + if (usertime == 0) then + print *,' ' + print *,'Warning: For a NetCDF file, the user has requested' + print *,'to read in an hour 0 file, however a scan of the' + print *,'time data values in the NetCDF file indicates' + print *,'that there is no hour 0 data in this file. ' + print *,'We will substitute either missing values or ' + print *,'the values from the TC Vitals data in the ' + print *,'hour 0 record and then start searching at the ' + print *,'next lead time.' + ncfile_has_hour0 = 'n' + else + print *,' ' + print *,'!!! ERROR: For a NetCDF file, the user has' + print *,' requested to process a particular lead time that' + print *,' does not exist in the NetCDF list of time ' + print *,' values.' + print *,' n= ',n + print *,' usertime= iftotalmins(n)= ',iftotalmins(n) + print *,' STOPPING....' + stop 99 + endif + + elseif (match_check == 'y') then + + if (usertime == 0) then + if (verb .ge. 1) then + print *,' ' + print *,'+++ For the input NetCDF file, an hour0 data ' + print *,' record exists in the data file.' + endif + ncfile_has_hour0 = 'y' + endif + + endif + + enddo userloop +c + return + end +c +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_valid_point (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) +c +c ABSTRACT: This subroutine checks to see if the input lat/lon +c point is associated with four surrounding (i,j) locations that +c have valid data. The writing of this routine was prompted by the +c HFIP project in February, 2009. Some of their high resolution +c data for their inner nests contained grids that had been rotated +c from native map projections to regular lat/lon grids, but that +c rotation left "empty" spots on the lat/lon grid where there is +c no data. Then when searching in find_maxmin, we were running +c barnes iterations from these lat/lon locations where there was +c no data, which would give artificially low values at those +c lat/lon locations (because the barnes scheme would only include +c points that were relatively far away where there was valid data). +c So in this routine, we call subroutine fix_latlon_to_ij in order +c to get the nearest (i,j) coordinates, and then we check all of +c these points to make sure that valid data exist. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing in i-direction +c dy grid spacing in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c rlatt,rlont input lat/lon about which we will check the +c surrounding (i,j) locations for valid data. +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c icvpret return code from this routine. A value of 0 means that +c all is okay and the input point is surrounded by valid +c data. + + USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,ifix,jfix + integer ifilret,icvpret + character(*) cmaxmin + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real rlont,rlatt,xdum,gridpoint_maxmin + real dx,dy,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +c + call fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt + & ,xdum,ifix,jfix,gridpoint_maxmin,'checker' + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) + + if (ifilret /= 0) then + icvpret = 99 + return + endif + + if (valid_pt(ifix,jfix)) then + icvpret = 0 + else + icvpret = 99 + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.025) then + grfact = 20 + else if (grdspc > 0.025 .and. grdspc <= 0.05) then + grfact = 12 + else if (grdspc > 0.05 .and. grdspc <= 0.10) then + grfact = 6 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif + + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (1*grfact) + iend = ipfix + (2*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (2*grfact) + iend = ipfix + (1*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (1*grfact) + iend = ipfix + (1*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (2*grfact) + jend = jpfix + (1*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (1*grfact) + jend = jpfix + (2*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (1*grfact) + jend = jpfix + (1*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +c print *,' End of fix_latlon_to_ij, gridpoint_maxmin = ' +c & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine rvcal (imax,jmax,dlon,dlat,z,vp) +c +c ABSTRACT: This routine calculates the relative vorticity (zeta) +c from u,v on an evenly-spaced lat/lon grid. Centered finite +c differences are used on the interior points and one-sided +c differences are used on the boundaries. +c +c NOTE: There are 3 critical arrays in this subroutine, the first +c being zeta and the 2nd and 3rd being u and v. There is a +c critical difference in the array indexing for the levels. For +c zeta, the array is dimensioned with levels from 1 to 3, with +c 1 = 850, 2 = 700, 3 = sfc. However, there is an extra level +c for the winds, such that the level dimension goes 1 = 850, +c 2 = 700, 3 = 500, 4 = sfc. So we need to adjust for that in +c this routine. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE trig_vals; USE grid_bounds + USE verbose_output + + implicit none + + dimension cosfac(jmax),tanfac(jmax) + real tmpzeta(imax,jmax) + real xlondiff,xlatdiff,dlon,dlat,dfix + real dlat_edge,dlat_inter,dlon_edge,dlon_inter + real rlat(jmax),cosfac,tanfac + integer z,iscanflag,nlat,nlon,i,j,imax,jmax,w + integer ii,jj + logical(1) vp(imax,jmax) + +c -------------------------- + +c Figure out what level of data we have and what the array +c indices should be. + + if (z == 1) then + ! z = 1 for 850 mb zeta, w = 1 for 850 mb winds + w = 1 + else if (z == 2) then + ! z = 2 for 700 mb zeta, w = 2 for 700 mb winds + w = 2 + else if (z == 3) then + ! z = 3 for sfc zeta, w = 4 for sfc (10m) winds + w = 4 + endif + +c Calculate grid increments for interior and edge points. + +c IMPORTANT: If dtk is defined in module trig_vals in km, then +c we need to multiply by 1000 here to get meters. If it's defined +c as meters, just let it be. Since the wind values are given in +c meters, that's why we need the dlon values to be in meters. + + if (dtk < 750.) then ! chances are, dtk was defined as km + dfix = 1000.0 + else ! dtk was already defined as meters + dfix = 1.0 + endif + + dlon_edge = dtk * dfix * dlon ! Di dist over 1 grid pt + dlat_edge = dtk * dfix * dlat ! Dj dist over 1 grid pt + dlon_inter = dtk * dfix * 2.0 * dlon ! Di dist over 2 grid pts + dlat_inter = dtk * dfix * 2.0 * dlat ! Dj dist over 2 grid pts + + +c Calculate required trig functions. These are functions of +c latitude. Remember that the grid must go from north to south. +c This north-to-south requirement has +c already been checked in subroutine getgridinfo. If necessary, +c any flipping of the latitudes was done there, and flipping of +c the data, again if necessary, was done in subroutine getdata. + + do j=2,jmax-1 + rlat(j) = glatmax - ((j-1) * dlat) + cosfac(j) = cos(dtr*rlat(j)) + tanfac(j) = (tan(dtr*rlat(j)))/erad + enddo + +c Set trig factors at end points to closest interior point +c to avoid a singularity if the domain includes the poles, +c which it will for the global grids (MRF, GDAS, GFS, UKMET,NCE) + + cosfac(1) = cosfac(2) + tanfac(1) = tanfac(2) + cosfac(jmax) = cosfac(jmax-1) + tanfac(jmax) = tanfac(jmax-1) + +c NOTE: These next bits of vorticity calculation code assume that +c the input grid is oriented so that point (1,1) is the upper +c left-most (NW) and point (imax,jmax) is the lower right- +c most point. Any other grids will probably crash the +c program due to array out of bounds errors. +c NOTE: Before each calculation is done, the logical array is +c checked to make sure that all the data points in this +c calculation have valid data (ie., that the points are not +c outside a regional model's boundaries). +c +c !!! IMPORTANT NOTE: While testing this, I uncovered a bug, which was +c that I had the "j+1" and "j-1" reversed. Just from a physical +c understanding, the du/dy term at a point is calculated by taking +c the u value north of the point minus the u value south of the +c point. Intuitively, this is u(j+1) - u(j-1). However, we have +c designed this program to have the northernmost point as +c the beginning of the grid (i.e., for the global grids, j=1 at 90N, +c and j increases southward). Thus, if you would do u(j+1) - +c u(j-1), you would actually be taking the u value south of the +c point minus the u value north of the point, EXACTLY THE OPPOSITE +c OF WHAT YOU WANT. Therefore, the vorticity calculations have +c been changed so that we now have u(j-1) - u(j+1). +c +c UPDATE FEB 2009: With limited domain grids that have missing +c data on them (such as you would have for a grid that has been +c converted from a non-lat/lon grid to a lat/lon grid), we were +c running into problems below with the setting of zeta values to +c a missing value of -999. In place of this, the easiest thing to +c do is to simply assign a value of the background coriolis value +c to that point. No, this is not correct, but it is the easiest +c workaround for this right now. Setting it to zero would be too +c far off. Setting it to the coriolis component has a net effect +c of not having much impact on the barnes scheme result. +c +c --------------- +c Interior points +c --------------- + + if ( verb .ge. 3 ) then + print *,'Just before inter rvcalc, dlon_inter = ',dlon_inter + & ,' dlat_inter = ',dlat_inter + endif + + do j=2,jmax-1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1) .and. vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo + enddo +c +c ----------------------------- +c Bottom (Southernmost) points +c ----------------------------- +c + j=jmax + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------- +c Top (Northernmost) points +c -------------------------- +c + j=1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c ------------------------------- +c Left edge (Westernmost) points +c ------------------------------- +c + i=1 + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------------- +c Right edge (Easternmost) points +c -------------------------------- +c + i=imax + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c --------- +c SW corner +c --------- + i=1 + j=jmax + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i+1,j,w)-v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NW corner +c --------- + i=1 + j=1 + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NE corner +c --------- + i=imax + j=1 + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c SE corner +c --------- + i=imax + j=jmax + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i,j,w)-v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + do ii=1,imax + do jj=1,jmax + tmpzeta(ii,jj) = zeta(ii,jj,z) * 1.e5 + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine thickness_calc (imax,jmax,vp) +c +c ABSTRACT: This routine calculates the thicknesses for three +c different layers: 200-500, 500-850 and 200-850 mb. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE verbose_output + + implicit none + + integer i,j,layer,upper,lower,imax,jmax + logical(1) vp(imax,jmax) + +c -------------------------- + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 +c +c The array indices for the levels for the 4 different GP height +c arrays (as assigned in subroutine getdata) are as follows: +c 1: 850 mb +c 2: 700 mb +c 3: 500 mb +c 4: 200 mb + + + do layer = 1,3 + + select case (layer) + case (1); upper=3; lower=1; + case (2); upper=4; lower=3; + case (3); upper=4; lower=1; + end select + + do j = 1,jmax + do i = 1,imax + + if (vp(i,j)) then + thick(i,j,layer) = hgt(i,j,upper) - hgt(i,j,lower) + else + thick(i,j,layer) = -999.0 + endif + + enddo + enddo + + enddo +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine first_ges_center (imax,jmax,dx,dy,cparm,fxy + & ,cmaxmin,trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) +c +c ABSTRACT: This subroutine scans an array and picks out areas of +c max or min, then loads those center positions into the first- +c guess lat & lon arrays to be used by subroutine tracker for +c locating the very specific low center positions. +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c dx Grid spacing in i-direction for the input grid +c dy Grid spacing in j-direction for the input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c finf Logical. Field of influence. Dimension same as fxy +c cmaxmin Char string to indicate if search is for a max or a min +c trkrinfo Derived type that holds/describes various tracker parms, +c including the contour interval to be used +c ifh Index for the forecast hour +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c contour_info Type cint_stuff from module contours. Contains +c contour information +c +c OUTPUT: +c maxmini Integer array containing i-indeces of max/min locations +c maxminj Integer array containing j-indeces of max/min locations +c ifgcret return code from this subroutine +c +c OTHER: +c storm Contains the tcvitals for the storms (module def_vitals) + + USE trkrparms; USE grid_bounds; USE set_max_parms; USE def_vitals + USE contours; USE tracked_parms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,n,isstart,ifamret,ibeg,jbeg,iend,jend + integer ifh,maxstorm,imax,jmax,itemp,ifgcret + integer stormct,oldstormct,mm + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + character(*) cparm,cmaxmin + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real dmax,dmin,dx,dy,dbuffer,tmp + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of first_ges_center *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for new lows at hour ',i4,':',i2.2) + print *,'*-------------------------------------------------*' + endif + + +c First check the user-supplied grid boundaries to see if we will +c scan the entire array or just a portion of it. + + if (trkrinfo%northbd < -998.0 .or. trkrinfo%southbd < -998.0 .or. + & trkrinfo%westbd < -998.0 .or. trkrinfo%eastbd < -998.0) then + ! User did not specify a subgrid, so scan the whole domain + ibeg = 1 + iend = imax + jbeg = 1 + jend = jmax + else + +c if (trkrinfo%westbd > 360.0 .or. trkrinfo%eastbd < 0.0 .or. +c & trkrinfo%westbd < 0.0 .or. + + if (trkrinfo%westbd > 360.0 .or. + & trkrinfo%northbd > 90.0 .or. trkrinfo%northbd <-90.0 .or. + & trkrinfo%southbd > 90.0 .or. trkrinfo%southbd <-90.0 .or. + & trkrinfo%westbd >= trkrinfo%eastbd .or. + & trkrinfo%southbd >= trkrinfo%northbd) then + + if (trkrinfo%westbd > trkrinfo%eastbd) then + + if (trkrinfo%westbd < 360.0 .and. + & trkrinfo%eastbd >= 0.0)then + + ! In this special case, the user has specified that the + ! western boundary be to the west of the Greenwich + ! meridian and the eastern boundary be to the east of it. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE: The user supplied grid lon boundaries' + print *,'++ span across the Greenwich meridian.' + print *,'++ ' + print *,'++ Western boundary: ',trkrinfo%westbd + print *,'++ Eastern boundary: ',trkrinfo%eastbd + print *,'++ Northern boundary: ',trkrinfo%northbd + print *,'++ Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ! Calculate the beginning and ending i and j points for + ! this case of spanning the Greenwich meridian. The + ! beginning and ending j points are, obviously, the same + ! as for the regular case below in the else. The + ! i-beginning point will also be the same as for the + ! regular case. However, the i-ending point will be + ! modified for the meridian wrap; it will be > imax. + + jbeg = int(((glatmax + dy - trkrinfo%northbd) + & / dy) + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) + & / dy) + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) + & / dx) + 0.5) +c iend = int(((trkrinfo%eastbd - glonmin + dx) +c & / dx) + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) + & / dx) + 0.5) + imax + + goto 377 + + endif + endif + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. There is a' + print *,'!!! problem with the user-supplied grid ' + print *,'!!! boundaries. Please check them and ' + print *,'!!! resubmit the program.' + print *,'!!!' + print *,'!!! Western boundary: ',trkrinfo%westbd + print *,'!!! Eastern boundary: ',trkrinfo%eastbd + print *,'!!! Northern boundary: ',trkrinfo%northbd + print *,'!!! Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ifgcret = 91 + return + + 377 continue + + else + ! Calculate the beginning and ending i and j points.... + jbeg = int(((glatmax + dy - trkrinfo%northbd) / dy) + & + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) / dy) + & + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) / dx) + & + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) / dx) + & + 0.5) + endif + endif + +c Scan the requested portion of the grid and pick out the max and +c min data values, figure out what the max and min contour levels +c will be, and fill an array with the values of the various +c intermediate, incremental contour levels. + + if (trkrinfo%contint <= 0) then + + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. For a midlat' + print *,'!!! or tcgen run of the tracker, the contour' + print *,'!!! interval supplied by the user is not ' + print *,'!!! greater than 0.' + print *,'!!! ' + print *,'!!! User-supplied contint = ',trkrinfo%contint + print *,' ' + endif + + ifgcret = 91 + return + endif + + dmin = 9.99e20 + dmax = -9.99e20 + + do j = jbeg,jend + do i = ibeg,iend + if (i > imax) then + itemp = i - imax ! If wrapping past GM + else + itemp = i + endif + if (valid_pt(itemp,j)) then + if (fxy(itemp,j) < dmin) dmin = fxy(itemp,j) + if (fxy(itemp,j) > dmax) dmax = fxy(itemp,j) + endif + enddo + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*--------------------------------------------*' + print *,'In first_ges_center, dmin= ',dmin,' dmax= ',dmax + endif + + +c We want to allow for storms moving out of the sub-region, +c in which case we might hit slightly lower or higher +c contours than were found in the sub-region, so allow for +c an extra buffer and modify dmin and dmax.... + + dbuffer = (dmax - dmin) / 2.0 + dmax = dmax + dbuffer + dmin = dmin - dbuffer + + if ( verb .ge. 3 ) then + print *,'after adjustment, dmin= ',dmin,' dmax= ',dmax + endif + +c Next 2 lines changed for compiler compatibility on +c other platforms.... +c contour_info%xmaxcont = dmax - amod(dmax,trkrinfo%contint) +c contour_info%xmincont = dmin - amod(dmin,trkrinfo%contint) + + tmp = trkrinfo%contint + contour_info%xmaxcont = dmax - mod(dmax,tmp) + contour_info%xmincont = dmin - mod(dmin,tmp) + + if ( verb .ge. 3 ) then + print *,'A1 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A1 contour_info%xmincont= ',contour_info%xmincont + endif + + if (contour_info%xmincont > contour_info%xmaxcont) then + contour_info%xmincont = contour_info%xmaxcont + endif + +c if (dmin > contour_info%xmincont) then +c contour_info%xmincont=contour_info%xmincont + trkrinfo%contint +c endif +c if (dmax < contour_info%xmaxcont) then +c contour_info%xmaxcont=contour_info%xmaxcont - trkrinfo%contint +c endif + + if ( verb .ge. 3 ) then + print *,'A2 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A2 contour_info%xmincont= ',contour_info%xmincont + print *,'maxconts= ',maxconts + endif + +c NOTE: In the loop below, the contour_info%contvals array is now +c (5/2003) no longer used in subsequent subroutines. But we still +c need to figure out the value of the contvals as we iterate the +c loop so we can know when we've surpassed dmax and can stop +c incrementing contour_info%numcont, which we do need in subsequent +c subroutines. + + contour_info%numcont = 0 + do n = 1,maxconts + contour_info%numcont = contour_info%numcont + 1 + contour_info%contvals(n) = contour_info%xmincont + + & float(n-1)*trkrinfo%contint +c print *,'n= ',n,' contour_info%contvals(n)= ' +c & ,contour_info%contvals(n) + if (contour_info%contvals(n) >= dmax) exit + enddo + + oldstormct = stormct + call find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) + + if (stormct > 0) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' ' + print *,'!!! ************************************************' + print *,'!!! ' + print *,'!!! NOTE: In first_ges_center, the value of stormct' + print *,'!!! returned from find_all_maxmins is not greater' + print *,'!!! than 0. This means there are no new centers' + print *,'!!! to track, which is not likely. Perhaps you are' + print *,'!!! searching over too small of an area??' + print *,'!!! ' + print *,'!!! ************************************************' + print *,' ' + endif + + endif + + print *,'ifh= ',ifh,' oldstormct= ',oldstormct + print *, ' stormct= ',stormct + + do mm = 1,300 + print *,'mm= ',mm,' maxmini(mm)= ',maxmini(mm) + & ,' maxminj(mm)= ',maxminj(mm) + enddo + + if (stormct > oldstormct .and. stormct > 0) then + isstart = oldstormct + 1 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,*) 'New search: ' + write (6,*) 'Possible new max/min locations at ifh= ',ifh + write (6,*) '--------------------------------------------' + endif + + do n = isstart,stormct + if (trkrinfo%type == 'midlat') then + storm(n)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(n)%tcv_center = 'TCG ' + endif + slonfg(n,ifh) = glonmin + (maxmini(n)-1)*dx + slatfg(n,ifh) = glatmax - (maxminj(n)-1)*dy + storm(n)%tcv_stspd = -99 + storm(n)%tcv_stdir = -99 + write (storm(n)%tcv_storm_id,'(i4.4)') n + write (storm(n)%tcv_storm_name,'(i4.4)') n + stormswitch(n) = 1 + if (cparm == 'mslp') then + + if ( verb .ge. 3 ) then + write (6,71) maxmini(n),maxminj(n),slonfg(n,ifh) + & ,360.-slonfg(n,ifh),slatfg(n,ifh) + & ,slp(maxmini(n),maxminj(n))/100.0 + endif + + endif + enddo + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' New search: ' + print *,'!!! NOTE: No new storms found in find_all_maxmins' + print *,'!!! at ifh = ',ifh,' stormct= ',stormct + print *,'!!! oldstormct= ',oldstormct + print *,' ' + endif + + endif + + 71 format (1x,'i= ',i4,' j= ',i4,' lon: ',f7.2,'E (',f6.2,'W)' + & ,2x,' lat: ',f6.2,' mslp: ',f6.1,' mb') +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) +c +c ABSTRACT: This subroutine will search an area delineated by +c input i and j indeces in order to find all local maxes or mins +c in that area. The (i,j) locations of the maxes/mins are returned +c in the maxmini and maxminj arrays. The input 3-character string +c cmaxmin will tell the subroutine to look for a "max" or a "min". +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c ibeg i-index for upper left location of grid to search +c iend i-index for lower right location of grid to search +c jbeg j-index for upper left location of grid to search +c jend j-index for lower right location of grid to search +c fxy Real array of data values +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c contour_info Type cint_stuff from module contours containing the +c the following 4 variables: +c 1. xmincont Real value for min contour level in the fxy data array +c 2. xmaxcont Real value for max contour level in the fxy data array +c 3. contvals Real array holding values of cont levels at this time +c 4. numcont Number of contour intervals found at this time +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c trkrinfo derived type containing various user-input tracker parms +c cmaxmin String that declares if "min" or "max" is being searched +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c +c OUTPUT: +c maxmini integer array containing i-indeces of the max/min points +c maxminj integer array containing j-indeces of the max/min points +c ifamret return code from this subroutine + + USE trkrparms; USE set_max_parms; USE contours + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + integer stormct,i,j,ibeg,iend,jbeg,jend,ix,jx,ixp1,ixm1 + integer ip,jp,maxstorm,jxp1,jxm1,ifamret,isret,iaret,iclmret + integer isoiret,icccret,igicwret,imax,jmax + character ccflag*1,get_last_isobar_flag*1,point_is_over_water*1 + character(*) cmaxmin + logical(1) still_finding_valid_maxmins,rough_gradient_check_okay + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real xavg,stdv,search_cutoff,dmin,dmax,sphere_cutoff + real plastbar,rlastbar,fract_land,dx,dy + +c----- + still_finding_valid_maxmins = .true. + + +c print *,'ctm beg of find_all_maxmins, maxstorm= ',maxstorm + + +c First, we want to get the mean and standard deviation of the input +c field to be searched. We can use the standard deviation info as +c part of our guideline for when to stop searching for maxes & mins. +c We will set the search cut-off threshold at 1/2 standard deviation +c above the mean for min searches. So, for the example of mslp, if +c the mean pressure over the whole domain is 1010 mb and the +c standard deviation is 12 mb, then when we are searching, if the +c lowest available (i.e., hasn't been found in a previous iteration +c of this loop) pressure is 1016, then it's time to stop searching. + + call avgcalc (fxy,imax*jmax,valid_pt,xavg,iaret) + call stdevcalc (fxy,imax*jmax,valid_pt,xavg,stdv,isret) + if (iaret /= 0 .or. isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_all_maxmins, the calls to avgcalc' + print *,'!!! and/or stdevcalc returned an error.' + print *,'!!! iaret= ',iaret,' isret= ',iaret + print *,' ' + endif + + ifamret = 98 + return + endif + + if (cmaxmin == 'min') then + search_cutoff = xavg + stdv*0.5 + else + search_cutoff = xavg - stdv*0.5 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In find_all_maxmins, search_cutoff= ',search_cutoff + print *,' ' + endif + +c Now begin to search the domain. We do a simple gridpoint scan, +c and once we find the max/min value, we pass the (i,j) coordinates +c at that point to a routine to check for a closed contour. Then +c we mask out those points in the contour (or, if there is not a +c closed contour, just the 8 points immediately surrounding the low +c center) and we do another iteration of search_loop to look for +c more lows. We mask out points we've found so that on subsequent +c iterations of search_loop, we don't find the same old center +c again and again and again..... + + search_loop: do while (still_finding_valid_maxmins) + + dmin = 9.99e20 + dmax = -9.99e20 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + ip = i + jp = j + + if (ip > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: In find_all_maxmins, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. The search' + print *,'!!! will not extend to the user-requested' + print *,'!!! grid boundary.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',ip + print *,' ' + endif + + exit iloop + + endif + endif + + if (valid_pt(ip,jp) .and..not. masked_out(ip,jp)) then + if (cmaxmin == 'min') then + if (fxy(ip,jp) < dmin) then + dmin = fxy(ip,jp) + ix = ip + jx = jp + endif + else + if (fxy(ip,jp) > dmax) then + dmax = fxy(ip,jp) + ix = ip + jx = jp + endif + endif + endif + + enddo iloop + enddo jloop + + if (cmaxmin == 'min') then + if (dmin < search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + else + if (dmax > search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + endif + +c As a rough first check, see if the neighboring points on all +c 4 sides have a gradient sloping down into the found min point, +c or at least that there is a flat field not having a gradient +c sloping away from the center point. + + call get_ijplus1_check_wrap (imax,jmax,ix,jx,ixp1,jxp1,ixm1 + & ,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In find_all_maxmins, the center we found' + print *,'!!! is too close to the grid boundary and will' + print *,'!!! NOT be checked for a closed contour.' + print *,'!!! ix= ',ix,' jx= ',jx,' fxy= ',fxy(ix,jx) + print *,'!!! ' + print *,' ' + endif + + masked_out(ix,jx) = .true. + cycle search_loop + endif + + if (cmaxmin == 'min') then + if (fxy(ix,jx) <= fxy(ixp1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxm1) .and. + & fxy(ix,jx) <= fxy(ixm1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + else + if (fxy(ix,jx) >= fxy(ixp1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxm1) .and. + & fxy(ix,jx) >= fxy(ixm1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + endif + + if (rough_gradient_check_okay) then + + if ( verb .ge. 3 ) then + print *,'Found a possible max/min at ix= ',ix,' jx= ',jx + endif + + +c From this rough check, we appear to have a gradient sloping +c in towards the center point. Now call the subroutine to +c check whether or not there is in fact a closed contour +c surrounding this local maximum or minimum. + + get_last_isobar_flag = 'n' + ccflag = 'n' + call check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,ccflag,cmaxmin,trkrinfo + & ,1,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if (ccflag == 'y') then + if (stormct < maxstorm) then + stormct = stormct + 1 + + if ( verb .ge. 3 ) then + print *,'AAA stormct= ',stormct,' ix= ',ix,' jx= ',jx + endif + + ! For a tcgen case, we will add in one additional check, + ! and that is to ensure the point is (mostly) over water. + ! Only do this check if the user has requested it (some + ! of the global models do not have a land-sea mask + ! included in the grib data files). + + point_is_over_water = 'u' + + if (trkrinfo%use_land_mask == 'y') then + call check_land_mask (imax,jmax,ix,jx,fract_land + & ,valid_pt,dx,dy,point_is_over_water,iclmret) + if (iclmret /= 0) then + print *,' ' + print *,'!!! ERROR from check_land_mask for ix= ',ix + & ,' jx= ',jx + print *,'!!! STOPPING PROGRAM' + stop 95 + endif + endif + + if (point_is_over_water /= 'n') then + maxmini(stormct) = ix + maxminj(stormct) = jx + endif + + else + + if ( verb .ge. 3 ) then + print *,'---max stormct reached, stormct= ', stormct + endif + + endif + else + + if ( verb .ge. 3 ) then + print *,'!!! contour check negative, ccflag= ',ccflag + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*-----------------------------------------------*' + print *,' ' + endif + + endif + +c Regardless of whether or not the found point turns out to have +c a closed contour, we don't want to find this local minimum or +c its 8 surrounding points again in a search on a subsequent +c iteration of this loop. + + masked_out(ix,jx) = .true. + masked_out(ix,jxp1) = .true. + masked_out(ixp1,jxp1) = .true. + masked_out(ixp1,jx) = .true. + masked_out(ixp1,jxm1) = .true. + masked_out(ix,jxm1) = .true. + masked_out(ixm1,jxm1) = .true. + masked_out(ixm1,jx) = .true. + masked_out(ixm1,jxp1) = .true. + + enddo search_loop + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine mask_based_on_wind_circ (imax,jmax,dx,dy,level + & ,valid_pt,masked_outc,trkrinfo + & ,ctlon,ctlat,cmodel_type,imbowret) +c +c ABSTRACT: This subroutine masks out grid points for a storm that +c is currently being tracked. It is called after a fix has been +c made at the current forecast hour. It is only used as a backup, +c that is, if the mslp data were not there and/or a fix position +c for mslp could not be made, then that means that the mask would +c not be able to get updated using the routine in subroutine +c check_closed_contour. But we still do need to update that mask, +c so we will instead do it based on wind circulation. We will go +c out radially from the center, starting at 40 km, then every +c 40 km from there on out. When the mean cyclonic Vt drops below +c 3 m/s, stop searching, and then mask out all grid points within +c that last-searched radius. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_outc Logical. T = data point is already accounted for, +c under the influence of another nearby max or min +c center; F = data point is available to be scanned by +c this subroutine for max or min centers. +c ctlon Fix longitude for the input parameter to this routine +c ctlon Fix latitude for the input parameter to this routine +c cmodel_type character, 'global' or 'regional' + + USE set_max_parms; USE trkrparms; USE grid_bounds + USE verbose_output; USE level_parms + + implicit none + + type (trackstuff) trkrinfo + + character(*) cmodel_type + integer, parameter :: numazim=24 + integer imax,jmax,level,imbowret,nlev,iazim,i,j + integer ibiret1,ibiret2,azimuth_ct,igvtret + integer jnfix,jsfix,iefix,iwfix + real vr(numazim),vt(numazim) + real dx,dy,ctlon,ctlat,rdist,bear,targlat,targlon + real xintrp_u,xintrp_v,grid_buffer,xmax_rdist_reached + real vt_mean,vt_azim_sum,xbear,dist,degrees + logical(1) valid_pt(imax,jmax),masked_outc(imax,jmax) + logical(1) searching_valid_pts + + imbowret = 0 + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + searching_valid_pts = .true. + + rdist = 40.0 ! units in km + xmax_rdist_reached = rdist ! units in km + + radial_loop: do while (searching_valid_pts) + + azimuth_ct = 0 + vt_azim_sum = 0.0 + vt = -999.0 + vr = -999.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (ctlat,ctlon,rdist,bear,targlat,targlon) + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + xmax_rdist_reached = rdist + exit radial_loop + endif + + ! These calls to bilin_int_uneven pass a variable, level, + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (ctlon,ctlat,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim) + & ,vt(iazim),igvtret) + azimuth_ct = azimuth_ct + 1 + vt_azim_sum = vt_azim_sum + vt(iazim) + else + xmax_rdist_reached = rdist + exit radial_loop + endif + + enddo azimloop + + if (azimuth_ct > 0) then + ! Compute azimuthally-averaged Vt at this distance + vt_mean = vt_azim_sum / float(azimuth_ct) + else + vt_mean = -999.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: rdist= ',rdist,' azimuth_ct= ',azimuth_ct + & ,' vt_azim_sum= ',vt_azim_sum,' vt_mean= ',vt_mean + endif + + if (ctlat >= 0.0) then + if (vt_mean >= 3.0) then + ! For a NH storm, if the cyclonic mean Vt >= 3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + else + if (vt_mean <= -3.0 .and. vt_mean > -998.0) then + ! For a SH storm, if the cyclonic mean Vt <= -3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + enddo radial_loop + + if ( verb .ge. 3 ) then + print *,'mbow: After radial_loop, rdist= ',rdist + & ,' xmax_rdist_reached= ',xmax_rdist_reached + endif + +c ----------------------------------------------------------------- +c At this point, we are done searching radially outwards away from +c the storm center. The max radial distance we reached is called +c xmax_rdist_reached. By getting to this spot in the subroutine, +c that means that we bumped out of radial_loop above because the +c rdist being used in that loop got to a radius at which the mean +c cyclonic Vt no longer was strong enough to continue the search +c outward, so we need to reduce it by 40 km here (back to the value +c for the last successful search). At a minimum, we will mask to a +c radius of 80 km. + + if (xmax_rdist_reached > 80.0) then + xmax_rdist_reached = xmax_rdist_reached - 40.0 + else + xmax_rdist_reached = 80.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: After adjustment of xmax_rdist_reached, rdist= ' + & ,rdist,' xmax_rdist_reached= ',xmax_rdist_reached + endif + + bearloop: do i = 1,4 + + ! Now find the values of the longitude for the farthest west + ! and east points and find the values of the latitude for the + ! farthest north and south points. The i and j indices + ! associated with these lons and lats will be used to define + ! the bounds of the grid over which we scan to find points + ! that will update the mask. + + select case (i) + case (1); xbear = 0.0; + case (2); xbear = 90.0; + case (3); xbear = 180.0; + case (4); xbear = 270.0; + end select + + call distbear (ctlat,ctlon,xmax_rdist_reached,xbear + & ,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,'mbow: distbear for i= ',i,' targlon= ',targlon + & ,' targlat= ',targlat + endif + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon > glonmax for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmax= ',glonmax + imbowret = 95 + return + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon < glonmin for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmin= ',glonmin + imbowret = 95 + return + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'either targlat > glatmx or targlat < glatmin, so' + print *,'we cannot update the mask.' + print *,'targlat= ',targlat,' glatmin= ',glatmin + print *,' glatmin= ',glatmin + imbowret = 95 + return + cycle bearloop + endif + + ! Get the i & j starting and ending points for our loop where + ! we will update the mask.... + + if (i == 1) then + + ! Get j for northern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jnfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jnfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 2) then + + ! Get i for eastern longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iefix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + elseif (i == 3) then + + ! Get i for southern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jsfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jsfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 4) then + + ! Get i for western longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iwfix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + endif + + enddo bearloop + + if ( verb .ge. 3 ) then + print *,'mbow: iwfix= ',iwfix,' iefix= ',iefix + & ,' jnfix= ',jnfix,' jsfix= ',jsfix + endif + + do i = iwfix,iefix + do j = jnfix,jsfix + + call calcdist (glon(i),glat(j),ctlon,ctlat,dist,degrees) + + if (dist < xmax_rdist_reached) then + masked_outc(i,j) = .true. + endif + + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,closed_contour,cmaxmin,trkrinfo + & ,num_requested_contours,contour_info + & ,get_last_isobar_flag,plastbar,rlastbar,icccret) +c +c ABSTRACT: This subroutine checks a field of data around an input +c (ix,jx) data point to see if a closed contour exists around +c that data point. It can check for a closed contour on a max or a +c min field, depending on the value of the input variable 'cmaxmin'. +c The algorithm works by examining rings of the 8 data points +c surrounding a data point that is in the contour interval. For +c example, in the diagram below, the X represents the location of +c the local minimum value which was passed into this routine with +c the coordinates (ix,jx), let's say it's 985 mb. And let's assume +c that the data values at points A-I are all in the 4 mb contour +c interval of 985-989 mb, and that all the surrounding points have +c data values >= 989. To test for a closed contour, we first check +c the ring of 8 points immediately around point X to see what their +c data values are. If a data value is found that is below the +c lower limit of this contour interval (985 mb) or lower than the +c local minimum value at the X point that we initially targeted +c (985 mb), then we do NOT have a closed contour, and we exit this +c subroutine. But in our example, that's not the case, and we have +c 5 points (B,D,E,F,G) that are in the interval. So in our next +c iteration of the loop, we set up 5 rings, each one set up around +c the points found in the first iteration (B,D,E,F,G), and we check +c the 8 points around each of those points. A logical array is +c used so that as soon as a point is found, it is flagged as being +c found. In this way, when we look at the ring around point D, for +c example, we won't pick point X again and set up another ring +c around it in the next ring iteration and end up in an infinite +c loop, going back and forth between point X and point D. While +c checking the 8 points in a ring, if a found data value is above +c our contour interval (i.e., >= 989 mb), we just ignore the +c point; we only mark points that are in our contour interval, +c and again, if we find a point below our contour interval, we +c exit the subroutine with a flag indicating a closed contour was +c NOT found. So in this method, we keep spreading out from the +c initial local minimum and creating and checking new rings until +c we either: (a) Hit the edge of the regional grid, in which case +c we consider a closed contour NOT found, (b) Run into a data +c point that has been marked as being under the influence of +c another nearby low, in which case we consider a closed contour +c NOT found, (c) Run into a point which is below (above) our +c contour interval for a min (max) check, in which case we +c consider a closed contour NOT found, or (d) we run out of +c points to keep searching, we have no rings left to create and +c check because all of the surrounding points are above (below) +c our contour interval for a min (max) check, and by default we +c consider this a closed contour and return to the calling +c subroutine a flag indicating such. +c +c + + + + + + + + + + +c + + + + + + + + + + +c + + A B + + + + + + +c + + C D X E + + + + +c + + + + F G + + + + +c + + + + + H I + + + +c + + + + + + + + + + +c + + + + + + + + + + +c +c UPDATE: This subroutine was updated to keep searching for +c multiple closed contours until it can't find anymore. The +c input parameter num_requested_contours dictates how many +c contours to search for. In the case of just trying to roughly +c locate new centers and establish that there is a closed +c circulation, num_requested_contours will = 1, and we will exit +c after finding that 1 contour. But for a check after making a +c full center fix, we set num_requested_contours = 999 so that +c we can keep searching for all closed contours around the low. +c In this 999 case, you will eventually get to a point where +c there is no closed contour. In that case, in the standard +c output you will see a message telling you that you hit a point +c that is not in the contour and that there is no closed contour, +c but you will also notice that the ccflag = y, meaning there is +c a closed contour (because you have found at least 1 closed +c contour along the way). The reason to keep searching for more +c closed contours is that we can then return the value of the +c outermost closed isobar. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c cmaxmin character string ('max' or 'min') that tells this +c routine what we're looking for. +c trkrinfo derived type that holds/describes various tracker parms +c contour_info Type cint_stuff from module contours. Contains +c contour information +c num_requested_contours For the simple first_ges_center check, +c this will be 1 (we just want to know if there's at +c least 1 closed contour). For the verifying check after +c we've found a center, this will be 9999 (i.e., just keep +c searching for more contours) +c get_last_isobar_flag character ('y' or 'n') to indicate whether +c or not to report on the value of the last closed isobar +c and the radius of the last closed isobar. +c +c OUTPUT: +c closed_contour character; A returned value of 'y' indicates that +c this routine was able to find a closed contour. +c plastbar Contains the value of the last closed isobar (unrounded) +c rlastbar Contains the mean radius of the last closed isobar +c +c LOCAL: +c num_pts_in_all_contours Counter for the number of pts inside of +c the contour we're looking at +c next_ring_ct Counter for the number of points that have been +c tagged to be used as center points for the next +c iteration of multiple_ring_loop. +c next_contour_ct Counter for the number of points that have been +c tagged to be used as center points in the first iteration +c through single_contour_scan_loop as we begin to scan +c points in the *next* contour interval. This counter gets +c incremented when, for example, we are searching points +c around a current center point and we find one that is not +c in our current interval, but rather is in the next +c interval. We want to remember this point and store the +c location, so we increment this counter and store the +c location in next_contour_i and next_contour_j arrays. +c beyond_contour_ct Counter for the number of points that have been +c tagged to be used as center points for some subsequent +c iteration of successive_contours_loop. This is +c different from next_contour_ct, which is used to hold +c the locations of points that are definitely in the +c *next* contour interval. Here, we have points that we +c just store in a pool of potential points to be searched +c in future iterations. These points can come about in +c cases where there is a very intense, very compact low +c with a tight pressure gradient, such that multiple +c contour intervals could be spanned in between 2 adjacent +c gridpoints (this is especially the case if the contour +c interval you have chosen is small). You need to be +c careful with how you handle this array. Once you find +c that you have searchable points in next_contour_i or +c next_contour_j, do not just simply empty out this +c beyond_contour count and its i and j arrays. The +c reason being that some of these "beyond" points may end +c up being used and searched in subsequent iterations, but +c not if we just delete them now. + + + USE set_max_parms; USE trkrparms; USE contours; USE grid_bounds + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,ir,iria,irja,irx,jrx,ix,jx,imax,jmax + integer nb,ibx,jby,nct,iflip + integer mr,ringct,ixp1,ixm1,jxp1,jxm1,nring,iter + integer icenx,jcenx,icccret,next_ring_ct,igicwret + integer num_pts_in_all_contours,next_contour_ct + integer beyond_contour_ct + integer num_pts_in_one_contour + integer num_requested_contours,num_found_contours + integer nm,im,jm,inall,insingle,isc_count,rlast_distct + character found_a_point_in_our_contour*1,closed_contour*1 + character found_a_point_below_contour*1 + character found_a_point_above_contour*1,get_last_isobar_flag*1 + character(*) cmaxmin + logical(1) still_scanning + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + logical(1) point_is_already_in_our_contour(imax,jmax) + logical(1) point_is_already_in_next_contour(imax,jmax) + logical(1) point_is_already_in_beyond_pool(imax,jmax) + integer isni,isnj,inci,incj,ibci,ibcj,ihmi,ihmj,itmi,itmj + integer, allocatable :: search_next_i(:) + integer, allocatable :: search_next_j(:) + integer, allocatable :: next_contour_i(:) + integer, allocatable :: next_contour_j(:) + integer, allocatable :: beyond_contour_i(:) + integer, allocatable :: beyond_contour_j(:) + integer, allocatable :: hold_mask_i_loc(:) + integer, allocatable :: hold_mask_j_loc(:) + integer, allocatable :: temp_mask_i_loc(:) + integer, allocatable :: temp_mask_j_loc(:) + integer, allocatable :: ringposi(:),ringposj(:) + real,allocatable :: ringpos(:,:) + real fxy(imax,jmax),contvals(maxconts) + real contlo,conthi,xcentval,contlo_next,conthi_next + real dist,degrees,rlast_distsum,plastbar,rlastbar +c + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + allocate (search_next_i(imax*jmax),stat=isni) + allocate (search_next_j(imax*jmax),stat=isnj) + allocate (next_contour_i(imax*jmax),stat=inci) + allocate (next_contour_j(imax*jmax),stat=incj) + allocate (beyond_contour_i((imax*jmax)/2),stat=ibci) + allocate (beyond_contour_j((imax*jmax)/2),stat=ibcj) + allocate (hold_mask_i_loc(imax*jmax),stat=ihmi) + allocate (hold_mask_j_loc(imax*jmax),stat=ihmj) + allocate (temp_mask_i_loc(imax*jmax),stat=itmi) + allocate (temp_mask_j_loc(imax*jmax),stat=itmj) + if (isni /= 0 .or. isnj /= 0 .or. inci /= 0 .or. incj /= 0 .or. + & ibci /= 0 .or. ibcj /= 0 .or. ihmi /= 0 .or. ihmj /= 0 .or. + & itmi /= 0 .or. itmj /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various search, hold and temp arrays.' + print *,'!!! isni = ',isni,' isnj= ',isnj + print *,'!!! inci = ',inci,' incj= ',incj + print *,'!!! ibci = ',ibci,' ibcj= ',ibcj + print *,'!!! ihmi = ',ihmi,' ihmj= ',ihmj + print *,'!!! itmi = ',itmi,' itmj= ',itmj + print *,' ' + endif + + STOP 98 + endif + + closed_contour = 'n' + xcentval = fxy(ix,jx) + num_found_contours = 0 + next_contour_ct = 0 + beyond_contour_ct = 0 + num_pts_in_all_contours = 0 + hold_mask_i_loc = 0 + hold_mask_j_loc = 0 + beyond_contour_i = 0 + beyond_contour_j = 0 + point_is_already_in_our_contour = .false. + point_is_already_in_beyond_pool = .false. + icccret = 0 + isc_count = 0 + plastbar = -999.0 + rlastbar = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* Top of check_closed_contour, ix= ',ix,' jx= ',jx + print *,'*-----------------------------------------------*' + print *,' ' + print *,'fxy(ix,jx)= ',fxy(ix,jx),' xcentval= ',xcentval + endif + +c First, set up the contour intervals that will be used. In +c the original version of this code, we used preset +c standard intervals (984,988,992,996,1000,1004....). But upon +c further review, it was decided that this was too arbitrary. +c So instead, we consider the found min (max) value to be the +c bottom (top) of the list of contour intervals. In this way, +c we can clearly specify and screen storms based on the "depth" +c of the pressure field as compared to the surroundings. + + i = 1 + do while (i <= maxconts) + if (cmaxmin == 'min') then + contvals(i) = xcentval + float(i-1)*trkrinfo%contint + i = i + 1 + else + iflip = maxconts - i + 1 + contvals(iflip) = xcentval - float(i-1)*trkrinfo%contint + i = i + 1 + endif + enddo + +c This successive_contours loop is the master loop.... + + successive_contours_loop: do while (num_found_contours < + & num_requested_contours) + +c Find the contour interval in which the center value resides. +c Note that the lower bound is included for a min check, while +c the upper bound is included for a max check. Note also that +c this subroutine can be used to find the last closed contour, +c and part of that functionality shows up in the next while +c statement where we reference "num_found_contours" in the +c array indeces for the contour values. Basically, the way we +c do this is, for example, if our central value is 990.4 mb and +c our contour interval is 4 mb, then in the first run through +c successive_contours_loop we see if we have a closed contour in +c the interval 990.4-994.4. If yes, then the next time through +c this loop, we see if we have a closed contour in the interval +c 994.4-998.4. If yes, then the next loop check is for 998.4- +c 1002.4, and so on.... We stop searching if we find a value +c that is either below the xcentval input into this subroutine +c or below the lower value of the current contour interval (this +c would mean a change in the gradient and would indicate that, +c in the case of mslp, we are heading down towards another, +c different low). + + isc_count = isc_count + 1 + + point_is_already_in_next_contour = .false. + + i = 1 + do while (i < maxconts) + if (cmaxmin == 'min') then + if (contvals(i) <= xcentval .and. xcentval < contvals(i+1)) + & then + + if ( verb .ge. 3 ) then + print *,'At A, num_found_contours= ',num_found_contours + endif + + contlo = contvals(i+num_found_contours) + conthi = contvals(i+1+num_found_contours) + + if ( verb .ge. 3 ) then + print *,'At A, contlo= ',contlo,' conthi= ',conthi + endif + exit + + endif + else + if (contvals(i) < xcentval .and. xcentval <= contvals(i+1)) + & then + contlo = contvals(i-num_found_contours) + conthi = contvals(i-num_found_contours+1) + exit + endif + endif + i = i + 1 + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'num_found_contours= ',num_found_contours + print *,'contlo= ',contlo,' conthi= ',conthi + print *,'xcentval= ',xcentval + endif + + +c This single_contour_scan_loop is the main loop for searching +c for one individual contour. If it is determined that a contour +c exists, control is returned to the successive_contours_loop, +c and if more contours were requested to be found, then the +c search continues onward & outward.... + + temp_mask_i_loc = 0 + temp_mask_j_loc = 0 + + iter = 1 + num_pts_in_one_contour = 0 + still_scanning = .true. + + rlast_distsum = 0.0 + rlast_distct = 0 + + single_contour_scan_loop: do while (still_scanning) + +c print *,' ' +c print *,' top of single contour scan loop' +c print *,'+++ iter= ',iter +c print *,' N1: next_contour_ct= ',next_contour_ct + + if (iter == 1 .and. num_found_contours == 0) then + ! For the first iteration, we have only the first ring, + ! which is centered on the input minimum/maximum point. + ringct = 1 + search_next_i(1) = ix + search_next_j(1) = jx + +c point_is_already_in_our_contour(ix,jx) = .true. +c num_pts_in_one_contour = num_pts_in_one_contour + 1 +c temp_mask_i_loc(num_pts_in_one_contour) = ix +c temp_mask_j_loc(num_pts_in_one_contour) = jx + + else if (iter == 1 .and. num_found_contours > 0) then + ! This is the first iteration in a *new* contour. + ! That is, we have already found 1 or more previous + ! contours while in previous iterations of + ! successive_contours_loop and we are now beginning + ! to look for the next contour. + +c print *,' N2: next_contour_ct= ',next_contour_ct + + if (next_contour_ct == 0) then + ! This would be for the special case in which, for + ! example, you've got a very intense, compact storm + ! that "skips" a contour. That is, suppose the + ! min pressure of a storm is 982 mb, and we are + ! utilizing a 4-mb contour interval, but all + ! surrounding data points are, say, 987 mb or + ! higher. Then, next_contour_ct would be 0 since no + ! data points were found in the next contour interval + ! of 982-986 mb, but we can continue searching since the + ! gradient is still sloping the correct way. The code in + ! this if statement handles this special case. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ALERT: next_contour_ct = 0 ' + endif + + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + +c print *,'b4 ZZ, ringct= ',ringct +c print *,'at ZZ, bcc= ',beyond_contour_ct +c & ,'contlo_next= ',contlo_next +c & ,'conthi_next= ',conthi_next + + bey_con_min_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_min_loop + endif + +c print *,'-- ZZ, ibx= ',ibx,' jby= ',jby +c & ,' fxy(ibx,jby)= ',fxy(ibx,jby) + + if (fxy(ibx,jby) >= contlo_next .and. + & fxy(ibx,jby) < conthi_next) then + +c print *,'>> ZZ HIT!!, ibx= ',ibx,' jby= ',jby +c +c print *,' +++ BEYOND in NEXT: i= ',ibx,' j= ',jby +c & ,' fxy= ',fxy(ibx,jby) + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + +c print *,'.. ZZ, next_contour_ct= ',next_contour_ct + + enddo bey_con_min_loop + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + +c print *,'At A, beyond_contour_ct= ',beyond_contour_ct +c print *,' contlo_next = ',contlo_next +c print *,' conthi_next = ',conthi_next + + bey_con_max_loop: do nb = 1,beyond_contour_ct + +c print *,'in bey_con_max_loop, nb= ',nb + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_max_loop + endif + +c print *,'ibx= ',ibx,' jby= ',jby,' data= ' +c & ,fxy(ibx,jby) + + if (fxy(ibx,jby) > contlo_next .and. + & fxy(ibx,jby) <= conthi_next) then + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + +c print *,' ++ HIT! ibx= ',ibx,' jby= ',jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + enddo bey_con_max_loop + endif + + if (next_contour_ct > 0) then + ringct = next_contour_ct + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! XXX next_contour_ct not > 0 !!!' + print *,'next_contour_ct= ',next_contour_ct + print *,'beyond_contour_ct= ',beyond_contour_ct + print *,'ringct= ',ringct + print *,'next_ring_ct= ',next_ring_ct + print *,'cycling to top of successive_contours_loop..' + print *,' ' + endif + + ! The number of rings that we have available to search + ! in the next contour interval is 0, so cycle all the + ! way back to the top of the outer loop, which is + ! successive_contours_loop, so that we can increase the + ! contour bounds and search inside those new bounds. + ! Again, this is for the case in which we have an + ! intense, compact storm and we are using a small + ! contour interval, such that we are essentially + ! "skipping" over one of these intervals in one of the + ! loop iterations. We need to bump up the + ! num_found_contours by one in order to increase the + ! array index in the contvals array at the top of the + ! successive_contours_loop. It is kosher to do this + ! since the reason we are cycling back to the top of + ! that loop is that we are skipping over a contour + ! interval. + + num_found_contours = num_found_contours + 1 + cycle successive_contours_loop + + endif + + else + + ringct = next_contour_ct + + endif + + do nring = 1,ringct + search_next_i(nring) = next_contour_i(nring) + search_next_j(nring) = next_contour_j(nring) +c print *,'at A, nring= ',nring,' next_contour_i(nring)= ' +c & ,next_contour_i(nring),' next_contour_j(nring)= ' +c & ,next_contour_j(nring) + enddo + + next_contour_ct = 0 + + else + ringct = next_ring_ct + endif + + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + allocate (ringposi(ringct),stat=iria) + allocate (ringposj(ringct),stat=irja) + if (iria /= 0 .or. irja /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various ring arrays. iria = ',iria + print *,'!!! irja = ',irja + print *,' ' + endif + + STOP 98 + endif + +ctm +c print *,' ' +c print *,'ringct= ',ringct + + do nring = 1,ringct + ringposi(nring) = search_next_i(nring) + ringposj(nring) = search_next_j(nring) +ctm +c print *,'nring= ',nring,' ringposi= ',ringposi(nring) +c & ,' ringposj= ',ringposj(nring) + enddo + + next_ring_ct = 0 + + ! This next loop reviews the points that have been + ! labelled for the "beyond_contour" pool. As we get further + ! into successive iterations of successive_contours_loop, + ! some of these previously "beyond" points are now within + ! the contour interval range that we are checking, so we + ! need to go through the list of "beyond" points and remove + ! any that are no longer in that "beyond" category.... + + check_beyond_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! This point may have been removed already in a + ! previous iteration of successive_contours_loop. + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle check_beyond_loop + endif + + ! Check to see if any of the points being searched in the + ! upcoming multiple_ring_loop are points that had previously + ! been saved as "beyond_contour" points. If so, remove + ! their status as "beyond_contour" points by setting the + ! logical flag to false. + + do nring = 1,ringct + + if (ibx == ringposi(nring) .and. jby == ringposj(nring)) + & then +c print *,' ' +c print *,'!!! beyond remove: ibx= ',ibx,' jby= ',jby + point_is_already_in_beyond_pool(ibx,jby) = .false. + endif + + enddo + + enddo check_beyond_loop + + +c In each iteration of single_contour_scan_loop, we can have a +c different number of rings to analyze. In the first +c iteration, we only have 1 ring, the initial ring around the +c local max/min that was input to this subroutine. Subsequent +c iterations will have a variable number of rings, depending on +c how many new data points within our contour interval were +c found in the previous iteration. + + multiple_ring_loop: do mr = 1,ringct + + icenx = ringposi(mr) + jcenx = ringposj(mr) + +ctm +c print *,' --- iter= ',iter,' mr= ',mr,' icenx= ',icenx +c & ,' jcenx= ',jcenx,' imax= ',imax,' jmax= ',jmax + + call get_ijplus1_check_wrap (imax,jmax,icenx,jcenx,ixp1,jxp1 + & ,ixm1,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NO CLOSED CONTOUR: The call to ' + print *,'!!! get_ijplus1_check_wrap indicates the' + print *,'!!! max/min contour extends past the edge of' + print *,'!!! our regional grid. ' + print *,' ' + print *,' ' + endif + + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c For each individual ring, we check all 8 points surrounding +c the center point. The points are numbered for each ring as +c shown in the diagram to the right of the "select case" +c statement just below. REMEMBER: The j in our grids +c increases from north to south, so that for a global grid, +c j=1 is at 90N and j=jmax is at 90S. + + individual_ring_loop: do ir = 1,9 + + select case (ir) + case (1); irx=ixm1; jrx=jcenx;! 2 3 4 + case (2); irx=ixm1; jrx=jxm1; ! + case (3); irx=icenx;jrx=jxm1; ! + case (4); irx=ixp1; jrx=jxm1; ! 1 (icenx,jcenx) 5 + case (5); irx=ixp1; jrx=jcenx;! + case (6); irx=ixp1; jrx=jxp1; ! + case (7); irx=icenx;jrx=jxp1; ! 8 7 6 + case (8); irx=ixm1; jrx=jxp1; ! + case (9); irx=icenx; jrx=jcenx; ! = center pt of ring + end select + +c Make sure the point we are looking at has valid data. +c This is an issue only on regional grids, where we have a +c buffer of bitmapped (null) data points surrounding the +c real grid. + +c print *,'ind ring loop: ir= ',ir,' irx= ',irx,' jrx= ',jrx + + if (.not. valid_pt(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a non-' + print *,'!!! valid point, meaning we are near the' + print *,'!!! bounds of the grid, or at least the ' + print *,'!!! bounds of the valid data for this ' + print *,'!!! grid. We will skip the' + print *,'!!! search for this center.' + print *,'!!! ' + print *,'!!! (i,j) of non-valid pt = (' + & ,irx,',',jrx,')' + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c Check to make sure that the point we are looking at is +c not considered under the influence of another nearby low. + + if (masked_out(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a point' + print *,'!!! that has been masked out, meaning it' + print *,'!!! belongs under the influence of ' + print *,'!!! another nearby low, so we will skip' + print *,'!!! the search for this center....' + print *,'!!! ' + print *,'!!! Min central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Masked-out value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of masked value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we have already hit this point on a previous ring +c check, then just ignore this point and cycle past it. + + if (point_is_already_in_our_contour(irx,jrx)) then +ctm +c print *,' ' +c print *,'Pt. AAA, already-in-contour.....' +c print *,'irx= ',irx,' jrx= ',jrx + cycle individual_ring_loop + endif + +c For a MIN check, check to see if the data point is below +c the contour interval or is below the local minimum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c For a MAX check, check to see if the data point is above +c the contour interval or is above the local maximum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c +c For example, for mslp, this would be as we're moving +c outward away from lower pressures to higher pressures, +c and then all of a sudden we come upon a lower pressure. +c This probably means we're heading toward another low +c pressure area, so mark the point and return to the +c calling routine. + + found_a_point_below_contour = 'n' + found_a_point_above_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) < xcentval .or. fxy(irx,jrx) < contlo) + & then + found_a_point_below_contour = 'y' + endif + else + if (fxy(irx,jrx) > xcentval .or. fxy(irx,jrx) > conthi) + & then + found_a_point_above_contour = 'y' + endif + endif + + if (found_a_point_below_contour == 'y' .or. + & found_a_point_above_contour == 'y') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a data' + print *,'!!! value that is less (greater) than the' + print *,'!!! current contour interval bound for a' + print *,'!!! min (max) and/or is less (greater) ' + print *,'!!! than the minimum (maximum) central ' + print *,'!!! value that we are centering the ' + print *,'!!! search on.' + print *,'!!! ' + print *,'!!! Central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Flagged value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of flagged value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we've made it this far, then we at least know that the +c gradient is still heading in the right direction. Do the +c check now to see if the value at this point is within our +c specific contour interval (there is the possibility that +c the value is beyond our interval, which will be checked +c for just below, and if that's the case, then that point +c will be processed in a subsequent iteration of this loop +c that encompasses that correct contour interval). + + found_a_point_in_our_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) >= contlo .and. fxy(irx,jrx) < conthi) + & then + found_a_point_in_our_contour = 'y' + endif + else + if (fxy(irx,jrx) > contlo .and. fxy(irx,jrx) <= conthi) + & then + found_a_point_in_our_contour = 'y' + endif + endif + + if (found_a_point_in_our_contour == 'y') then + ! We've found a data point in our interval, something + ! that is inside the closed contour, and it hasn't been + ! marked as being found in a previous iteration of this + ! loop, so mark it now and store the (i,j) location so + ! that we can scan a ring around this point in a + ! successive iteration of this loop for more potential + ! points within this interval... + + point_is_already_in_our_contour(irx,jrx) = .true. + + next_ring_ct = next_ring_ct + 1 + search_next_i(next_ring_ct) = irx + search_next_j(next_ring_ct) = jrx + +c print *,'at B, next_ring_ct= ',next_ring_ct +c & ,' search_next_i()= ',search_next_i(next_ring_ct) +c & ,' search_next_j()= ',search_next_j(next_ring_ct) + + num_pts_in_one_contour = num_pts_in_one_contour + 1 + temp_mask_i_loc(num_pts_in_one_contour) = irx + temp_mask_j_loc(num_pts_in_one_contour) = jrx + + if (get_last_isobar_flag == 'y') then + call calcdist (glon(ix),glat(jx) + & ,glon(irx),glat(jrx),dist,degrees) + rlast_distsum = rlast_distsum + dist + rlast_distct = rlast_distct + 1 + endif + +ctm +c print *,' ' +c print *,' PT IN! irx= ',irx,' jrx= ',jrx,' xval= ' +c & ,fxy(irx,jrx) +c print *,'next_ring_ct= ',next_ring_ct +c print *,'num_pts_in_one_contour= ' +c & ,num_pts_in_one_contour + endif + +c If we've made it this far AND the +c found_a_point_in_our_contour flag indicates that this +c point is not in our contour interval, then by default that +c means that this point is for a contour interval beyond +c what we're currently looking at. E.g., if we're looking +c at the contours around a 972 mb low and we're moving +c outward and currently checking the 984-988 mb contour +c interval, it means that we found, say, a gridpoint with +c 991 mb. So we want to mark that point for a future +c iteration of this loop that would be checking the +c 988-992 mb contour interval. + + if (found_a_point_in_our_contour /= 'y' .and. + & .not. point_is_already_in_next_contour(irx,jrx)) then + ! We've found a data point that is beyond our interval, + ! so this is not a concern for finding the bounds of + ! our current contour interval, but we want to mark + ! these points and remember them for the next iteration + ! of successive_scan_loop. (For example, suppose we + ! are currently searching for points in the 984-988 mb + ! range, and we find a point that is 990 -- mark it + ! here to be remembered when we scan for 988-992 mb). + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + if (fxy(irx,jrx) >= contlo_next .and. + & fxy(irx,jrx) < conthi_next) then + ! "NEXT_CONTOUR" Comment: + ! We've found a point that is in the very next + ! contour interval.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. + else if (fxy(irx,jrx) >= conthi_next) then + ! "BEYOND_CONTOUR" Comment: + ! This point is at least 1 contour interval beyond + ! the next contour interval. Dump the info into + ! these i and j arrays. This info will be used if + ! in the next iteration of single_contour_scan_loop, + ! next_contour_ct = 0. That would mean that we + ! have, e.g., an intensely deep low with a sharp + ! mslp gradient that essentially "skips" over a + ! contour interval. E.g., if using a 4 mb interval, + ! we go from 947 to 953 AND there are NO + ! intervening gridpoints in the 948-952 interval. + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) + endif + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + if (fxy(irx,jrx) > contlo_next .and. + & fxy(irx,jrx) <= conthi_next) then + ! See "NEXT_CONTOUR" comment just above.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. +c print *,'NEXT ncc= ',next_contour_ct +c & ,'next_contour_i()= ' +c & ,next_contour_i(next_contour_ct) +c & ,'next_contour_j()= ' +c & ,next_contour_j(next_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + else if (fxy(irx,jrx) <= contlo_next) then + ! See "BEYOND_CONTOUR" comment just above.... + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'BEYOND bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + endif + endif + endif + + enddo individual_ring_loop + + enddo multiple_ring_loop + + if (next_ring_ct > 0) then + iter = iter + 1 + else + icccret = 0 + still_scanning = .false. + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + num_found_contours = num_found_contours + 1 + closed_contour = 'y' + if (num_found_contours == 1) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Closed contour found ' + endif + + endif + endif + + enddo single_contour_scan_loop + + do insingle = 1,num_pts_in_one_contour + num_pts_in_all_contours = num_pts_in_all_contours + 1 + inall = num_pts_in_all_contours + hold_mask_i_loc(inall) = temp_mask_i_loc(insingle) + hold_mask_j_loc(inall) = temp_mask_j_loc(insingle) + enddo + + if (get_last_isobar_flag == 'y') then + if (cmaxmin == 'min') then + plastbar = conthi + else + plastbar = contlo + endif + if (rlast_distct > 0) then + rlastbar = rlast_distsum / float(rlast_distct) + rlastbar = rlastbar * 0.539638 ! convert km to nm + else + rlastbar = -999.0 + endif + endif + + enddo successive_contours_loop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'END SUM: num of iterations = ',isc_count + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_land_mask (imax,jmax,ix,jx,fract_land,valid_pt + & ,dx,dy,point_is_over_water,iclmret) +c +c ABSTRACT: This subroutine looks at the values for the land-sea +c mask surrounding an input (i,j) position to determine if less +c than 50% of the area surrounding the input (i,j) position within +c 75 km radius is land. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c +c OUTPUT: +c fract_land Fraction of points/area that is covered by land +c point_is_over_water y/n: A value of 'y' is returned if <50% +c of the points/area is covered by land +c iclmret Return code from this routine +c + USE grid_bounds; USE tracked_parms + USE trkrparms; USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + logical(1) valid_pt(imax,jmax) + character point_is_over_water*1 + integer, parameter :: numazim=8 + integer iazim,ibiret1,imax,jmax,ix,jx,iclmret,imct + real bear,targlat,targlon,xplon,yplat,rdist,xintrp_mask + real fract_land,dx,dy,xmask_sum +c + iclmret = 0 + +c First, calculate the longitude and latitude of the input ix and +c jx points. If the xplon value ends up being >360.0 (this can +c happen for basin-scale HWRF), don't worry about it. Just leave +c it be, as the trigonometry will work out the same for lons >360. + + xplon = glonmin + (ix-1)*dx + yplat = glatmax - (jx-1)*dy + + rdist = 75.0 ! (We will always look only 75 km radius out for + ! this particular land-sea mask application) + + imct = 0 + +c Now go around the storm via azimloop and get interpolated +c values of the land-sea mask at each azimuth at a radial +c distance of 75 km from the center point.... + + xmask_sum = 0.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 45.0 + + call distbear (yplat,xplon,rdist,bear,targlat,targlon) + + ! These calls to bilin_int_uneven pass a variable, level, + ! that is used for applications of interpolating wind + ! data. Here, we are instead interpolating the land-sea + ! mask data, so we don't care about the level, so just + ! pass a dummy value of 850, which never gets used. + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,850,'m',xintrp_mask,ibiret1) + + if (ibiret1 == 0) then + xmask_sum = xmask_sum + xintrp_mask + imct = imct + 1 + else + iclmret = 95 + return + endif + + enddo azimloop + +c Now get the mask value directly at the point that was input to +c this routine.... + + xmask_sum = xmask_sum + lsmask(ix,jx) + imct = imct + 1 + +c Now get the mean land fraction.... + + if (imct > 0) then + + fract_land = xmask_sum / float(imct) + if (fract_land < 0.50) then + point_is_over_water = 'y' + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Land check yes: Point is over water. ' + print *,' Land check value: fract_land= ',fract_land + endif + else + point_is_over_water = 'n' + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Land check NO: Point is over land. ' + print *,' Land check value: fract_land= ',fract_land + endif + endif + + else + + iclmret = 95 + return + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine get_ijplus1_check_wrap (imax,jmax,i,j,iplus1,jplus1 + & ,iminus1,jminus1,trkrinfo,igicwret) +c +c ABSTRACT: This subroutine takes an (i,j) position input and +c returns the four neighboring (i,j) points to the east, south, +c west and north. The routine checks for wrap around the GM, so +c that if, for example, you are on a global 360x181 grid and you +c are at point i=360, then i+1 = 361, so you need something to +c adjust that back to i = 1. Likewise, if you are at i=1 and +c looking for point i-1, it will adjust it to be point 360 +c instead of the meaningless point 0 (i=0). + + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer i,j,imax,jmax,iplus1,jplus1,iminus1,jminus1,igicwret + + igicwret = 0 + + jplus1 = j + 1 + jminus1 = j - 1 + iplus1 = i + 1 + iminus1 = i - 1 + + if (iplus1 > imax) then + if (trkrinfo%gridtype == 'global') then + iplus1 = iplus1 - imax ! If wrapping east of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is too close to the eastern bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested eastern ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',iplus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (iminus1 < 1) then + if (trkrinfo%gridtype == 'global') then + iminus1 = imax + iminus1 ! If wrapping west of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested western search boundary' + print *,'!!! is too close to the western bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested western ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested western i = ',iminus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (jplus1 > jmax .or. jminus1 < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The ' + print *,'!!! user-requested northern or southern search' + print *,'!!! boundary is too close to the bounds of the' + print *,'!!! grid. Cut back your requested northern or' + print *,'!!! southern boundary by a degree or 2 in the' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested northern j = ',jminus1 + print *,'!!! User-requested southern j = ',jplus1 + print *,'!!! jmax of grid = ',jmax + print *,' ' + endif + + igicwret = 91 + return + endif + + return + end + +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + SUBROUTINE qsort(x,ind,n) +c +c Code converted using TO_F90 by Alan Miller +c Date: 2002-12-18 Time: 11:55:47 + + IMPLICIT NONE + INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(12, 60) + + REAL (dp), INTENT(IN) :: x(n) + INTEGER, INTENT(OUT) :: ind(n) + INTEGER, INTENT(IN) :: n + +c *************************************************************************** +c +c ROBERT RENKA +c OAK RIDGE NATL. LAB. +c +c THIS SUBROUTINE USES AN ORDER N*LOG(N) QUICK SORT TO SORT A REAL (dp) +c ARRAY X INTO INCREASING ORDER. THE ALGORITHM IS AS FOLLOWS. IND IS +c INITIALIZED TO THE ORDERED SEQUENCE OF INDICES 1,...,N, AND ALL INTERCHANGES +c ARE APPLIED TO IND. X IS DIVIDED INTO TWO PORTIONS BY PICKING A CENTRAL +c ELEMENT T. THE FIRST AND LAST ELEMENTS ARE COMPARED WITH T, AND +c INTERCHANGES ARE APPLIED AS NECESSARY SO THAT THE THREE VALUES ARE IN +c ASCENDING ORDER. INTERCHANGES ARE THEN APPLIED SO THAT ALL ELEMENTS +c GREATER THAN T ARE IN THE UPPER PORTION OF THE ARRAY AND ALL ELEMENTS +c LESS THAN T ARE IN THE LOWER PORTION. THE UPPER AND LOWER INDICES OF ONE +c OF THE PORTIONS ARE SAVED IN LOCAL ARRAYS, AND THE PROCESS IS REPEATED +c ITERATIVELY ON THE OTHER PORTION. WHEN A PORTION IS COMPLETELY SORTED, +c THE PROCESS BEGINS AGAIN BY RETRIEVING THE INDICES BOUNDING ANOTHER +c UNSORTED PORTION. +c +c INPUT PARAMETERS - N - LENGTH OF THE ARRAY X. +c +c X - VECTOR OF LENGTH N TO BE SORTED. +c +c IND - VECTOR OF LENGTH >= N. +c +c N AND X ARE NOT ALTERED BY THIS ROUTINE. +c +c OUTPUT PARAMETER - IND - SEQUENCE OF INDICES 1,...,N PERMUTED IN THE SAME +c FASHION AS X WOULD BE. THUS, THE ORDERING ON +c X IS DEFINED BY Y(I) = X(IND(I)). +c +c ********************************************************************* + + ! NOTE -- IU AND IL MUST BE DIMENSIONED >= LOG(N) WHERE LOG HAS BASE 2. + + !********************************************************************* + + INTEGER :: iu(21), il(21) + INTEGER :: m, i, j, k, l, ij, it, itt, indx + REAL :: r + REAL (dp) :: t + + ! LOCAL PARAMETERS - + + ! IU,IL = TEMPORARY STORAGE FOR THE UPPER AND LOWER + ! INDICES OF PORTIONS OF THE ARRAY X + ! M = INDEX FOR IU AND IL + ! I,J = LOWER AND UPPER INDICES OF A PORTION OF X + ! K,L = INDICES IN THE RANGE I,...,J + ! IJ = RANDOMLY CHOSEN INDEX BETWEEN I AND J + ! IT,ITT = TEMPORARY STORAGE FOR INTERCHANGES IN IND + ! INDX = TEMPORARY INDEX FOR X + ! R = PSEUDO RANDOM NUMBER FOR GENERATING IJ + ! T = CENTRAL ELEMENT OF X + + IF (n <= 0) RETURN + + ! INITIALIZE IND, M, I, J, AND R + + DO i = 1, n + ind(i) = i + END DO + m = 1 + i = 1 + j = n + r = .375 + + ! TOP OF LOOP + + 20 IF (i >= j) GO TO 70 + IF (r <= .5898437) THEN + r = r + .0390625 + ELSE + r = r - .21875 + END IF + + ! INITIALIZE K + + 30 k = i + + ! SELECT A CENTRAL ELEMENT OF X AND SAVE IT IN T + + ij = i + r*(j-i) + it = ind(ij) + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) > t) THEN + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + END IF + + ! INITIALIZE L + + l = j + + ! IF THE LAST ELEMENT OF THE ARRAY IS LESS THAN T, + ! INTERCHANGE IT WITH T + indx = ind(j) + IF (x(indx) >= t) GO TO 50 + ind(ij) = indx + ind(j) = it + it = indx + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) <= t) GO TO 50 + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + GO TO 50 + + ! INTERCHANGE ELEMENTS K AND L + + 40 itt = ind(l) + ind(l) = ind(k) + ind(k) = itt + + ! FIND AN ELEMENT IN THE UPPER PART OF THE ARRAY WHICH IS + ! NOT LARGER THAN T + + 50 l = l - 1 + indx = ind(l) + IF (x(indx) > t) GO TO 50 + + ! FIND AN ELEMENT IN THE LOWER PART OF THE ARRAY WHCIH IS NOT SMALLER THAN T + + 60 k = k + 1 + indx = ind(k) + IF (x(indx) < t) GO TO 60 + + ! IF K <= L, INTERCHANGE ELEMENTS K AND L + + IF (k <= l) GO TO 40 + + ! SAVE THE UPPER AND LOWER SUBSCRIPTS OF THE PORTION OF THE + ! ARRAY YET TO BE SORTED + + IF (l-i > j-k) THEN + il(m) = i + iu(m) = l + i = k + m = m + 1 + GO TO 80 + END IF + + il(m) = k + iu(m) = j + j = l + m = m + 1 + GO TO 80 + + + ! BEGIN AGAIN ON ANOTHER UNSORTED PORTION OF THE ARRAY + + 70 m = m - 1 + IF (m == 0) RETURN + i = il(m) + j = iu(m) + + 80 IF (j-i >= 11) GO TO 30 + IF (i == 1) GO TO 20 + i = i - 1 + + ! SORT ELEMENTS I+1,...,J. NOTE THAT 1 <= I < J AND J-I < 11. + + 90 i = i + 1 + IF (i == j) GO TO 70 + indx = ind(i+1) + t = x(indx) + it = indx + indx = ind(i) + IF (x(indx) <= t) GO TO 90 + k = i + + 100 ind(k+1) = ind(k) + k = k - 1 + indx = ind(k) + IF (t < x(indx)) GO TO 100 + + ind(k+1) = it + GO TO 90 + END SUBROUTINE qsort + +c +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT11/FORT31 + subroutine open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + +C ABSTRACT: This subroutine must be called before any attempt is +C made to read from the input GRIB files. The GRIB and index files +C are opened with a call to baopenr. This call to baopenr was not +C needed in the cray version of this program (the files could be +C opened with a simple Cray assign statement), but the GRIB-reading +C utilities on the SP do require calls to this subroutine (it has +C something to do with the GRIB I/O being done in C on the SP, and +C the C I/O package needs an explicit open statement). +C +C INPUT: +c inp Contains user-input info on the date & data +C lugb The Fortran unit number for the GRIB data file +C lugi The Fortran unit number for the GRIB index file +c ifh integer index for lead time level +c gfilename If using individual files for each tau, gfilename will +c contain the grib data filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +c ifilename If using individual files for each tau, gfilename will +c contain the grib index filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +C lout The Fortran unit number for the output grib file +C +C OUTPUT: +C iret The return code from this subroutine + + USE inparms + USE verbose_output + + implicit none +c + type (datecard) inp + + logical(1) output_file_open + logical(1) file_open + character(*) gfilename,ifilename +c character(120) gopen_g_file,gopen_i_file +cPeng 05/28/2018 Bug Fixed for FV3-GFS Job Crashed. + character(255) gopen_g_file,gopen_i_file + character(2) lugb_c,lugi_c + character(6) enameb,enamei + integer igoret,iioret,iooret,lugb,lugi,lout,iret,nlen1,nlen2 + + iret=0 + + if (inp%file_seq == 'onebig') then + write(lugb_c,'(i2)')lugb + write(lugi_c,'(i2)')lugi + enameb='FORT'//adjustl(lugb_c) + enamei='FORT'//adjustl(lugi_c) + call get_environment_variable(enameb,gopen_g_file,status=igoret) + call get_environment_variable(enamei,gopen_i_file,status=iioret) +c if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then +cPENG---2018-06-07-------------------------------- + if (igoret /= 0 .or. iioret /= 0 ) then + gopen_g_file(1:5) = "fort." + gopen_i_file(1:5) = "fort." + write(gopen_g_file(6:7),'(I2)') lugb + write(gopen_i_file(6:7),'(I2)') lugi + endif + else + nlen1 = len_trim(gfilename) + gopen_g_file = trim(gfilename(1:nlen1)) + nlen2 = len_trim(ifilename) + gopen_i_file = trim(ifilename(1:nlen2)) + endif + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + +c print *,'gopen_g_file= ',gopen_g_file,'....' +c print *,'gopen_i_file= ',gopen_i_file,'....' + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end + +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT12 + subroutine read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + ii=1 + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + maxstorm = numtcv + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that you have the Fortran' + print *,'!!! unit assigned right in your script.' + endif + + iret = 99 + return + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT14 + subroutine read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + ii = numtcv + 1 + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1,1x + & ,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end + +cPENG----2018-06-07 ------------------------ + subroutine get_ushe_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanushe,igridushe,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,iushet) +c +cABSTRACT: This subroutine finds the minimum and mean U-shear values +c between 200 & 850 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cPENG----2018-06-07--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07------------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanushe,dx,dy,re,ri,parmlon,parmlat + real igridushe,imeanushe + integer n,ix1,ix2,npts,imax,jmax,iushet,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ushe_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + iushet = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_ushe_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + iushet = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max ushe values at 200 and 850 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_ushe_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_ushe_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeanushe = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (ushear(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid ushe data for this level, so + ! we first call barnes now to get the mean ushe + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then +c cvort_maxmin = 'max' +c else + cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'ushe' + & ,ushear(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanushe + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cPENG--------------------------- +c imeanushe = int (xmeanushe + 0.5) + imeanushe = xmeanushe + else + imeanushe = -999. + igridushe = -999. + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_ushe_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for ushe values will not be done.') + exit report_ushe_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanushe = -999. + igridushe = -999. + exit report_ushe_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeanushe,imeanushe + 621 format (1x,' xmeanushe= ',f9.3,' imeanushe= ',f9.3) + write (6,*) ' --- mean ushe raw = ',xmeanushe + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! ushe data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,ushear(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanushe,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cPENG--------------------------- +c igridushe = int (gridpoint_maxmin + 0.5) + igridushe = gridpoint_maxmin + else + igridushe = -999. + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridushe,ifilret + 623 format (1x, + & ' grid ushe= ',f9.3,' igrid ushe= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid ushe raw= ',gridpoint_maxmin + endif + + enddo report_ushe_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get U-shear for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +cPENG----2018-06-07-------------------------------------------------- +c----------------------------------------------------------------------- + subroutine get_rhum_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanrhum,igridrhum,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,irhumt) +c +c ABSTRACT: This subroutine finds the minimum and mean RH values +c at 500 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cPENG----2018-06-07--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07------------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanrhum,dx,dy,re,ri,parmlon,parmlat + real igridrhum,imeanrhum + integer n,ix1,ix2,npts,imax,jmax,irhumt,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_rhum_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + irhumt = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_rhum_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + irhumt = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max rhum values at 500 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_rhum_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_rhum_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeanrhum = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (rhumid(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid rhum data for this level, so + ! we first call barnes now to get the mean rhum + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then +c cvort_maxmin = 'max' +c else + cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'rhum' + & ,rhumid(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanrhum + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cPENG------------------------------------------------- +c imeanrhum = int (xmeanrhum + 0.5) + imeanrhum = xmeanrhum + else + imeanrhum = -99.0 + igridrhum = -99.0 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_rhum_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for rhum values will not be done.') + exit report_rhum_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanrhum = -99.0 + igridrhum = -99.0 + exit report_rhum_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeanrhum,imeanrhum + 621 format (1x,' xmeanrhum= ',f9.3,' imeanrhum= ',f9.3) + write (6,*) ' --- mean rhum raw = ',xmeanrhum + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! rhum data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,rhumid(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanrhum,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cPENG------------------------------------------------- +c igridrhum = int (gridpoint_maxmin + 0.5) + igridrhum = gridpoint_maxmin + else + igridrhum = -99.0 + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridrhum,ifilret + 623 format (1x, + & ' grid rhum= ',f9.3,' igrid rhum= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid rhum raw= ',gridpoint_maxmin + endif + + enddo report_rhum_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get RH for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end + +cPENG----2018-06-07------------------------------------------- +c This is for area mean caculations +c--------------------------------------------------------------------- + subroutine fix_latlon_mean_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +cPENG----2018-06-07--------------------------------------- + real dsum, dnum + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.10) then + grfact = 4 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif +cPENG----2012--04--18---10X10 average ------------------------- + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (4*grfact) + iend = ipfix + (5*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (5*grfact) + iend = ipfix + (4*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (4*grfact) + iend = ipfix + (4*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (5*grfact) + jend = jpfix + (4*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (4*grfact) + jend = jpfix + (5*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (4*grfact) + jend = jpfix + (4*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix +cPENG----2018-06-07--------------------------------------- + dsum=0.0 + dnum=0.0 + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif +cPENG----2018-06-07--------------------------------------- + dsum=dsum+fxy(i,j) + dnum=dnum+1 + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +cPENG----2018-06-07--------------------------------------- + if (dnum.gt.0.0) then + gridpoint_maxmin=dsum/dnum + else + gridpoint_maxmin=-9999.0 + endif + + print *,'istart=',istart,'iend=',iend + print *,'jstart=',jstart,'jend=',jend + print *,'End of fix_latlon_mean_ij, gridpoint_maxmin = ' + & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +cPENG----2018-06-07------10X10 area mean temperature----------------- +c----------------------------------------------------------------------- + subroutine get_temp_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeantemp,igridtemp,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,itempt) +c +c ABSTRACT: This subroutine finds the maximum and mean temperature +c between 300 & 500 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cPENG----2018-06-07--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07------------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeantemp,dx,dy,re,ri,parmlon,parmlat + real igridtemp,imeantemp + integer n,ix1,ix2,npts,imax,jmax,itempt,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_temp_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + itempt = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_temp_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + itempt = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max temp values at 300 and 500 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_temp_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_temp_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeantemp = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (tmean(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid temp data for this level, so + ! we first call barnes now to get the mean temp + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' +c else +c cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'temp' + & ,tmean(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeantemp + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cPENG------------------- +c imeantemp = int (xmeantemp + 0.5) + imeantemp = xmeantemp + else + imeantemp = -99.0 + igridtemp = -99.0 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_temp_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for temp values will not be done.') + exit report_temp_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeantemp = -99.0 + igridtemp = -99.0 + exit report_temp_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeantemp,imeantemp + 621 format (1x,' xmeantemp= ',f9.3,' imeantemp= ',f9.3) + write (6,*) ' --- mean temp raw = ',xmeantemp + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! temp data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,tmean(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeantemp,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cPENG------------------- +c igridtemp = int (gridpoint_maxmin + 0.5) + igridtemp = gridpoint_maxmin + else + igridtemp = -99.0 + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridtemp,ifilret + 623 format (1x, + & ' grid temp= ',f9.3,' igrid temp= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid temp raw= ',gridpoint_maxmin + endif + + enddo report_temp_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get T-mean for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_gfs.f_10152019 b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_gfs.f_10152019 new file mode 100644 index 0000000000..bf4b58eec0 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_main_gfs.f_10152019 @@ -0,0 +1,27270 @@ + program trakmain +c +c$$$ MAIN PROGRAM DOCUMENTATION BLOCK +c +c Main Program: GETTRK Track model vortices +C PRGMMR: MARCHOK ORG: NP22 DATE: 2002-05-20 +c +c ABSTRACT: This program tracks the average of the max or min +c of several parameters in the vicinity of an input +c first guess (lat,lon) position of a vortex in order to give +c forecast position estimates for that vortex for a given numerical +c model. For the levels 700 & 850 mb, the tracked parameters are: +c Relative vorticity (max), wind magnitude (min), and geopotential +c height (min). Also tracked is the min in the MSLP. So many +c parameters are tracked in order to provide more accurate position +c estimates for weaker storms, which often have poorly defined +c structures/centers. Currently, the system is set up to be able +c to process GRIB input data files from the GFS, MRF, UKMET, GDAS, +c ECMWF, NGM, NAM and FNMOC/NAVGEM models. Two 1-line files +c are output from this program, both containing the forecast fix +c positions that the tracker has obtained. One of these output +c files contains the positions at every 12 hours from forecast +c hour 0 to the end of the forecast. The other file is in ATCF +c format, which is the particular format needed by the Tropical +c Prediction Center, and provides the positions at forecast hours +c 12, 24, 36, 48 and 72, plus the maximum wind near the storm center +c at each of those forecast hours. +c +c Program history log: +c 98-03-16 Marchok - Original operational version. +c 98-07-15 Marchok - Added code to calculate radii of gale-, storm-, +c and hurricane-force winds in each quadrant. +c 99-04-01 Marchok - Added code to be able to read in 4-digit years +c off of the TC Vitals records. +c Added code, including subroutine is_it_a_storm, +c to make a better determination of whether or +c not the center that was found at each time is +c the center of a storm, and not just a passing +c vort max, etc. +c 99-06-15 Marchok - Fixed a bug in calcdist that was triggered by a +c rounding error sending a number just above 1 +c into ACOS to get the distance between 2 +c identical points (which, obviously, is 0). +c 00-06-20 Marchok - Added GDAS option for vortex relocation work. +c Changed nhalf from 3 to 5. Relaxed the +c requirements for pthresh and vthresh. +c 00-11-30 Marchok - Added ability to handle GFDL and NCEP Ensemble +c model data. Extended time range to be able to +c handle 5-day capability. Forecast hours are +c now input via a namelist (easiest way to account +c for NAM, GFS and GFDL having different forecast +c lengths at 00/12z and 06/18z). Model ID's are +c now input via a namelist (makes it easier, for +c example, to run for many different ensemble +c members). Added new output, the atcfunix +c format, needed for 5-day forecasts. +c 01-08-24 Marchok Fixed a bug in rvcal and getgridinfo. When a +c grid that was south-->north is flipped in +c conv1d2d_real to be north-->south, the scanning +c mode flag remains 64 and what we would consider +c the max and min latitudes are reversed, so I +c added code to correct this in both routines. +c 02-05-20 Marchok Weakened the mslp gradient threshold and v850 +c threshold in is_it_a_storm to cut down on the +c number of dropped storms. +c 03-03-18 Marchok Fixed a bug in get_ij_bounds that was allowing +c a cos(90) and cos(-90), which then led to a +c divide by zero. +c 05-08-01 Marchok Updated to allow tracking of ECMWF hi-res, ECMWF +c ensemble, CMC hi-res, CMC ensemble, NCEP +c ensemble. +c 06-11-07 Marchok Updated to locate, and report to the atcfunix +c file, the value of the gridpoint minimum value +c of mslp. Previously, the barnes-averaged +c value had been used. +c 08-01-10 Marchok Changed the storm ID for genesis tracking so +c that the ID includes info +c on storm detection location & time. Added +c algorithms for Hart's cyclone phase space. +c Added new output fields to the atcfunix +c records, actually creating a modified atcfunix +c record, to include things such as the mean & +c max values of zeta850 & zeta700 centered on +c the storm, the speed & direction of storm +c translation, and the Hart CPS parameters. +c 10-01-07 Marchok - input grib lead time can be hrs or minutes +c - added code for warm core check +c - added code to detect genesis +c - added code to report on sfc wind structure +c - added buffer ("grid_buffer") to avoid fixing +c center to boundaries on regional grids +c - modified rvcal to report missing zeta values +c as background coriolis instead of -999, since +c the -999 was messing up center-fixing +c - added 10-m wind and sfc zeta as center-fixing +c parms. +c +c 10-05-25 Slocum Add verbose feature to code +c 0 = Not terminal output, 1 = error messages only +c 2 = all output +c +c 10-05-26 Marchok - added flags and code to check the temporal +c consistency of the mslp closed contour and +c Vt850 checks for tcgen and midlat cases. +c +c 13-04-01 Marchok Added code to upgrade the wind radii diagnosid. +c Hurricane Sandy exposed an issue with the +c tracker for large storms. The code was modified +c to use an iterative technique that can +c diagnose radii for large storms but still +c accurately diagnost radii for small storms. See +c subroutine getradii for more details. +c +c 15-11-01 Marchok Replaced the routine which tracks the wind +c minimum at the center of a storm, as that +c routine proved troublesome with very hi-res +c grids (0.02-deg) from HWRF for very small +c storms. This has been replaced with a routine +c that looks for "wind circulation difference", +c whereby the center for this parm is located at +c the spot where the tangential wind circulation +c minus the wind magnitude at the candidate +c center position is maximized. ALSO: Added in +c tracking of thickness as an additional +c tracked parm. ALSO: Added a separate verbose +c flag for only the GRIB2 read diagnostics, which +c can be voluminous. +c +c 16-09-01 Marchok Added in the ability to read in NetCDF files. +c As with GRIB data, the NetCDF data must be on +c a lat/lon grid. +c +c 17-08-31 Marchok Added a logical bitmap capability for NetCDF +c files to prevent the accessing of missing data. +c Also modified the code to permit more accurate +c reporting of the grid point value of the +c minimum SLP for reporting to the atcfunix file. +c Finally, fixed a bug (reported by JTWC) whereby +c radii were being reported for thresholds that +c were in exceedance of the tracker-diagnosed +c Vmax (e.g., 34-kt radii for a storm with +c Vmax = 25 kts). +c +c Input files: +c unit 11 Unblocked GRIB1 file containing model data +c unit 12 Text file containing TC Vitals card for current time +c unit 31 Unblocked GRIB index file +c +c Output files: +c unit 61 Output file with forecast positions every 12h from +c vt=00h to the end of the forecast +c unit 62 Output file in ATCF format, with forecast positions +c at vt = 12, 24, 36, 48 and 72h, plus wind speeds. +c unit 63 Output file with forecast wind radii for 34, 50 and +c 64 knot thresholds in each quadrant of each storm. +c +c Subprograms called: +c read_nlists Read input namelists for input date & storm number info +c read_tcv_card Read TC vitals file to get initial storm position +c getgridinfo Read GRIB file to get basic grid information +c tracker Begin main part of tracking algorithm +c +c Attributes: +c Language: Standard Fortran_90 +c +c$$$ +c +c------- +c +c LOCAL: +c +c ifhours: Integer array holding numerical forecast times for +c the input model (99 = no more times available). +c These values are read in via a namelist. +c Model numbers used: (1) GFS, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) NAM, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble (13) SREF +c Ensemble, (14) NCEP Ensemble (from ensstat mean +c fields), (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) Ensemble RELOCATION (21) UKMET hi-res (NHC) +c (23) FNMOC Ensemble +c stormswitch: This switch tells how to handle each storm in +c the TCV file: +c 1 = process this storm for this forecast hour. +c 2 = Storm was requested to be tracked, but either +c the storm went off the grid (regional models), +c the storm dissipated, or the program was +c unable to track it. +c 3 = Storm was NOT requested to be tracked at all. +c storm: An array of type tcvcard. Each member of storm +c contains a separate TC Vitals card. +c maxstorm: Maximum number of storms the system is set up to +c handle at any 1 time. +c slonfg,slatfg: Holds first guess positions for storms. The +c very first, first guess position is read from the +c TC vitals card. (maxstorm,maxtime) +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms) +c + USE def_vitals; USE inparms; USE set_max_parms; USE level_parms + USE trig_vals; USE atcf; USE trkrparms; USE verbose_output + USE netcdf_parms +c + implicit none +c + logical(1) file_open + integer date_time(8) + character (len=10) big_ben(3) + character :: ncfile*180,ncfile_has_hour0*1 + integer itret,iggret,iicret,igcret,iret,ifhmax,maxstorm,numtcv + integer iocret,enable_timing,ncfile_id,ncfile_tmax,irnhret + integer, parameter :: lugb=11,lugi=31,lucard=12,lgvcard=14,lout=51 +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + +c -------------------------------------------------------- + + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: beginning ... ',i2.2,':',i2.2,':',i2.2) + + call w3tagb('GETTRK ',1999,0104,0058,'NP22 ') + + pi = 4. * atan(1.) ! Both pi and dtr were declared in module + dtr = pi/180.0 ! trig_vals, but were not yet defined. + ncfile_has_hour0 = 'n' ! Default value; set in read_netcdf_hours +c + call read_nlists (inp,trkrinfo,netcdfinfo) + enable_timing=trkrinfo%enable_timing + + call read_fhours (ifhmax) + + call read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_tcv_card, num vitals = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_tcv_card, rc= ',iret + endif + goto 890 + endif + + call read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_gen_vitals, total number of vitals (both' + & ,' TC and non-TC) now = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_gen_vitals, rc= ' + & ,iret + endif + goto 890 + endif + + if (inp%file_seq == 'onebig') then + if (trkrinfo%inp_data_type == 'netcdf') then + ncfile = netcdfinfo%netcdf_filename + print *,' ' + print *,'before open_ncfile call, ncfile= ',ncfile + call open_ncfile (ncfile,ncfile_id) + print *,'after open_ncfile call, ncfile_id= ',ncfile_id + call read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) + if (irnhret /= 0) then + print *,'(/,a32,a5,i4,/)','!!! ERROR: in read_netcdf_hours,' + & ,' rc= ',irnhret + goto 890 + endif + else + call open_grib_files (inp,lugb,lugi,'dummy','dummy',lout,iret) + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: in open_grib_files, rc= ' + & ,iret + goto 890 + endif + endif + endif + + call tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +890 continue + + igcret=0 + iicret=0 + iocret=0 + + inquire (unit=lugb, opened=file_open) + if (file_open) call baclose(lugb,igcret) + inquire (unit=lugi, opened=file_open) + if (file_open) call baclose(lugi,iicret) + inquire (unit=lout, opened=file_open) + if (file_open) call baclose(lout,iocret) + if ( verb .ge. 3 ) then + print *,'baclose: igcret= ',igcret,' iicret= ',iicret + print *,'baclose: iocret= ',iocret + endif + call w3tage('GETTRK ') +c + stop + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +c ABSTRACT: This subroutine is the core of the program. It contains +c the main loop for looping through all the forecast hours and all +c the storms. Basically, the way it works is that it has an outer +c loop that loops on the forecast hour. At the beginning of this +c loop, the data are read in for all parameters and levels needed +c for tracking. The full regional or global grid is read in. +c If vorticity was not read in (some of the centers do not send us +c vorticity), then vorticity calculations are done on the whole +c grid at both 850 and 700 mb. Then the program goes into the inner +c loop, which loops on storm number (program originally set up to +c handle a max of 15 storms). For each storm, subroutine +c find_maxmin is called for the following parameters: Rel Vort and +c geopotential hgt at 700 & 850 mb, and MSLP. Within find_maxmin, +c a barnes analysis is performed over the guess position of the +c storm to find the max or min value, and then iteratively, the +c grid size is cut in half several times and the barnes analysis +c rerun to refine the positioning of the max or min location. After +c the center positions for these parameters have been obtained, +c subroutine get_uv_center is called to get a center fix for the +c minimum in the wind field, specifically, a minimum in the +c magnitude of the wind speed (vmag). The calculation of the vmag +c minimum is done differently than the calculation for the other +c parameters; for vmag, the grid near the storm center guess +c position is interpolated down to a very fine grid, and then +c find_maxmin is called and a barnes analysis is done on that +c smaller grid. For vmag, there are no further calls made to barnes +c with a smaller grid, since the grid has already been interpolated +c down to a smaller grid. Once all of the parameter center fixes +c have been made, subroutine fixcenter is called to average these +c positions together to get a best guess fix position. Then a check +c is done with a call to subroutine is_it_a_storm to make sure that +c the center that we have found does indeed resemble a tropical +c cyclone. Finally, subroutine get_next_ges is called to make a +c guess position for the next forecast time for this storm. +c +c INPUT: +c inp contains input date and model number information +c maxstorm maximum # of storms to be handled +c numtcv number of storms read off of the tcvitals file +c ifhmax max number of analysis & forecast times to be handled +c trkrinfo derived type that holds/describes various tracker parms +c ncfile if the input data type is netcdf, then this ncfile +c variable contains the name of the netcdf file +c ncfile_id if the input data type is netcdf, then this ncfile_id +c variable contains an integer id assigned to the netcdf +c file from the open_ncfile subroutine +c ncfile_has_hour0 character flag (y|n) that, if the tracker is +c running on NetCDF data, tells if the NetCDF file +c actually contains hour0 data or not (some, like the +c 2016 version of FV3, do not). +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file itself in +c subroutine read_netcdf_fhours. +c +c OUTPUT: +c itret return code from this subroutine +c +c LOCAL PARAMETERS: +c storm contains the tcvitals for the storms +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c maxtime Max number of forecast times program can track +c maxtp Max number of tracked parameters program will track. +c Currently (7/2015), this maxtp is 11, and these 11 are +c listed just a few lines below. +c readflag L Indicates status of read for each of 16 parms: +c 1: 850 mb absolute vorticity +c 2: 700 mb absolute vorticity +c 3: 850 mb u-comp +c 4: 850 mb v-comp +c 5: 700 mb u-comp +c 6: 700 mb v-comp +c 7: 850 mb gp hgt +c 8: 700 mb gp hgt +c 9: MSLP +c 10: near-surface u-comp +c 11: near-surface v-comp +c 12: 500 mb u-comp +c 13: 500 mb v-comp +c 14: Mean temperature, centered at 400 mb +c 15: 500 mb gp hgt +c 16: 200 mb gp hgt +c 17: Land-Sea Mask (for use in tcgen applications, and +c even there, it's optional) +c +c calcparm L indicates which parms to track and which not to. +c Array positions are defined exactly as for clon +c and clat, listed next, except that, in general, when +c flag 3 is set to a value, flag 4 is set to the same +c value as 3, and when flag 5 is set to a value, flag +c 6 is set to the same value as 5. This is because +c 3 & 4 are for the 850 mb winds, and if either u or +c v is missing, we obviously can't calculate the +c magnitude of the wind. The same applies for 5 & 6, +c which are for the 700 mb winds. And also for reference, +c here is a list of all the variables & levels for the +c tracked parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms). +c For the third position (max_#_parms), here they are: +c 1: Relative vorticity at 850 mb +c 2: Relative vorticity at 700 mb +c 3: Wind circulation difference at 850 mb +c 4: NOT CURRENTLY USED +c 5: Wind circulation difference at 700 mb +c 6: NOT CURRENTLY USED +c 7: Geopotential height at 850 mb +c 8: Geopotential height at 700 mb +c 9: Mean Sea Level Pressure +c 10: Wind circulation difference at 10 m +c 11: Relative vorticity at 10 m +c 12: Lower-level thickness (500-850) +c 13: Upper-level thickness (200-500) +c 14: Deep-Layer thickness (200-850) +c +c xmaxwind Contains maximum near-surface wind near the storm +c center for each storm at each forecast hour. +c stderr Standard deviation of the position "errors" of the +c different parameters for each storm at each time. +c fixlat,fixlon: Contain the final coordinates for each storm at +c each forecast hour. These coordinates are a +c weighted average of all the individual parameter +c positions (hgt, zeta, mslp, vmag). +c cvort_maxmin: Contains the characters 'max' or 'min', and is +c used when calling the find_maxmin routine for the +c relative vorticity (Look for max in NH, min in SH). +c vradius Contains the distance from the storm fix position to +c each of the various near-surface wind threshhold +c distances in each quadrant. +c (3,4) ==> (# of threshholds, # of quadrants) +c See subroutine getradii for further details. +c wfract_cov Fractional coverage (areal coverage) of winds +c exceeding a certain threshold (34, 50, 64 kts) in +c each quadrant. +c (5,5,3) ==> (# of quadrants + 1, # of distance bins, +c # of thresholds). +c The "extra" array size for quadrants (5, instead of 4) +c is there to hold the total (i.e., "whole disc") +c statistics. +c See subroutine get_fract_wind_cov for further details +c +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c isastorm Character array used in the call to is_it_a_storm, +c tells whether the minimum requirement for an MSLP +c gradient was met (isastorm(1)), whether for the midlat +c and tcgen cases if a closed mslp contour was found +c (isastorm(2)), and if a circulation exists at 850 mb +c (isastorm(3)). Can have a value of 'Y' (requirement +c met), 'N' (requirement not met) or 'U' (requirement +c undetermined, due to the fact that no center location +c was found for this parameter). +c maxmini These 2 arrays contain the i and j indeces for the +c maxminj max/min centers that are found using the rough check +c in first_ges_ctr and subsequent routines. Only needed +c for a midlatitude or a genesis run, NOT needed for a +c TC tracker run. +c stormct Integer: keeps and increments a running tab of the +c number of storms that have been tracked at any time +c across all forecast hours. Used only for midlat or +c tcgen runs. +c gridprs This contains the actual value of the minimum pressure +c at a gridpoint. The barnes analysis will return an +c area-averaged value of pressure; this variable will +c contain the actual minimum value at a gridpoint near +c the lat/lon found by the barnes analysis. +c closed_mslp_ctr_flag This flag keeps track of the value of the +c closed contour flag returned from subroutine +c check_closed_contour. +c vt850_flag This flag keeps track of the value of the flag for +c the 850 mb Vt check. +c----- +c + USE def_vitals; USE inparms; USE tracked_parms; USE error_parms + USE set_max_parms; USE level_parms; USE grid_bounds; USE trkrparms + USE contours; USE atcf; USE radii; USE trig_vals; USE phase + USE gen_vitals; USE structure; USE verbose_output + USE waitfor_parms; USE module_waitfor; USE netcdf_parms + USE tracking_parm_prefs +c + implicit none +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (cint_stuff) contour_info +c + character, allocatable :: closed_mslp_ctr_flag(:,:)*1 + character, allocatable :: vt850_flag(:,:)*1 + character :: r34_check_okay*1,had_to_try_backup_850_vt_check*1 + character :: need_to_expand_r34(4)*1,ncfile_has_hour0*1 + character*(*), intent(in) :: ncfile + integer :: ncfile_id +c integer, parameter :: nreadparms=17 +cPENG----2018-06-07 ------------------------ + integer, parameter :: nreadparms=19 + + real, allocatable :: prstemp(:),iwork(:) + integer, parameter :: numdist=14,numquad=4,lout=51 + integer, allocatable :: prsindex(:) + integer imax,jmax,ifh,ist,irf,jj,istmp,ifhtemp,itret,ivpa + integer isiret1,isiret2,isiret3,idum,m,iix,jjx,imode,numtcv + integer iha,isa,iua,iva,iza,maxstorm,ivort,ifix,jfix,issret + integer imoa,imoca,iksa,isda,ileadtime,leadtime_check + integer ioaret,ioaxret,ifgcret,ifmret,igugret,isoiret,icccret + integer igrret,igmwret,iorret,ignret,iovret,icbret,igucret,ita +cPENG----2018-06-07 ------------------------ + integer ish, irh + + integer ifilret,ifret,iaret,isret,iotmret,iwa,iisa,sl_counter + integer iicret,igcret,pfcret,igwcret,imbowret,iatret + logical(1), allocatable :: valid_pt(:,:) + logical(1), allocatable :: masked_outc(:,:),masked_out(:,:) + logical(1) readflag(nreadparms),calcparm(maxtp,maxstorm) + logical(1) tracking_previously_known_storms + logical(1) need_to_flip_lats,need_to_flip_lons + logical(1) file_open,first_time_thru_getradii + character cvort_maxmin*3,isastorm(3)*1,ccflag*1,gotten_avg_value*1 + character cmaxmin*3,get_last_isobar_flag*1,wcore_flag*1 + character gfilename*120,ifilename*120,gridmove_status*7 + integer vradius(3,4),igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) +cPENG----2018-06-07 ------------------------ + real igridushe,imeanushe + real igridrhum,imeanrhum + real igridtemp,imeantemp + + integer maxmini(maxstorm),maxminj(maxstorm),pdf_ct_bin(16) + integer ifcsthour,stormct,prevstormct,kf,istmspd,istmdir,iggret + integer igiret,iuret,jdum,icount,ilonfix,jlatfix,igpret,ifhmax + integer ibeg,jbeg,iend,jend,ix1,ix2,n,ilev,npts,icpsa,igzvret +cPENG----2018-06-07 ------------------------ + integer iushet, irhumt, itempt + real wcore_mean_val,wcore_point_max + + integer igfwret,ioiret,igisret,iofwret,iowsret,igwsret,igscret + integer pdf_ct_tot,lugb,lugi,iret,icmcf,iccfh,ivt8f + integer waitfor_gfile_status,waitfor_ifile_status,ncfile_tmax + integer wait_max_ifile_wait,ivr,r34_good_ct,itha,ilma,inctcv + integer date_time(8) + character (len=10) big_ben(3) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridprs(maxstorm,maxtime) + real wfract_cov(5,5,3) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real ike(max_ike_cats) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmaxwind(maxstorm,maxtime),xmeanzeta + real stderr(maxstorm,maxtime),xval(maxtp),cps_vals(3) + real gridpoint_maxmin,dist,distnm,xknots,xmaxspeed + real uvgeslon,uvgeslat,xavg,stdv,search_cutoff,re,ri,dx,dy + real xinp_fixlat,xinp_fixlon,degrees,plastbar,rlastbar + real xinterval_fhr,cc_time_sum_tot,cc_time_sum_yes + real rmax,sdp,wdp,paramb,vtl_slope,vtu_slope + real xsfclon,xsfclat,cc_time_pct,radmax,r34_dist_thresh + real prev_latmax,prev_latmin,prev_lonmax,prev_lonmin + real vradius_km,hold_old_contint,tcv_max_wind_ms + real tcv_mslp_pa,r34_from_tcv,roci_from_tcv + real proci_from_tcv,prs_contint_thresh + integer enable_timing,igrct + character(pfc_cmd_len) :: pfc_final +c + prev_latmax = -999.0 + prev_latmin = -999.0 + prev_lonmax = -999.0 + prev_lonmin = -999.0 + enable_timing=trkrinfo%enable_timing + icmcf = 0 + ivt8f = 0 + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + allocate (closed_mslp_ctr_flag(maxstorm,ifhmax),stat=icmcf) + allocate (vt850_flag(maxstorm,ifhmax),stat=ivt8f) + ! Initialize flags to 'u', not 'n'. That way, + ! when we are evaluating its value back over recent past hours, + ! we can distinguish a "no" value from an initialized value of + ! 'u' for which a storm hadn't yet been detected. + closed_mslp_ctr_flag = 'u' + vt850_flag = 'u' + endif + + allocate (prsindex(maxstorm),stat=iisa) + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iisa /= 0 .or. iva /= 0 .or. iwa /= 0 .or. icmcf /= 0 .or. + & ivt8f /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating prsindex,' + print *,'!!! prstemp or iwork array for storms: iisa = ',iisa + print *,'!!! iva= ',iva,' iwa= ',iwa,' icmcf= ',icmcf + print *,'!!! ivt8f= ',ivt8f + endif + itret = 94 + return + endif + + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + clon = 0.0 + clat = 0.0 + stderr = stermn ! initialize stderr to 0.1 (error_parms) + itret = 0 + xmaxwind = 0.0 + stormct = 0 + + ! It is critical to initialize the gridprs array to something + ! greater than normal atmospheric pressures (I've chosen 9999.99 + ! mb). This is so that in the sort on pressure before stormloop, + ! the top of the sorting index array will be filled with pressure + ! values from active storms, while those inactive 9999 storms + ! will fill the bottom of the sorting index array (prsindex). + + gridprs = 999999.0 + fixlon = -999.0 + fixlat = -999.0 + + if (inp%file_seq == 'multi') then + ! Each tau will have a separate file, starting with unit + ! number 200 (GRIB data) and 5200 (GRIB index file) and + ! incrementing upwards from there for each tau. + if (trkrinfo%gribver == 1) then + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + else + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + endif + else + ! All lead times are included in one big file. These values + ! for lugb and lugi will remain static for all taus. + lugb = 11 + lugi = 31 + endif + + ifh = 1 + + if ( verb .ge. 3 ) then + print *,'top of tracker, ifh= ',ifh,' ifhmax= ',ifhmax + endif + + ifhloop: do while (ifh <= ifhmax) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------*' + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* New forecast hour: ',i4,':',i2.2) + print *,'*-------------------------------------------*' + endif + + if (inp%file_seq == 'multi') then + + lugb = lugb + 1 + lugi = lugi + 1 + + call get_grib_file_name (ifh,gfilename,ifilename) + + if (use_waitfor == 'y') then + + ! First check for existence of grib file.... + + call waitfor(trim(gfilename),waitfor_gfile_status + & ,wait_min_age,wait_min_size,wait_max_wait + & ,wait_sleeptime) + if (waitfor_gfile_status /= 0) then + print *,' ' + write(6,405) + write(6,406) wait_max_wait,trim(gfilename) + 405 format('ERROR: TIMEOUT from waitfor for GRIB file.') + 406 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + ! Now check for existence of index file. Use a separate + ! max_wait time -- a much shorter one -- since once the + ! grib file is there, the index file should appear within + ! a matter of seconds. Also, the index file is much + ! smaller, so set the wait_min_size accordingly. + + wait_max_ifile_wait = 180 + wait_min_size = 500 + call waitfor(trim(ifilename),waitfor_ifile_status + & ,wait_min_age,wait_min_size,wait_max_ifile_wait + & ,wait_sleeptime) + if (waitfor_ifile_status /= 0) then + print *,' ' + write(6,415) + write(6,416) wait_max_ifile_wait,trim(ifilename) + 415 format('ERROR: TIMEOUT from waitfor for INDEX file.') + 416 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + endif + + call open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: from open_grib_files, rc= ' + & ,iret + print *,'!!! Files after hour0 are missing, ' + & ,'exiting normally' + stop 0 + endif + endif + + if (trkrinfo%inp_data_type == 'grib') then + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is CLOSED' + endif + endif + + !-------------------------------------------------------------- + ! Within this next IF statement, we deal with writing out atcf + ! records for storms for the case in which we have netcdf data, + ! but that netcdf data does not have hour0 data (as of Nov 2016, + ! this is the case for FV3 data). In this case, we write out + ! missing values for the hour0 time, and then we update the + ! guess for next lead time by extrapolating data from TC Vitals. + ! Note in the IF statement itself, "iftotalmins" is the array + ! of *user-requested* lead times, meaning that the user has + ! requested to look at hour0, but the ncfile_has_hour0 flag + ! indicates the hour0 time is not in the NetCDF data. + !-------------------------------------------------------------- + + if (ifh == 1 .and. iftotalmins(ifh) == 0 .and. + & trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') then + + null_netcdf_hour0_storm_loop: do inctcv = 1,numtcv + + call output_atcfunix (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,inctcv +c & ,0,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,inctcv + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',inctcv + write (6,431) storm(inctcv)%tcv_storm_id + & ,storm(inctcv)%tcv_storm_name + 431 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + + call advect_tcvitals_from_hour0 (slonfg,slatfg,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) + + if (iatret /= 0) then + fixlon (inctcv,ifh) = -999.0 + fixlat (inctcv,ifh) = -999.0 + stormswitch(inctcv) = 2 + cycle null_netcdf_hour0_storm_loop + endif + + stormswitch(inctcv) = 1 + + enddo null_netcdf_hour0_storm_loop + + ifh = ifh + 1 + cycle ifhloop + + endif + + !-------------------------------------------------------------- + ! Make call to getgridinfo in order to get info on the imax, + ! jmax, as well as the x- and y-increments, and also to see if + ! the grid is correctly oriented for the tracker so that the + ! data go north to south and west to east or if we need to flip + ! either the lats or the lons. + !-------------------------------------------------------------- + + if (trkrinfo%inp_data_type == 'grib') then + call getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) + else + print *,' ' + print *,'!!! ERROR: trkrinfo%inp_data_type NOT VALID ' + print *,'!!! trkrinfo%inp_data_type= ',trkrinfo%inp_data_type + print *,'!!! Should have value of grib or netcdf.' + print *,'!!! EXITING....' + print *,' ' + stop 93 + endif + + if (iggret == 0) then + if ( verb .ge. 1 ) then + print *,'TEST after getgridinfo in sub tracker, ' + & ,'iggret= ',iggret + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in getgridinfo, rc= ' + & ,iggret + endif + stop 95 + endif + + if (inp%modtyp == 'regional' .and. inp%nesttyp == 'moveable') + & then + if (glatmax == prev_latmax .and. glatmin == prev_latmin .and. + & glonmax == prev_lonmax .and. glonmin == prev_lonmin) then + ! The moveable, nested regional grid has not moved since + ! the last lead time. This could be an indication that the + ! model lost the storm and so the grid has not moved to + ! stay with the cyclone center. Set a flag to indicate this. + gridmove_status = 'stopped' + else + gridmove_status = 'moving' + endif + else + gridmove_status = 'notappl' + endif + + prev_latmax = glatmax + prev_latmin = glatmin + prev_lonmax = glonmax + prev_lonmin = glonmin + + gotten_avg_value = 'n' + +c First, allocate the working data arrays.... + + if (allocated(valid_pt)) deallocate (valid_pt) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) +cPENG----2018-06-07------------------------------- + if (allocated(ushear)) deallocate (ushear) + if (allocated(rhumid)) deallocate (rhumid) + + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + + ! Allocate all of the allocatable arrays.... + + allocate (valid_pt(imax,jmax),stat=ivpa) + allocate (zeta(imax,jmax,nlevzeta),stat=iza) + allocate (u(imax,jmax,nlevs),stat=iua) + allocate (v(imax,jmax,nlevs),stat=iva) + allocate (hgt(imax,jmax,nlevhgt),stat=iha) + allocate (slp(imax,jmax),stat=isa) + allocate (tmean(imax,jmax),stat=ita) +cPENG----2018-06-07------------------------------- + allocate (ushear(imax,jmax),stat=ish) + allocate (rhumid(imax,jmax),stat=irh) + + allocate (thick(imax,jmax,nlevthick),stat=itha) + allocate (lsmask(imax,jmax),stat=ilma) + allocate (masked_out(imax,jmax),stat=imoa) + allocate (masked_outc(imax,jmax),stat=imoca) + + ita=0 + icpsa=0 + if (phaseflag == 'y') then + if (phasescheme == 'cps' .or. phasescheme == 'both') then + if (allocated(cpshgt)) deallocate (cpshgt) + allocate (cpshgt(imax,jmax,nlevs_cps),stat=icpsa) + endif + endif + +c if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. +c & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. +c & itha /= 0 .or. imoa /= 0 .or. imoca /= 0 .or. ilma /= 0) +c & then +cPENG----2018-06-07 ------------------------ + if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. + & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. + & itha /= 0 .or. imoa /= 0 .or. imoca /= 0 .or. ilma /= 0 + & .or. ish /= 0 .or. irh /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating arrays.' + print *,'!!! iza = ',iza,' iua= ',iua,' iha= ',iha + print *,'!!! iva = ',iva,' isa= ',isa,' icpsa= ',icpsa + print *,'!!! iksa = ',iksa,' isda= ',isda,' ivpa= ',ivpa + print *,'!!! ita = ',ita,' imoa= ',imoa,' imoca= ',imoca + print *,'!!! itha = ',itha,' ilma= ',ilma + endif + itret = 94 + return + endif + + masked_out = .false. ! Initialize all pts to false at each hr + masked_outc = .false. ! Initialize all pts to false at each hr + + if ( verb .ge. 3 ) then + print *,'in beginning of tracker, imax= ',imax,' jmax= ',jmax + endif + +c Initialize all readflags to NOT FOUND for this forecast time, +c then call subroutine to read data for this forecast time. + + zeta = -9999.0 + u = -9999.0 + hgt = -9999.0 + v = -9999.0 + slp = -9999.0 + tmean = -9999.0 +cPENG----2018-06-07 ------------------------ + ushear = -9999.0 + rhumid = -9999.0 + + readflag = .FALSE. + + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: b4 getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + + if (trkrinfo%inp_data_type == 'grib') then + call getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,32) date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: after getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + +c Count how many parms were successfully read for this fcst time. +c Also, for right now, put the value of readflag into all of the +c calcparms for parameters 3 through 9. Note that in getdata we +c read in 17 parms, but in this next loop we only check the +c readflags up to maxtp (= 14 as of 7/2015). That's because +c parms 12 & 13 are for 500 mb u & v, which are not used for +c tracking, only for calculating the deep layer mean wind for +c the next guess, and parm 14 is the 300-500 mb mean temperature, +c which is used for determining storm phase. Parms 10 & 11 are +c for the near-surface winds, which are used in estimating surface +c winds near the storm, and will now also be used as a +c parameter for position estimates. Finally, parm 17 is the +c land-sea mask, which is not used as a tracking parm. + + idum = 0 + do irf = 1,nreadparms + if (readflag(irf)) idum = idum + 1 + if (irf > 2 .and. irf < 10) then + ! calcparm for parms > 9 is done further below. + do jj=1,maxstorm + calcparm(irf,jj) = readflag(irf) + enddo + endif + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Of ',nreadparms,' readable parms, you read in ',idum + print *,'parms for this fcst hour from the input grib file.' + endif + +c If not enough tracked parms were read in, exit the program.... + + if (idum == 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in subroutine tracker' + print *,'!!! Not enough tracked parms read in from getdata.' + print *,'!!! Check for a problem with the input GRIB file.' + print *,'!!! Model identifier = ',inp%model + print *,'!!! STOPPING EXECUTION FOR THIS MODEL' + endif + itret = 99 + ifhtemp = ifh + do while (ifhtemp <= ifhmax) + do istmp=1,maxstorm + fixlon (istmp,ifhtemp) = -999.0 + fixlat (istmp,ifhtemp) = -999.0 + enddo + ifhtemp = ifhtemp + 1 + enddo +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) + if (ifh == 1) then + ! Per Jim Gross (1/01), if the tracker ran but was unable + ! to get an initial fix (or, in this case, unable to get + ! the data needed to run), write out zeroes for the 00h + ! fixes to indicate that the tracker ran unsuccessfully, + ! but don't write out any subsequent forecast times + ! with zeroes.... + vradius = 0 + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initial value of 'undetermined' + do istmp = 1,maxstorm + if (stormswitch(istmp) /= 3) then + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0,-999.0,inp,istmp + & ,ifcsthour,0.0,0.0,vradius,maxstorm + & ,trkrinfo,-99.0,-99.0,-99.0,cps_vals + & ,wcore_flag,ioaxret) + call output_hfip (-999.0,-999.0,inp,istmp + & ,ifh,0.0,0.0,vradius,-99.0,ioaxret) + endif + enddo + endif + return + endif + +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for z850, z700 and mslp.... + + if (user_wants_to_track_gph850 == 'n' .or. + & user_wants_to_track_gph850 == 'N') then + do jj=1,maxstorm + calcparm(7,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_gph700 == 'n' .or. + & user_wants_to_track_gph700 == 'N') then + do jj=1,maxstorm + calcparm(8,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_mslp == 'n' .or. + & user_wants_to_track_mslp == 'N') then + do jj=1,maxstorm + calcparm(9,jj) = .FALSE. + enddo + endif + + +c Parameters 1 & 2 are abs vorticity at 850 & 700. If the data +c files had this parm at 850 & 700 (ECMWF & UKMET do NOT), then +c we don't need to re-calculate relative vorticity, we just need +c to subtract out the Coriolis component. If the files did not +c have vorticity, then we need to calculate relative vorticity. +c If we're able to read vorticity or calculate it, then set the +c vorticity calcparms to TRUE for all storms for now. + + vortloop: do ivort=1,2 + + if (ivort == 1) then + if (user_wants_to_track_zeta850 == 'n' .or. + & user_wants_to_track_zeta850 == 'N') then + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (ivort == 2) then + if (user_wants_to_track_zeta700 == 'n' .or. + & user_wants_to_track_zeta700 == 'N') then + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (readflag(ivort)) then + + call subtract_cor (imax,jmax,dy,ivort) + + do jj=1,maxstorm + calcparm(ivort,jj) = .TRUE. + enddo + else + if (ivort == 1) then + if (readflag(3) .and. readflag(4)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(1,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + endif + else + if (readflag(5) .and. readflag(6)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(2,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + endif + endif + endif + + enddo vortloop + + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for user preferences for the wind circulation +c difference at 850 & 700... + + if (readflag(3) .and. readflag(4)) then + if (user_wants_to_track_wcirc850 == 'n' .or. + & user_wants_to_track_wcirc850 == 'N') then + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(3,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + endif + + if (readflag(5) .and. readflag(6)) then + if (user_wants_to_track_wcirc700 == 'n' .or. + & user_wants_to_track_wcirc700 == 'N') then + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(5,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + endif + + +c Compute the sfc vorticity if sfc_u and sfc_v have been read in. + + if (readflag(10) .and. readflag(11)) then + + if (user_wants_to_track_wcircsfc == 'n' .or. + & user_wants_to_track_wcircsfc == 'N') then + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(10,jj) = .TRUE. + enddo + endif + + if (user_wants_to_track_zetasfc == 'n' .or. + & user_wants_to_track_zetasfc == 'N') then + do jj=1,maxstorm + calcparm(11,jj) = .FALSE. + enddo + else + ! The 3 in the next call to rvcal is to indicate the 3rd + ! level for the zeta array, which is for the surface (or + ! 10m) data. + call rvcal (imax,jmax,dx,dy,3,valid_pt) + do jj=1,maxstorm + calcparm(11,jj) = .TRUE. + enddo + endif + + else + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + calcparm(11,jj) = .FALSE. + enddo + endif + + +c Compute the thicknesses for 200-850, 200-500 and 500-850 mb +c if the gp hgt fields have been read in for 200, 500 and 850. + + if (readflag(7) .and. readflag(15) .and. readflag(16)) then + + call thickness_calc (imax,jmax,valid_pt) + + do jj=1,maxstorm + + if (user_wants_to_track_thick500850 == 'n' .or. + & user_wants_to_track_thick500850 == 'N') then + calcparm(12,jj) = .FALSE. + else + calcparm(12,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200500 == 'n' .or. + & user_wants_to_track_thick200500 == 'N') then + calcparm(13,jj) = .FALSE. + else + calcparm(13,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200850 == 'n' .or. + & user_wants_to_track_thick200850 == 'N') then + calcparm(14,jj) = .FALSE. + else + calcparm(14,jj) = .TRUE. + endif + + enddo + else + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Thickness will not be tracked since at least' + print *,'one of the gp height fields was not read in.' + print *,' readflag(7) -- 850 mb ---> ',readflag(7) + print *,' readflag(15) -- 500 mb ---> ',readflag(15) + print *,' readflag(16) -- 200 mb ---> ',readflag(16) + print *,' ' + endif + do jj=1,maxstorm + calcparm(12,jj) = .FALSE. + calcparm(13,jj) = .FALSE. + calcparm(14,jj) = .FALSE. + enddo + endif + +c --------------------------------------------------------------- +c Now call find_maxmin for the variables zeta, hgt and slp. Only +c process those storms for which stormswitch is set to 1. If a +c storm is selected to be processed, we still have to check the +c calcparm for each parameter, to make sure that the particular +c parm exists at that level and is able to be processed. +c +c The following commented-out data statements are just included +c as a reference so you can see the array positioning of the +c different parameters and levels that are read in: +c +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,sfc,sfc +c ,100,100,100,100,100/ +c data iglev /850,700,850,850,700,700,850,700,0,sfc,sfc +c ,500,500,400,500,200/ +c +c And also for reference, here are the variables / levels for +c the *tracked* parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c NOTE: For mid-latitude cases, we will track ONLY mslp, which +c is why we set all the other calcparms to 'false' just below. + + if (trkrinfo%type == 'midlat') then + do m = 1,maxstorm + calcparm(1,m) = .false. + calcparm(2,m) = .false. + calcparm(3,m) = .false. + calcparm(4,m) = .false. + calcparm(5,m) = .false. + calcparm(6,m) = .false. + calcparm(7,m) = .false. + calcparm(8,m) = .false. + calcparm(10,m) = .false. + calcparm(11,m) = .false. + calcparm(12,m) = .false. + calcparm(13,m) = .false. + calcparm(14,m) = .false. + enddo + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + call sort_storms_by_pressure (gridprs,ifh,maxstorm,prsindex + & ,issret) + if ( (ifh == 1) .or. + & (ifh == 2 .and. trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') ) then + stormct = numtcv + endif + endif + + prevstormct = stormct + tracking_previously_known_storms = .true. + + stormloop: do sl_counter = 1,maxstorm + + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initialized value of 'undetermined' + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + ist = prsindex(sl_counter) + else + ist = sl_counter + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + + if (ist == (prevstormct + 1)) then + + ! For the mid-latitude and tropical cyclogenesis cases, we + ! need to scan the mslp field to find new storms. If we + ! are at this point inside the if statement in stormloop, + ! then that means we have looped through and attempted to + ! track all storms that have already been found up to this + ! point in the forecast, and we need to scan the field for + ! any new storms at this forecast hour. If this is for + ! forecast hour = 0, then right off the bat we may be + ! scanning the field (if there were no tcvitals records + ! read in for this forecast), since ist = 1 and + ! (prevstormct + 1) = 0 + 1 = 1. All that the call just + ! below to first_ges_center does is return a rough idea + ! of the location of new lows; more specific locations are + ! obtained through the barnes analysis tracking algorithm + ! further below. + + if (readflag(9)) then + if (ifh > 1) then + ! We need the use of 2 different masks. One + ! (masked_out) is to be used when looking for new lows, + ! so that after we find a new low, we mask out the + ! surrounding area so we don't find it on a subsequent + ! search for this forecast hour. The other + ! (masked_outc) is used in the routine to check for a + ! closed contour. If checking for a closed contour + ! at, say 70W/25N, this and surrounding points may have + ! already been masked out in first_ges_center, so "N" + ! would misleadingly/incorrectly be returned from + ! check_closed_contour, so that is why we need 2 masks. + ! But now after the first forecast hour (t=0), the way + ! we have this set up is that we track previously known + ! storms first, and once we're done with them, we + ! search for new storms at that same forecast hour. + ! But when looking for new storms, we need to know the + ! positions of the previously tracked storms at this + ! current forecast hour, so we copy the masked_outc + ! array to masked_out in this case.... + + masked_out = masked_outc + + endif + call first_ges_center (imax,jmax,dx,dy,'mslp',slp + & ,'min',trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) + tracking_previously_known_storms = .false. + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In subroutine tracker, readflag' + print *,'!!! for mslp indicates that the mslp data' + print *,'!!! is not available for this forecast ' + print *,'!!! hour, and it is needed for a "midlat"' + print *,'!!! or "tcgen" run of the tracker. ' + print *,'!!! We will exit....' + print *,'!!! readflag(9) = ',readflag(9) + print *,'!!! ifh= ',ifh + print *,' ' + endif + itret = 98 + return + endif + endif + endif + + xval = 0.0 ! initialize entire xval array to 0 + isastorm = 'U' ! re-initialize flag for each time, each storm + + select case (stormswitch(ist)) + + case (1) + + vradius = 0 + + if ( verb .ge. 2 ) then + print *,' ---------------------------------------------' + print *,' | *** TOP OF STORM LOOP *** ' + print *,' | Beginning of storm loop in tracker for' + print *,' | Storm number ',ist + write (6,418) ifhours(ifh),ifclockmins(ifh) + 418 format (1x,' | Forecast hour: ',i4,':',i2.2) + print *,' | Storm name = ',storm(ist)%tcv_storm_name + print *,' | Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,' ---------------------------------------------' + print *,' ' + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3 + & ,'_',i3.3,a1,'_',i4.4,a1,'_',a3) + + endif +c First, make sure storm is within the grid boundaries... + + call check_bounds (slonfg(ist,ifh),slatfg(ist,ifh),ist,ifh + & ,trkrinfo,icbret) + if (icbret == 95) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + + if (slatfg(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + if (calcparm(1,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,1),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(1,ist),clon(ist,ifh,1),clat(ist,ifh,1) + & ,xval(1),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(2,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,2),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(2,ist),clon(ist,ifh,2),clat(ist,ifh,2) + & ,xval(2),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(7,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,1),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(7,ist) + & ,clon(ist,ifh,7),clat(ist,ifh,7),xval(7) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(8,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,2),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(8,ist) + & ,clon(ist,ifh,8),clat(ist,ifh,8),xval(8) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(9,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for mslp' + endif + + call find_maxmin (imax,jmax,dx,dy,'slp' + & ,slp,'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(9,ist) + & ,clon(ist,ifh,9),clat(ist,ifh,9),xval(9) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(11,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for sfc zeta' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,3),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(11,ist),clon(ist,ifh,11),clat(ist,ifh,11) + & ,xval(11),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 + + if (calcparm(12,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 500-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,1),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(12,ist) + & ,clon(ist,ifh,12),clat(ist,ifh,12),xval(12) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(13,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-500 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,2),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(13,ist) + & ,clon(ist,ifh,13),clat(ist,ifh,13),xval(13) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(14,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,3),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(14,ist) + & ,clon(ist,ifh,14),clat(ist,ifh,14),xval(14) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c Now get centers for wind circulation at 700 & 850 mb and +c at 10m. First, get a modified guess lat/lon position for +c wind circulation. Do this because we will be searching +c for this wind circulation center over a smaller area and +c so it's more crucial to have a better first guess position. +c This modified guess position will be an average of the first +c guess position for this time and the fix positions for this +c time from some of the other parameters. + + if (slatfg(ist,ifh) >= 0.0) then + cmaxmin = 'max' + else + cmaxmin = 'min' + endif + + if (calcparm(3,ist) .and. calcparm(4,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 850 mb ' + endif + + print *,' ' + print *,'Before first call to get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' inp%modtyp= ',inp%modtyp + print *,' cmaxmin= ',cmaxmin + print *,' nlev850= ',nlev850 + print *,' u(1,1,nlev850)= ',u(1,1,nlev850) + print *,' u(imax,jmax,nlev850)= ',u(imax,jmax,nlev850) + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' calcparm(3,ist)= ',calcparm(3,ist) + print *,' clon(ist,ifh,3)= ',clon(ist,ifh,3) + print *,' clat(ist,ifh,3)= ',clat(ist,ifh,3) + print *,' xval(3)= ',xval(3) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,141) date_time(5),date_time(6),date_time(7) + 141 format (1x,'TIMING: Before GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,850,valid_pt,calcparm(3,ist) + & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,142) date_time(5),date_time(6),date_time(7) + 142 format (1x,'TIMING: After GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,850,valid_pt,calcparm(3,ist) +c & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + endif + else + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + clon(ist,ifh,3) = 0.0 + clat(ist,ifh,3) = 0.0 + endif + endif + + if (calcparm(5,ist).and. calcparm(6,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 700 mb ' + endif + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,143) date_time(5),date_time(6),date_time(7) + 143 format (1x,'TIMING: Before GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,700,valid_pt,calcparm(5,ist) + & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,144) date_time(5),date_time(6),date_time(7) + 144 format (1x,'TIMING: After GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,700,valid_pt,calcparm(5,ist) +c & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + endif + else + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + clon(ist,ifh,5) = 0.0 + clat(ist,ifh,5) = 0.0 + endif + endif + + if (calcparm(10,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for the' + print *,'surface (10m) level' + endif + + ! NOTE: The 1020 in the call here is just a number/code + ! to indicate to the subroutine to process sfc winds. + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,145) date_time(5),date_time(6),date_time(7) + 145 format (1x,'TIMING: Before GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,1020,valid_pt,calcparm(10,ist) + & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) + & ,trkrinfo,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,146) date_time(5),date_time(6),date_time(7) + 146 format (1x,'TIMING: After GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,1020,valid_pt,calcparm(10,ist) +c & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) +c & ,trkrinfo,igucret) + + if (igwcret /= 0) then + calcparm(10,ist) = .FALSE. + endif + else + calcparm(10,ist) = .FALSE. + clon(ist,ifh,10) = 0.0 + clat(ist,ifh,10) = 0.0 + endif + endif + +c ------------------------------------------------------ +c All of the parameter center fixes have been done. Now +c average those positions together to get the best guess +c fix position. If a center fix is able to be made, then +c call subroutine get_max_wind to get the maximum near- +c surface wind near the center, and then call get_next_ges +c to get a guess position for the next forecast hour. + + if (stormswitch(ist) == 1) then + + call fixcenter (clon,clat,ist,ifh,calcparm + & ,slonfg(ist,ifh),slatfg(ist,ifh),inp + & ,stderr,fixlon,fixlat,xval,maxstorm,ifret) + + if (ifret == 0) then + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'regional')then + if (fixlon(ist,ifh) > (trkrinfo%eastbd + 7.0) .or. + & fixlon(ist,ifh) < (trkrinfo%westbd - 7.0) .or. + & fixlat(ist,ifh) > (trkrinfo%northbd + 7.0) .or. + & fixlat(ist,ifh) < (trkrinfo%southbd - 7.0)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! will NOT be made for this time due' + print *,'!!! the storm being more than 7 degrees' + print *,'!!! outside the user-specified lat/lon' + print *,'!!! bounds for this run. We will stop' + print *,'!!! tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + 432 format (1x,'!!! Fcst hr = ',i4,':',i2.2) + print *,'!!! fixlat= ',fixlat(ist,ifh) + print *,'!!! fixlon= ',fixlon(ist,ifh) + print *,'!!! User East Bound = ',trkrinfo%eastbd + print *,'!!! User West Bound = ',trkrinfo%westbd + print *,'!!! User North Bound = ',trkrinfo%northbd + print *,'!!! User South Bound = ',trkrinfo%southbd + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + endif + cycle stormloop + endif + endif + else + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + +c Just because we've found a center doesn't mean there is +c actually a storm there. I noticed in the first year that +c for some decaying or just weak storms, the tracker would +c identify a center to follow, but it may have only been +c a weak trough passing by, or something else that's not +c our storm. This next subroutine checks to see that the +c surface pressure gradient and/or tangential winds at +c 850 mb resemble a storm. It is called twice; the first +c time for MSLP, the 2nd time for 850 mb winds. We will +c apply these storm-checking criteria if either the mslp +c or v850 check come back negative. Remember, there +c is the possibility that centers could not be found for +c 1 or both of these parameters, in which case the isastorm +c flag will have a value of 'U', for "undetermined". + + isiret1 = 0; isiret2 = 0; isiret3 = 0 + + print *,' ttest, ifret= ',ifret + + if (ifret == 0) then + + print *,' ttest, calcparm(9,ist)= ',calcparm(9,ist) + + if (calcparm(9,ist)) then + + ! Do a check of the mslp gradient.... + + print *,' ttest, in IF part: ' + print *,' clon(ist,ifh,9)= ',clon(ist,ifh,9) + print *,' clat(ist,ifh,9)= ',clat(ist,ifh,9) + print *,' xval(9)= ',xval(9) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,clon(ist,ifh,9),clat(ist,ifh,9) + & ,xval(9),trkrinfo,isastorm(1),isiret1) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for mslp (e.g., + ! maybe the mslp fix was too far away from the + ! guess?), then this check isn't performed. We are + ! changing this so that the mslp gradient check will + ! still be performed, but using the mean fixlat and + ! fixlon positions as the center. Still, we first + ! need to check to see if mslp was even read in. If + ! it wasn't, then we are just out of luck. + + print *,' ttest, in ELSE part: ' + + if (trkrinfo%use_backup_mslp_grad_check == 'y' .or. + & trkrinfo%use_backup_mslp_grad_check == 'Y') then + + print *,' ttest ELSE, readflag(9)= ',readflag(9) + + if (readflag(9)) then + + print *,'ttest ELSE A, ist= ',ist,' ifh= ',ifh + print *,'ttest ELSE A, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE A, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,9999.0,ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + + print *,'ttest ELSE B, ifilret= ',ifilret + + if (ifilret == 0) then + + print *,'ttest ELSE B, ifilret= ',ifilret + print *,'ttest ELSE B, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE B, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,gridpoint_maxmin,trkrinfo,isastorm(1) + & ,isiret1) + + if (isiret1 == 0) then + ! Even though calcparm(9) is FALSE and mslp + ! will not be used for center-fixing + ! purposes, we need to fill the clat and clon + ! arrays just a few lines below so that + ! calls to fix_latlon_to_ij below do not + ! get screwed up. So, into the clat and clon + ! arrays we put the mean fixlat and fixlon + ! positions for this lead time. + clat(ist,ifh,9) = fixlat(ist,ifh) + clon(ist,ifh,9) = fixlon(ist,ifh) + xval(9) = gridpoint_maxmin + endif + + endif + + endif + + endif + + endif + + ! If we have found a valid mslp gradient, then make + ! a call to fix_latlon_to_ij to (1) get the actual + ! gridpoint value of the mslp (the value previously + ! stored in xval(9) is an area-averaged value coming + ! from the barnes analysis), and (2) to get the + ! (i,j) indices for this gridpoint to be used in the + ! call to check_closed_contour below. + ! + ! NOTE: If a mslp fix was not made, or if the mslp + ! "isastorm" flag comes back as no, we make the same + ! call to fix_latlon_to_ij, but we use the mean fix + ! position as our input to search around, and then + ! basically we just find the lowest mslp near that + ! mean fix position. There is a check on the value + ! of xinp_fixlat and xinp_fixlon to make sure that + ! they contain valid values and not just the + ! initialized -999 values. + + if (isiret1 == 0 .and. isastorm(1) == 'Y') then + xinp_fixlat = clat(ist,ifh,9) + xinp_fixlon = clon(ist,ifh,9) + if (verb >= 3) then + print *,' ttest at location C IF....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + else + xinp_fixlat = fixlat(ist,ifh) + xinp_fixlon = fixlon(ist,ifh) + if (verb >= 3) then + print *,' ttest at location C ELSE....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + endif + + if (xinp_fixlat > -99.0 .and. xinp_fixlon > -990.0) + & then + if (verb >= 3) then + print *,' ttest at location D' + endif + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,xinp_fixlon,xinp_fixlat + & ,xval(9),ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (verb >= 3) then + print *,' ttest at location E, ifilret= ',ifilret + endif + if (ifilret == 0) then + gridprs(ist,ifh) = gridpoint_maxmin + else + ! Search went out of regional grid bounds.... + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + print *,' ttest at location F' + + ! For a "tracker" case, check to see if the user has + ! requested to compute and write out the ROCI. If + ! so, then we make a call to check_closed_contour, + ! being sure to specify 999 as the number of levels + ! to check.... + + if (isiret1 == 0 .and. isastorm(1) == 'Y' .and. + & trkrinfo%type == 'tracker') then + + if (trkrinfo%want_oci) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + if (xval(9) < 1100.0) then + ! Pressure units are in mb... + prs_contint_thresh = 4.0 + elseif (xval(9) >80000.0) then + ! Pressure units are in Pa... + prs_contint_thresh = 400.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' tracker. The mslp value' + print *,' (xval(9)) is not in range.' + print *,' before call to' + print *,' check_closed_contour.' + print *,' xval(9) = ',xval(9) + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + if (trkrinfo%contint < prs_contint_thresh) then + hold_old_contint = trkrinfo%contint + trkrinfo%contint = prs_contint_thresh + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before going into routine to diagnose' + print *,'the ROCI for a tracker run, the ' + print *,'requested contour interval is being ' + print *,'adjusted up (coarser) to avoid having' + print *,'the contour check routine break and ' + print *,'return an invalid value.' + print *,'User-requested contint value (Pa) = ' + & ,hold_old_contint + print *,'Modified contint value (Pa) = ' + & ,trkrinfo%contint + endif + endif + + masked_outc = .false. + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ' + & ,rlastbar,' nm' + print *,' ' + endif + + endif + + endif + + ! For the midlat & tcgen cases, do a check to see if + ! there is a closed mslp contour. The ifix and jfix + ! values passed into check_closed_contour are the + ! values for the (i,j) at the gridpoint minimum, + ! which was obtained just above from the call to + ! fix_latlon_to_ij. + ! UPDATE 7/12/2016 tpm: A change was made to fix a + ! hole in the logic. Previously, for a genesis run + ! (type = midlat or tcgen), if a fix was not made + ! for mslp, then the isastorm(1) flag would not be + ! 'Y', and so the call to check_closed_contour in + ! the following IF statement would not be made, and + ! that would prevent the mask from getting updated + ! for this particular storm, allowing the same storm + ! to be detected when the scan for new storms takes + ! place at this lead time (i.e., after all previously- + ! known storms from the last lead time have been + ! tracked). As a fix, if that isastorm(1) flag is not + ! 'Y', then we call a new subroutine which updates the + ! mask based on the circulation at 850 mb. + + if (isastorm(1) == 'Y' .and. isiret1 == 0 .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ',rlastbar + & ,' nm' + print *,' ' + endif + + ! This next bit of code adds a second layer of closed + ! contour checking. This is to decrease the + ! occurrence of interrupted midlat and tcgen tracks, + ! which usually happens when the closed contour + ! criterion is not met for one time period. So in + ! this next code, we check to see if the ccflag was + ! 'y' for at least half the time over the last 24h. + ! For time periods shorter than 24h (e.g., the storm + ! was just detected at 144h and we are now at 156h), + ! the threshold is still that for at least half of + ! the time the system has been detected as a storm, + ! it must have a ccflag value of 'y'. + + if (ccflag == 'y') then + closed_mslp_ctr_flag(ist,ifh) = 'y' + else + closed_mslp_ctr_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & closed_mslp_ctr_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (closed_mslp_ctr_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.50) then + ccflag = 'y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE ON CLOSED CONTOUR CHECK: The' + print *,' ccflag returned for this hour was' + print *,' NO, but a check of recent ccflags' + print *,' indicates that more than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + ccflag = 'n' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!! NOTE ON CLOSED CONTOUR CHECK: The' + print *,'!! ccflag returned for this hour was' + print *,' NO, and a check of recent ccflags' + print *,' indicates that less than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + if (ccflag == 'y') then + isastorm(2) = 'Y' + else if (ccflag == 'n') then + isastorm(2) = 'N' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*---------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*---------------------------------------*' + print *,' ' + endif + + + else if (isastorm(1) /= 'Y' .and. + & calcparm(3,ist) .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + ! The isastorm(1) flag indicates that a mslp gradient + ! could not be found at this lead time, so the mask + ! cannot be updated using mslp. Instead, + ! do a check of the 850 mb wind circulation + ! surrounding the 850 wind circulation fix, and then + ! set the mask to be TRUE for all points within the + ! area where mean cyclonic Vt exceed +1 m/s.... + +c call check_closed_contour (imax,jmax,ifix,jfix,slp +c & ,valid_pt,masked_outc,ccflag,'min',trkrinfo +c & ,999,contour_info,get_last_isobar_flag,plastbar +c & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Calling mask_based_on_wind_circ at ' + & ,ifcsthour + endif + + call mask_based_on_wind_circ (imax,jmax,dx,dy,850 + & ,valid_pt,masked_outc,trkrinfo + & ,clon(ist,ifh,3),clat(ist,ifh,3),inp%modtyp + & ,imbowret) + + endif + + ! For tropical cyclones, check the avg 850 mb tangential + ! windspeed close to the storm center.... + + if (trkrinfo%type == 'tcgen' .or. + & trkrinfo%type == 'tracker') then + + had_to_try_backup_850_vt_check = 'n' + + if (calcparm(3,ist)) then + + if (verb .ge. 3) then + print *,' ' + print *,'Checking 850 mb Vt speed using 850 mb ' + print *,'wind circulation fix: ' + print *,' 850 mb wcirc fix lon= ',clon(ist,ifh,3) + print *,' 850 mb wcirc fix lat= ',clat(ist,ifh,3) + print *,' Multi-parm fix lon= ',fixlon(ist,ifh) + print *,' Multi-parm fix lat= ',fixlat(ist,ifh) + print *,' ' + endif + + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,clon(ist,ifh,3),clat(ist,ifh,3) + & ,xval(3),trkrinfo,isastorm(3),isiret3) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for 850 mb wind + ! circulation (maybe the 850 mb wind circulation fix + ! was too far away from the guess?), then this check + ! isn't performed. We are changing this so that the + ! 850 mb Vt wind speed check will still be + ! performed, but using the mean fixlat and fixlon + ! positions as the center. Still, we first need to + ! check to see if 850 mb u-comp and v-comp were even + ! read in. If they weren't, then we are just out + ! of luck. + + had_to_try_backup_850_vt_check = 'y' + isiret3 = -99 + + if (trkrinfo%use_backup_850_vt_check == 'y' .or. + & trkrinfo%use_backup_850_vt_check == 'Y') then + + if (readflag(3) .and. readflag(4)) then + + if (verb .ge. 3) then + print *,' ' + print *,'!!! NOTE: 850 mb wcirc fix not ' + print *,'available. We are instead ' + print *,'checking 850 mb Vt speed using ' + print *,'multi-parm fix position: ' + print *,' Multi-parm fix lon= ' + & ,fixlon(ist,ifh) + print *,' Multi-parm fix lat= ' + & ,fixlat(ist,ifh) + print *,' ' + endif + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,0.00,trkrinfo,isastorm(3),isiret3) + + endif + + endif + + endif + + if (calcparm(3,ist) .or. + & (had_to_try_backup_850_vt_check == 'y' .and. + & isiret3 == 0) ) then + + if (trkrinfo%type == 'tcgen') then + ! This next bit of code adds a second layer of 850 + ! mb Vt magnitude checking. This is to decrease + ! the occurrence of interrupted tcgen tracks, + ! which occasionally happens for weak storms when + ! this criterion is not met for one time period. + ! So in this next code, we check to see if the + ! vt850_flag was 'y' for at least 75% of the time + ! over the last 24h. For time periods shorter + ! than 24h (e.g., the storm was just detected at + ! 144h and we are now at 156h), the threshold is + ! still that for at least 75% of the time the + ! system has been detected as a storm, it must + ! have a vt850_flag value of 'y'. + + if (isastorm(3) == 'Y') then + vt850_flag(ist,ifh) = 'y' + else + vt850_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & vt850_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - + & fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (vt850_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / + & cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.75) then + isastorm(3) = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ NOTE ON Vt_850 CHECK: The ' + print *,' isastorm flag returned for ' + print *,' this hour was NO, but a' + print *,' check of recent vt850_flags' + print *,' indicates that more than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE ON Vt_850 CHECK: The ' + print *,'!!! isastorm flag returned for ' + print *,' this hour was NO, and a' + print *,' check of recent vt850_flags ' + print *,' indicates that less than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + endif + + endif + endif + + else + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + isastorm(1) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! could not be made for mslp, ' + print *,'!!! therefore we will stop tracking ' + print *,'!!! for this storm.' + endif + + else + isastorm(1) = 'N' + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a TC tracker case, a fix could' + print *,'!!! not be made using any tracked parms,' + print *,'!!! therefore we will stop tracking for' + print *,'!!! this storm.' + endif + + endif + + if ( verb .ge. 3 ) then + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + + endif + + if (isiret1 /= 0 .or. isiret2 /= 0 .or. isiret3 /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: One of the calls to ' + print *,'!!! is_it_a_storm produced an error.' + print *,'!!! Chances are this is from a call to ' + print *,'!!! get_ij_bounds, meaning we are too close' + print *,'!!! to a regional grid boundary to do this ' + print *,'!!! analysis. Processing will continue....' + print *,'!!! isiret1= ',isiret1,' isiret2= ',isiret2 + print *,'!!! isiret3= ',isiret3 + endif + + endif + + if (isastorm(1) == 'N' .or. isastorm(2) == 'N' .or. + & isastorm(3) == 'N') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! At least one of the isastorm flags from' + print *,'!!! subroutine is_it_a_storm is "N", so ' + print *,'!!! either we were unable to find a good ' + print *,'!!! mslp gradient and/or a valid 850 mb ' + print *,'!!! circulation for the storm at this time,' + print *,'!!! or, for the cases of midlat or tcgen ' + print *,'!!! tracking, a closed mslp contour could ' + print *,'!!! not be found, thus we will stop tracking' + print *,'!!! this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! mslp gradient flag = ',isastorm(1) + print *,'!!! closed contour flag = ',isastorm(2) + print *,'!!! 850 mb winds flag = ',isastorm(3) + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + + ! Now do another check for the tracker and tcgen cases. + ! If the isastorm flags for mslp gradient and v850 BOTH + ! came back positive AND you have been able to locate an + ! 850 mb vort center, just do a check to make sure that + ! the distance between the 850 vort center and the mslp + ! center is not too great. + + if (trkrinfo%type == 'tracker' .or. + & trkrinfo%type == 'tcgen') then + if (isastorm(1) == 'Y' .and. isastorm(3) == 'Y' .and. + & calcparm(1,ist) .and. stormswitch(ist) == 1) then + +c if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) >= 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) < 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else +c trkrinfo%max_mslp_850 = 323.0 +c endif + + call calcdist (clon(ist,ifh,9),clat(ist,ifh,9) + & ,clon(ist,ifh,1),clat(ist,ifh,1),dist + & ,degrees) + + if (dist > trkrinfo%max_mslp_850) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, the dist betw' + print *,'!!! the mslp center & the 850 zeta ' + print *,'!!! center is too great, thus we will' + print *,'!!! stop tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + print *,'!!! Actual distance (km) = ',dist + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Actual distance between the parm centers' + print *,'for 850 zeta and mslp is ',dist,' (km)' + print *,'Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + endif + + endif + endif + endif + + ! Do one final check. Check the new fix position and + ! the old fix position and calculate the speed that the + ! storm would have had to travel to get to this point. + ! If that speed exceeds a certain threshold (~60 kt), + ! assume you're tracking the wrong thing and quit. + ! Obviously, only do this for times > 00h. The check + ! in the if statement to see if the previous hour's + ! lats and lons were > -999 is for the midlat and + ! tcgen cases -- remember, they can have genesis at + ! any hour of the forecast, in which case the previous + ! forecast hour's lat & lon would be -999. + + if (ifh > 1 .and. stormswitch(ist) == 1) then + if (fixlon(ist,ifh-1) > -999.0 .and. + & fixlat(ist,ifh-1) > -999.0 ) then + + if (trkrinfo%type == 'midlat') then + xmaxspeed = maxspeed_ml + else + xmaxspeed = maxspeed_tc + endif + + call calcdist (fixlon(ist,ifh-1),fixlat(ist,ifh-1) + & ,fixlon(ist,ifh),fixlat(ist,ifh),dist + & ,degrees) + + ! convert distance from km to nm and get speed. + + distnm = dist * 0.539638 + xinterval_fhr = fhreal(ifh) - fhreal(ifh-1) + xknots = distnm / xinterval_fhr + + if (xknots > xmaxspeed) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, calculated spd' + print *,'!!! of the storm from the last position' + print *,'!!! to the current position is too high,' + print *,'!!! so we will stop tracking this storm' + print *,'!!! (For fear that we are not actually ' + print *,'!!! tracking our storm, but have instead' + print *,'!!! locked onto some other feature....)' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max speed allowed (kt) = ',xmaxspeed + print *,'!!! Actual speed (kt) = ',xknots + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'The average speed that the storm moved' + print *,'at since the previous forecast time is' + & ,xknots,' knots.' + endif + + endif + + endif + + endif + + endif + +c Now get the maximum near-surface wind speed near the storm +c center (get_max_wind). Also, call getradii to get the +c radii in each storm quadrant of gale-force, storm-force +c and hurricane force winds. + + if (readflag(10) .and. readflag(11) .and. ifret == 0 + & .and. stormswitch(ist) == 1) then + call get_max_wind (fixlon(ist,ifh),fixlat(ist,ifh) + & ,imax,jmax,dx,dy,valid_pt,levsfc + & ,xmaxwind(ist,ifh),trkrinfo,rmax,igmwret) +c if (igmwret /= 0 .and. gridmove_status == 'stopped') then + if (igmwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Return code from get_max_wind is /= 0. ' + print *,'!!! rcc= igmwret= ',igmwret + print *,'!!! Also, this is a moveable, regional grid' + print *,'!!! and the grid did not change from last' + print *,'!!! lead time to current one, so what has' + print *,'!!! likely happened is that the storm has ' + print *,'!!! moved close to the edge of the nested ' + print *,'!!! grid domain, but the nested grid itself' + print *,'!!! had stopped moving, probably because it' + print *,'!!! dropped or lost the storm.' + print *,'!!! ' + print *,'!!! TRACKING WILL STOP FOR THIS STORM' + print *,'!!! ' + endif + + stormswitch(ist) = 2 + cycle stormloop + endif + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + + ! For the radii, we encountered a problem with radmax + ! being too small. It was set at 650 km. Hurricane + ! Sandy exceeded this in the models, so the values + ! returned from getradii were close to the default + ! radmax value of 650 km (350 nm), instead of higher. + ! To fix it, we now use an iterative technique, where + ! we start with radmax as a small value (500 km). If + ! getradii returns a value for R34 in a quadrant that + ! does not exceed 0.97*radmax, then that value is ok. + ! If it does exceed 0.97*radmax, then we bump up radmax + ! by 50 km and call getradii again, looking to diagnose + ! radii only in those quadrants where the + ! need_to_expand_r34 flag = 'n'. BTW... note the + ! initial IF statement... we will only go into this + ! routine if the max wind just diagnosed for this lead + ! time is at least 34 kts (17.5 m/s). + + if (xmaxwind(ist,ifh) >= 17.5) then + + vradius = 0 + first_time_thru_getradii = .true. + r34_check_okay = 'n' + do ivr = 1,4 + need_to_expand_r34(ivr) = 'y' + enddo + radmax = 500.0 ! Initial radmax, in km + + igrct = 1 + + if ( verb .ge. 3 ) then + write (6,242) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 242 format (1x,'TIMING: b4 getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + getrad_iter_loop: do while + & (r34_check_okay == 'n' .and. radmax <= 1050.) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,244) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 244 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + call getradii (fixlon(ist,ifh),fixlat(ist,ifh),imax + & ,jmax,dx,dy,valid_pt,storm(ist)%tcv_storm_id + & ,ifcsthour,xmaxwind(ist,ifh),vradius + & ,trkrinfo,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) + + if (igrret /= 0) then + if (verb >= 3) then + print *,' ' + print *,'!!! ERROR: Return code from getradii = ' + & ,igrret + print *,'!!! Searching for radii will not be ' + print *,'!!! completed for this lead time and' + print *,'!!! all radii values will be set to ' + print *,'!!! missing.' + print *,' ' + exit getrad_iter_loop + endif + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,245) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 245 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + first_time_thru_getradii = .false. + igrct = igrct + 1 + r34_dist_thresh = 0.97 * radmax + r34_good_ct = 0 + do ivr = 1,4 + vradius_km = float(vradius(1,ivr)) / 0.5396 + if (vradius_km < r34_dist_thresh) then + r34_good_ct = r34_good_ct + 1 + need_to_expand_r34(ivr) = 'n' + endif + enddo + if (r34_good_ct == 4) then + r34_check_okay = 'y' + endif + radmax = radmax + 50.0 + enddo getrad_iter_loop + + if ( verb .ge. 3 ) then + write (6,246) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 246 format (1x,'TIMING: after getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + endif + + endif + +c If the user has requested so, then call a routine to +c determine the type of cyclone, using Bob Hart's +c cyclone phase space (CPS) algorithms. It is only used +c for times after t=0, since for the first check (of the +c "parameter B" thickness asymmetry), we need to know +c in which direction the storm is moving. Pulling that +c storm movement data off of the tcvitals is not reliable +c since the model storm may not be moving in the same +c direction as the observed storm. However, we could do +c an upgrade later where this storm movement data is +c pulled from the "genesis vitals", which are derived +c from the model forecast data itself, not the obs. + + if (phaseflag == 'y' .and. stormswitch(ist) == 1) then + wcore_flag = 'u' ! 'u' = undetermined +c call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm +c & ,cps_vals,wcore_flag,igpret) +cPENG----2018-06-07 ------------------------ + call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag + & ,wcore_mean_val,wcore_point_max,igpret) + + endif + + if (structflag == 'y' .or. ikeflag == 'y') then + call get_sfc_center (fixlon(ist,ifh),fixlat(ist,ifh) + & ,clon,clat,ist,ifh,calcparm,xsfclon + & ,xsfclat,maxstorm,igscret) + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,er_wind,sr_wind,er_vr,sr_vr + & ,er_vt,sr_vt,maxstorm,trkrinfo,igwsret) + if (igwsret == 0) then + call output_wind_structure (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),er_wind,sr_wind + & ,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iowsret) + endif + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,wfract_cov,pdf_ct_bin + & ,pdf_ct_tot,maxstorm,trkrinfo,igfwret) + if (igfwret == 0) then + call output_fract_wind (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),wfract_cov,'earth' + & ,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) + endif + endif + + if (ikeflag == 'y' .and. stormswitch(ist) == 1) then + call get_ike_stats (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,ike,sdp,wdp,maxstorm + & ,trkrinfo,igisret) + if (igisret == 0) then + call output_ike (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),ike,sdp,wdp,maxstorm + & ,ioiret) + endif + endif + +c Now print out the current fix position and intensity +c (in knots) to standard output. Conversion for m/s to +c knots (1.9427) is explained in output_atcf. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to fixcenter, fix positions at ' + write (6,442) ifhours(ifh),ifclockmins(ifh) + 442 format (1x,'forecast hour= ',i4,':',i2.2,' follow:') + print *,' ' + endif + + if (ifret == 0 .and. stormswitch(ist) == 1) then + + if ( verb .ge. 3 ) then + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + & ,int((xmaxwind(ist,ifh)*1.9427) + 0.5) + print *,' ' + endif + + ! Only call output routines every atcffreq/100 hours.... + + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + + if (leadtime_check == 0) then + + ifcsthour = ileadtime / 100 + + call output_atcfunix (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + + ! Get the storm motion vector and the speed of + ! motion so that we can output this in the + ! "atcf_sink" forecast text file. + + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'vitals',trkrinfo + & ,ignret) + else + istmdir = -999 + istmspd = -999 + ignret = 0 + endif + + if ( verb .ge. 3 ) then + write (6,617) istmspd,istmdir,ignret + 617 format (1x,'+++ RPT_STORM_MOTION: istmspd= ',i5 + & ,' istmdir= ',i5,' rcc= ',i3) + endif + + ! Call a routine to find the mean & max relative + ! vorticity near the storm at 850 & 700. These will + ! be written out to the "atcf_sink" fcst text file. + + imeanzeta = -99 + igridzeta = -99 + call get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) +cPENG----2018-06-07 ------------------------ + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + + call get_temp_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeantemp,igridtemp,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,itempt) + + call get_ushe_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanushe,igridushe,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,iushet) + + call get_rhum_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanrhum,igridrhum,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,irhumt) +cPENG----2018-06-07 ------------------------ + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (fixlon(ist,ifh) +c & ,fixlat(ist,ifh),inp,ist +c & ,ifcsthour,xmaxwind(ist,ifh) +c & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo +c & ,istmspd,istmdir,plastbar,rlastbar,rmax +c & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo + & ,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + + call output_atcf_sink (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta + & ,igridzeta,cps_vals,plastbar,rlastbar + & ,ioaxret) + + if (inp%model == 12 .and. ifcsthour == 0) then + ! Write vitals for GFS ens control analysis + call output_tcvitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,iovret) + + endif + endif + + ! The exception here is for the call to the output_hfip + ! routine, which will be called for every lead time + ! that is processed.... + + call output_hfip (fixlon(ist,ifh),fixlat(ist,ifh),inp,ist + & ,ifh,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,rmax,ioaxret) + else + + if ( verb .ge. 3 ) then + write (6,452) 'fixpos ',storm(ist)%tcv_storm_id + & ,' fhr= ',ifhours(ifh),ifclockmins(ifh) + & ,' Fix not made for this forecast hour' + 452 format (1x,a7,1x,a4,a6,i4,':',i2.2,a36) + + print *,' ' + print *,'!!! RETURN CODE from fixcenter not equal to 0,' + print *,'!!! or output from is_it_a_storm indicated the' + print *,'!!! system found was not our storm, or the ' + print *,'!!! speed calculated indicated we may have ' + print *,'!!! locked onto a different center, thus a fix' + print *,'!!! was not made for this storm at this ' + print *,'!!! forecast hour.' + print *,'!!! mslp gradient check = ',isastorm(1) + print *,'!!! mslp closed contour check = ',isastorm(2) + print *,'!!! 850 mb winds check = ',isastorm(3) + print *,'!!! fixcenter return code = ifret = ',ifret + print *,' ' + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + +c if (inp%model == 1 .or. inp%model == 8 .or. +c & inp%model == 22) then +cPENG + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + + ! For the vt=00h lead time, if the tracker failed to + ! locate a position, we are going to write out an + ! atcfunix record that contains the position, + ! intensity, mslp and 34-kt wind radii from TC Vitals + ! for this storm and initial time. Only do this for + ! the GFS or GDAS runs of the tracker. + + tcv_max_wind_ms = float(storm(ist)%tcv_vmax) + tcv_mslp_pa = float(storm(ist)%tcv_pcen) * 100.0 + + ! Convert tcvitals NE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15ne) + if (r34_from_tcv > 0.0) then + vradius(1,1) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,1) = 0 + endif + + ! Convert tcvitals SE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15se) + if (r34_from_tcv > 0.0) then + vradius(1,2) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,2) = 0 + endif + + ! Convert tcvitals SW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15sw) + if (r34_from_tcv > 0.0) then + vradius(1,3) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,3) = 0 + endif + + ! Convert tcvitals NW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15nw) + if (r34_from_tcv > 0.0) then + vradius(1,4) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,4) = 0 + endif + + ! Convert tcvitals roci from km to nm + + if (storm(ist)%tcv_penvrad > 0) then + roci_from_tcv = float(storm(ist)%tcv_penvrad) + rlastbar = roci_from_tcv * 0.5396 + else + rlastbar = -99.0 + endif + + ! Convert tcvitals pressure at roci from km to nm + + if (storm(ist)%tcv_penv > 0) then + proci_from_tcv = float(storm(ist)%tcv_penv) + plastbar = proci_from_tcv * 100.0 + else + plastbar = -99.0 + endif + + write (6,291) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + & ,atcfymdh + 291 format (1x,'NOTE: TCVITALS_USED_FOR_ATCF_F00 ' + & ,' Storm ID: ',a4,' Storm name: ',a9 + & ,' YMDH: ',i10) + + call output_atcfunix (slonfg(ist,ifh) + & ,slatfg(ist,ifh),inp,ist + & ,ifcsthour,tcv_max_wind_ms + & ,tcv_mslp_pa,vradius,maxstorm,trkrinfo + & ,plastbar,rlastbar,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + else + + ! For all other models, we print out missing + ! data values at tau=00h if the tracker was + ! unable to find the storm.... + + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + + endif + + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (trkrinfo%type == 'tracker') then + ! Update 11/11: For a 'tracker' run, i.e., one in + ! which we know that there is an observed storm in + ! the area, we will assume that there was some type + ! of problem in the initialization that prevented + ! the storm from being found. In this case, even + ! though we have written out zeroes for the 00h + ! time, we want to at least try tracking again at + ! the next lead time. Requested by HWRF folks.... + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',ist + write (6,301) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + 301 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + call get_next_ges (slonfg,slatfg,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + stormswitch(ist) = 1 + endif + + endif + cycle stormloop + endif + + +c Now get first guess for next forecast time's position. +c But first, if this is the first time level (ifh=1) and +c the user has requested that storm vitals be output (this +c is usually only done for model analyses in order to get +c an analysis position from one time to the next), we will +c write out a storm vitals record for this time level. +c Note that we have already gotten the next guess position +c info just above for the case of the repeated analysis +c data, so we'll just output the genesis vitals record. + + if (ifh <= ifhmax) then + if (ifh == 1 .and. trkrinfo%out_vit == 'y') then + call output_gen_vitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,istmspd,istmdir,iovret) + endif + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: Problem getting first guess ' + print *,'!!! position for next lead time. Return' + print *,'!!! code from call to get_next_ges = ' + print *,'!!! ignret = ',ignret + print *,'!!! Storm name = ' + & ,storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! TRACKING WILL STOP FOR THIS STORM.' + endif + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + else + istmdir = -999 + istmspd = -999 + endif + endif + + case (2) + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Case 2 in tracker for stormswitch' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + imeantemp = -99.0 + igridtemp = -99.0 + imeanushe = -999.0 + igridushe = -999.0 + imeanrhum = -99.0 + igridrhum = -99.0 + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then +c call output_atcf_gen (-999.0 +c & ,-999.0,inp,ist +c & ,ifcsthour,0.0 +c & ,0.0,vradius,maxstorm,trkrinfo +c & ,-99,-99,-999.0,-999.0,-99.0 +c & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) +cPENG----2018-06-07 ------------------------ + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + endif + + case (3) + continue + +c print *,' ' +c print *,'!!! Case 3 in tracker for stormswitch' +c print *,'!!! Storm name = ',storm(ist)%tcv_storm_name +c print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + if (leadtime_check == 0) then + ifcsthour = ileadtime / 100 + endif + if (trkrinfo%inp_data_type == 'grib') then + call output_tracker_mask (masked_outc,valid_pt,ifh + & ,ifcsthour,imax,jmax,iotmret) + endif + endif + + if(use_per_fcst_command=='y') then +c User wants us to run a command per forecast time + +! Replace %[FHOUR] with forecast hour, %[FMIN] with forecast minute. + +! The %[] format is chosen to avoid shell syntax errors if someone +! includes unknown %[] constructs. A stray , for example, +! would generate syntax errors or unexpected results in some +! shells. + +! If an unrecognized %[xxx] sequence is used, it will be retained in +! the final command. This allows the underlying command to detect +! the unreplaced %[] and use suitable default values or abort, as +! appropriate. + + pfc_final=per_fcst_command + call argreplace(pfc_final,pfc_cmd_len,'%[FHOUR]', & + & ifhours(ifh)) + call argreplace(pfc_final,pfc_cmd_len,'%[FMIN]', & + & iftotalmins(ifh)) + + if(verb.ge.2) then + print *,' ' + print *,'!!! Running per-fcst command' + print *,'!!! Unparsed = ',trim(per_fcst_command) + print *,'!!! Parsed = ',trim(pfc_final) + endif + call run_command(trim(pfc_final),pfcret) + if(pfcret/=0 .and. verb.ge.1) then + print *,' ' + print *,'!!! Non-zero exit status from per-fcst command' + print *,'!!! Command = ',trim(pfc_final) + print *,'!!! Exit status = ',pfcret + print *,'!!! Continuing anyway...' + elseif(pfcret==0 .and. verb.ge.2) then + print *,' ' + print *,'!!! Per-fcst command returned success status (0)' + endif + endif + + ifh = ifh + 1 + if (ifh > ifhmax) exit ifhloop + + if (inp%file_seq == 'multi') then + call baclose(lugb,igcret) + call baclose(lugi,iicret) + if ( verb .ge. 3 ) then + print *,'baclose return code for unit ',lugb,' = igcret = ' + & ,igcret + print *,'baclose return code for unit ',lugi,' = iicret = ' + & ,iicret + endif + endif + + enddo ifhloop +c +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) +c + 73 format ('fixpos ',a4,' fhr= ',i4,':',i2.2,' Fix position= ' + & ,f7.2,'E (',f6.2,'W)',2x,f7.2,' Max Wind= ',i3,' kts') + + if (allocated(prstemp)) deallocate (prstemp) + if (allocated(prsindex)) deallocate (prsindex) + if (allocated(iwork)) deallocate(iwork) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) +cPENG----2018-06-07 ------------------------ + if (allocated(ushear)) deallocate (ushear) + if (allocated(rhumid)) deallocate (rhumid) + + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(vt850_flag)) deallocate (vt850_flag) + if (allocated(closed_mslp_ctr_flag)) + & deallocate (closed_mslp_ctr_flag) + if (allocated(netcdf_file_time_values)) + & deallocate (netcdf_file_time_values) + if (allocated(nctotalmins)) + & deallocate (nctotalmins) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine argreplace(arg,n,name,val) + ! This subroutine is used to generate the pre-forecast-command + ! It will edit the command (argument "arg") and replace string + ! name with value val. That is how the per-forecast-command + ! has these modifications: + + ! %[FHOUR] -> replace with -> last forecast hour + ! %[FMIN] -> replace with -> last forecast minute + + implicit none + + integer, intent(in) :: n + character(n), intent(inout) :: arg + character(*), intent(in) :: name + integer, intent(in) :: val + + integer found,namelen,i1,i2 + character(n) :: out + + found=index(arg,name) + namelen=len(name) + i1=found-1 ! last char that is before name + i2=found+namelen ! index of last char in name + + if(found==0) return + + out=' ' + + if(found>1 .and. i21) then +! special case: name is at end of string +! hope the value fits... + write(out,'(A,I0)') arg(1:i1),val + elseif(i2 + & ,'... gopen_i_file= ...',a,'...') + + print *,'gopen_g_file= ',gopen_g_file,'....' + print *,'gopen_i_file= ',gopen_i_file,'....' + + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + inquire (unit=lout, opened=output_file_open) + if (output_file_open) then + iooret = 0 + else + fnameo(1:5) = "fort." + write(fnameo(6:7),'(I2)') lout + call baopenw (lout,fnameo,iooret) + endif + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + inquire (file=gopen_g_file, opened=file_open4) + if (file_open4) then + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is OPEN' + else + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is CLOSED' + endif + + inquire (file=gopen_i_file, opened=file_open5) + if (file_open5) then + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is OPEN' + else + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'gettrk baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine open_ncfile (filename,ncid) + +c ABSTRACT: This subroutine opens a netcdf file specified by the +c input file "ncfile" and returns the netcdf file id that will be +c associated with that file. +c +c INPUT: +c ncfile character full-path file netcdf name +c +c OUTPUT: +c ncfile_id integer, netcdf id assigned to the netcdf file + + implicit none + + include "netcdf.inc" + + character*(*), intent(in) :: filename + integer, intent(out) :: ncid + integer :: status + + status = nf_open (filename, NF_NOWRITE, ncid) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine open_ncfile +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine is_it_a_storm (imax,jmax,dx,dy,cparm,ist + & ,defined_pt,parmlon,parmlat + & ,parmval,trkrinfo,stormcheck,isiret) + +c ABSTRACT: This subroutine is called after the center of the storm +c has been fixed. Its purpose is to determine whether or not +c the center that was found is actually a storm, and not just some +c passing trough (this has happened in the case of decaying or weak +c storms). It's called twice -- once to check for a minimum MSLP +c gradient, and once to check for a circulation at 850 mb. The +c subroutine input parameter "cparm" determines which parameter to +c check for. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is to be checked: +c slp = mslp, for a check of mslp gradient +c v850 = tangential winds at 850 mb +c ist integer storm number (internal to the tracker) +c defined_pt Logical; bitmap indicating if valid data at that pt. +c parmlon Longitude of the max/min value for the input parameter +c parmlat Latitude of the max/min value for the input parameter +c parmval Data value at parm's max/min point (used for mslp call) +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c stormcheck Character; set to 'Y' if mslp gradient or 850 mb +c tangential winds check okay. +c isiret Return code for this subroutine. +c + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE tracked_parms; USE atcf; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + real vt,vtavg,vr,parmlat,parmlon,parmval,dist + real pthresh,vthresh,degrees,dx,dy,dell,ri,radinf + real pgradient,xmaxpgrad + character(*) cparm + logical(1) defined_pt(imax,jmax) + character*1 stormcheck + integer isiret,imax,jmax,ist,npts,ilonfix,jlatfix,igvtret + integer ibeg,iend,jbeg,jend,ivt,i,j,iix,jix,bskip,igiret + + isiret = 0 + stormcheck = 'N' + + dell = (dx+dy)/2. + +c First define the radius of influence, which depends on the +c grid spacing of the model data being used. The ceiling statement +c for npts in the first if statement is needed in case the +c resolution of the grib files eventually goes very low, down to +c say a half degree or less, in order to cover enough points in +c the search. + + if (dell < 1.24) then ! GFS, MRF, NAM, NGM, NAVGEM, GDAS, + ! GFDL, NCEP Ensemble & Ensemble + ! Relocation, SREF Ensemble + ri = ritrk_most + if (cparm == 'slp') then + radinf = 300.0 + else + radinf = 225.0 + endif + npts = ceiling(radinf/(dtk*(dx+dy)/2.)) + else if (dell >= 1.24 .and. dell < 2.49) then ! UKMET + ri = ritrk_most + radinf = 275.0 + npts = 2 + else ! ECMWF + ri = ritrk_coarse + radinf = 350.0 + npts = 1 + endif + + pthresh = trkrinfo%mslpthresh ! These are read in in + vthresh = trkrinfo%v850thresh ! subroutine read_nlists.... + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,parmlon,parmlat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij B, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij B, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print*,' ' + print*,'!!! ERROR in is_it_a_storm from call to' + print*,'!!! get_ij_bounds, stopping processing for ' + print*,'!!! storm number ',ist + endif + + isiret = 92 + return + endif + +c If the input cparm is slp, then check to see that the MSLP +c gradient in any direction from the MSLP center is at least +c 1mb / 200km, or 0.005mb/km. This is based on discussions with +c Morris & Bob, who have had good results using a 2mb/200km +c requirement. Since their model has a much finer resolution than +c all of the models we run the tracker on AND a much better +c depiction of the hurricane vortex, we do not use a requirement +c as strict as theirs, and so make the requirement only half as +c strong as theirs. +c +c If the input cparm is v850, then check to see that there is +c a circulation at 850 mb. We will do this by calculating the +c tangential wind of all points within a specified radius of +c the 850 minimum wind center, and seeing if there is a net +c average tangential wind speed of at least 5 m/s. +c +c UPDATE APRIL 2000: I've relaxed the thresholds slightly from +c 0.005 mb/km to 0.003 mb/km, and the wind threshold from +c 5 m/s to 3 m/s. Also, note that a special case for GDAS has +c been hardwired in that is weaker (0.002 mb/km and 2 m/s). +c That weaker GDAS requirement is for Qingfu's relocation stuff. +c +c UPDATE JULY 2001: The relaxed requirement put in place in +c April 2000 for the GDAS relocation has also been put in place +c for the GFS ensemble relocation. + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the loop. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, ilonfix= ',ilonfix + & ,' jlatfix= ',jlatfix + print *,'ibeg jbeg iend jend = ',ibeg,jbeg,iend,jend + print *,'cparm= ',cparm,' parmlon parmlat = ',parmlon,parmlat + print *,'parmval= ',parmval + print *,' ' + endif + + vtavg = 0.0 + ivt = 0 + + xmaxpgrad = -999.0 + + jloop: do jix = jbeg,jend,bskip + iloop: do iix = ibeg,iend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine is_it_a_storm' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + i = iix - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine ' + print *,'!!! is_it_a_storm for a non-global grid.' + print *,'!!! STOPPING....' + print *,'!!! i= ',i,' imax= ',imax + print *,' ' + endif + + stop 97 + endif + endif + + call calcdist(parmlon,parmlat,glon(i),glat(j),dist,degrees) + + if (dist > radinf .or. dist == 0.0) cycle + + if (defined_pt(i,j)) then + + if (cparm == 'slp') then + pgradient = (slp(i,j) - parmval) / dist + if (pgradient > xmaxpgrad) xmaxpgrad = pgradient + + if ( verb .ge. 3 ) then + write (6,93) i,j,glon(i),glat(j),dist,slp(i,j),pgradient + endif + + if (pgradient > pthresh) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, valid pgradient found.' + print '(a23,f8.5)',' pgradient threshold = ',pthresh + print '(a23,f8.5)',' pgradient found = ',pgradient + print *,'mslp center = ',parmlon,parmlat,parmval + print *,'pgrad loc = ',glon(i),glat(j),slp(i,j) + endif + + stormcheck = 'Y' + exit jloop + endif + endif + + if (cparm == 'v850') then + call getvrvt (parmlon,parmlat,glon(i),glat(j) + & ,u(i,j,nlev850),v(i,j,nlev850),vr,vt,igvtret) + if ( verb .ge. 3 ) then + write (6,91) i,j,glon(i),glat(j),u(i,j,nlev850) + & ,v(i,j,nlev850),vr,vt + endif + + vtavg = vtavg + vt + ivt = ivt + 1 + endif + + endif + + enddo iloop + enddo jloop + + 91 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' u= ',f8.4,' v= ',f8.4,' vr= ',f9.5,' vt= ',f9.5) + + 93 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' dist= ',f8.2,' slp= ',f10.2,' pgradient= ',f8.5) + + if (stormcheck /= 'Y' .and. cparm == 'slp') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, valid pgradient NOT FOUND.' + write (6,94) '!!! (Max pgradient less than ',pthresh,' mb/km)' + 94 format (1x,a29,5x,f8.5,a7) + write (6,95) '!!! Max pgradient (mb/km) found = ',xmaxpgrad + 95 format (1x,a34,f8.5) + print *,' ' + endif + + endif + + if (cparm == 'v850') then + + if (ivt > 0) then + vtavg = vtavg / float(ivt) + else + vtavg = 0.0 + endif + + if (parmlat > 0) then + if (vtavg >= vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (>= +',vthresh,' m/s for a NH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed +',vthresh + & ,' m/s (NH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + else + if (vtavg <= -vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (<= -',vthresh,' m/s for a SH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed -',vthresh + & ,' m/s (SH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + endif + + endif +c + return + end +c +c----------------------------------------------------------------------- +ccPENG----2018-06-07 ------------------------ +c----------------------------------------------------------------------- +c subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm +c & ,cps_vals,wcore_flag,igpret) + subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag + & ,wcore_mean_val,wcore_point_max,igpret) + +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure or phase of a cyclone. Initially, we +c will just have it use the Hart cyclone phase space (CPS) scheme. + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trkrparms; USE grid_bounds + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character wcore_flag*1,okay_to_call_cps_routines*1 +cPENG----2018-06-07 ------------------------ + real wcore_mean_val,wcore_point_max + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real cps_vals(3) + real dx,dy,paramb,vtl_slope,vtu_slope + integer imax,jmax,igpret,igcpret,ist,ifh,maxstorm + integer igvpret,igcv1ret,igcv2ret + logical(1) valid_pt(imax,jmax) +c + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,611) + write (6,613) + write (6,615) + write (6,*) ' ' + + 611 format(1x,'#-----------------------------------------------#') + 613 format(1x,'# start of routine to determine cyclone phase...#') + 615 format(1x,'#-----------------------------------------------#') + endif + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + if (ifh > 1 .or. (ifh == 1 .and. trkrinfo%type == 'tracker')) + & then + + ! This condition that ifh > 1 is so that we *not* do the cps + ! stuff for fhour=0 if it's a tcgen or midlat case, since we + ! don't know the model storm motion direction for the + ! analysis. For a regular case where type = 'tracker', we + ! have the observed storm's heading direction from tc vitals, + ! so we can use that (even though the model's storm direction + ! may differ slightly from the observed storm). This current + ! if statement and the ones below carefully check for these + ! various instances. + + okay_to_call_cps_routines = 'n' + + if (ifh > 1) then + if (fixlon(ist,ifh-1) > -990.0 .and. + & fixlat(ist,ifh-1) > -990.0) then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level ' + print *,' >< since the fixlon and fixlat at the ' + print *,' >< previous lead time are undefined.' + print *,' >< This is likely the first found position' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + endif + endif + elseif (ifh == 1 .and. trkrinfo%type == 'tracker') then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level.' + print *,' >< The likely reason is that ifh=0 and' + print *,' >< this is a genesis case, so we do not ' + print *,' >< know the storm motion direction.' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + print *,' >< trkrinfo%type ',trkrinfo%type + endif + endif + + if (okay_to_call_cps_routines == 'y') then + + ! Similarly, these next two conditions (previous lat and + ! previous lon > -999) are in there in case we're doing a + ! tcgen or midlat case and this is the *first* time level + ! within a forecast that the storm has been detected (again, + ! we don't yet know the storm heading). + + call get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'lower',vtl_slope + & ,maxstorm,igcv1ret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'upper',vtu_slope + & ,maxstorm,igcv2ret) + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh) + & ,paramb,vtl_slope,vtu_slope + endif + + cps_vals(1) = paramb + cps_vals(2) = vtl_slope + cps_vals(3) = vtu_slope + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diagnostics were requested but will NOT' + print *,' >< be performed for this time level since we ' + print *,' >< are either at the first time level for a ' + print *,' >< genesis case (type = midlat or tcgen), or' + print *,' >< we are at any time level in which for some' + print *,' >< reason the fixlon and fixlat at the' + print *,' >< previous time level are not defined.' + print *,' >< ifh= ',ifh + endif + + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diags were requested but will NOT be' + print *,' >< performed for this time level since we are at' + print *,' >< time level 1 for a genesis case ' + print *,' >< (type = midlat or tcgen) and we cannot' + print *,' >< diagnose the model direction of storm' + print *,' >< movement. ifh= ',ifh + endif + + endif + + endif + + 73 format ('cps_stats: ',a4,' lead time= ',i3,':',i2,' paramb= ' + & ,f8.2,' vtl= ',f9.2,' vtu= ',f9.2) + + + if (phasescheme == 'vtt' .or. phasescheme == 'both') then +c call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +cPENG----2018-06-07 ------------------------ + call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag + & ,wcore_mean_val,wcore_point_max,igvpret) + + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + + 631 format(1x,'#-------------------------------------------------#') + 633 format(1x,'# End of routine to determine cyclone phase... #') + 635 format(1x,'#-------------------------------------------------#') + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines "Parameter B", which determines +c the degree of thermal symmetry between the "left" and "right" +c hemispheres of a storm, in the layer between 900 and 600 mb. +c We evaluate only those points that are within 500 km of the +c storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zthicksum(2) + real rlonc,rlatc,rlonb,rlatb,xdist,degrees,d,cosarg + real st_heading,st_heading_rad,ricps,dx,dy + real pt_dir,pt_dir_rad,zthick,hemval,paramb + real zthick_right_mean,zthick_left_mean + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer left_ct,right_ct,hemis,icount,maxstorm,ip + logical(1) valid_pt(imax,jmax) +c + ricps = 500.0 + +c ----------------------------------------------------------------- +c First, determine the angle that the storm took getting from the +c last position to the current one. If this is for ifh=1 for a +c regular type=tracker case, we will just use the storm direction +c as read from the tcvitals card. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + if (d == 0.0) then + + ! Storm is stationary... + st_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + print '(a43,f9.3)' + & ,' In get_cps_paramb, model storm heading = ' + & ,st_heading + print *,' ' + endif + +c ----------------------------------------------------------------- +c Now call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_paramb from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + igcpret = 92 + return + endif + +c ----------------------------------------------------------------- +c Now loop through all of the points of the subdomain. If the +c point is further than 500 km from the storm center, discard it. +c Otherwise, evaluate the angle from the storm center to this point +c to determine the hemisphere of the point, that is, if the point +c is to the left or the right of the storm track. +c ----------------------------------------------------------------- + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the + ! loop for the evaluation of parameter B. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + left_ct = 0 + right_ct = 0 + zthicksum = 0 + icount = 0 + +c print *,'CPS CORE: ibeg= ',ibeg,' iend= ',iend +c print *,'CPS CORE: jbeg= ',jbeg,' jend= ',jend + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + icount = icount + 1 + +c print *,'CPS CORE: ist= ',ist,' ifh= ',ifh,' j= ',j,' i= ',i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_paramb, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Parameter B will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_paramb' + print *,'!!! for a non-global grid.' + print *,'!!! Parameter B will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_PARAMB....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon= ',glon(ip),' glat= ',glat(j) + print *,'!!! Parameter B will not be computed.' + print *,'!!! EXITING GET_CPS_PARAMB....' + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + + !---------------------------------------------------------- + ! Calculate angle from storm center to point, in a 0-360 + ! framework, clockwise positive. + !---------------------------------------------------------- + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-fixlon(ist,ifh)) * dtr + rlatb = fixlat(ist,ifh) * dtr + d = degrees * dtr + + if (d > 0.) then + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_dir_rad = acos(cosarg) + else + pt_dir_rad = 2*pi - acos(cosarg) + endif + else + pt_dir_rad = 0.0 + endif + + pt_dir = pt_dir_rad / dtr + + !------------------------------------------------------------ + ! Based on the angle that the point is from the storm center, + ! determine if the point is to the left or the right of the + ! storm track. + !------------------------------------------------------------ + + if (st_heading >= 180.0) then + if ((st_heading - pt_dir) > 0.0 .and. + & (st_heading - pt_dir) <= 180) then + hemis = 2 + left_ct = left_ct + 1 + else + hemis = 1 + right_ct = right_ct + 1 + endif + else + if ((pt_dir - st_heading) > 0.0 .and. + & (pt_dir - st_heading) <= 180) then + hemis = 1 + right_ct = right_ct + 1 + else + hemis = 2 + left_ct = left_ct + 1 + endif + endif + + !------------------------------------------------------------ + ! Calculate the 600-900 mb thickness at this point and add + ! the thickness value to the array for the correct "storm + ! hemisphere". + !------------------------------------------------------------ + + zthick = cpshgt(ip,j,7) - cpshgt(ip,j,1) + zthicksum(hemis) = zthicksum(hemis) + zthick + + if ( verb .ge. 3 ) then + write (6,51) rlonb/dtr,rlatb/dtr,rlonc/dtr,rlatc/dtr + & ,st_heading,pt_dir,hemis,zthick + endif + + enddo iloop + enddo jloop + + 51 format (1x,'stlon stlat = ',2(f6.2,2x),' ptlon ptlat = ' + & ,2(f6.2,2x),' sthead= ',f6.2,' ptdir= ',f6.2,' hemis= ' + & ,i1,' zthick= ',f7.2) + +c ------------------------------------------------------------------ +c Now calculate parameter B. The hemval parameter = +1 for storms +c in the Northern Hemisphere and -1 for Southern Hemisphere storms. +c ------------------------------------------------------------------ + + zthick_right_mean = zthicksum(1) / float(right_ct) + zthick_left_mean = zthicksum(2) / float(left_ct) + + if (fixlat(ist,ifh) < 0.0) then + hemval = -1.0 + else + hemval = 1.0 + endif + + paramb = hemval * (zthick_right_mean - zthick_left_mean) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' right_ct= ',right_ct,' left_ct= ',left_ct + print *,' zthicksum(1)= ',zthicksum(1) + print *,' zthicksum(2)= ',zthicksum(2) + print *,' zthick_right_mean= ',zthick_right_mean + print *,' zthick_left_mean= ',zthick_left_mean + print *,' hemval= ',hemval + print *,' END of get_cps_paramb, paramb= ',paramb + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,clayer,vth_slope,maxstorm,igcvret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines the thermal wind profile for +c either the lower troposphere (i.e., between 600 and 900 mb) or the +c upper troposphere (i.e., between 300 and 600 mb). We evaluate +c only those points that are within 500 km of the storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character clayer*5 + real tmp1,tmp2,tmp3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zmax(7),zmin(7),zdiff(7),xlolevs(7),xhilevs(7),plev(7) + real dlnp(7),dzdlnp(7),dz(7),lnp(7) + real vth_slope,xdist,degrees,d,cosarg + real ricps,dx,dy,R2 + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j,k,kix + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igcvret,igiret + integer kbeg,kend,maxstorm,ip + logical(1) valid_pt(imax,jmax) + + data xlolevs /900.,850.,800.,750.,700.,650.,600./ + data xhilevs /600.,550.,500.,450.,400.,350.,300./ +c data xlolevs /90000.,85000.,80000.,75000.,70000.,65000.,60000./ +c data xhilevs /60000.,55000.,50000.,45000.,40000.,35000.,30000./ +c + ricps = 500.0 + plev = 0.0 + + if (clayer == 'lower') then + kbeg = 1 + kend = 7 + plev = xlolevs + else + kbeg = 7 + kend = 13 + plev = xhilevs + endif + +c ----------------------------------------------------------------- +c First, call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_vtl from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + igcvret = 92 + return + endif + +c ------------------------------------------------------------------ +c Now loop through all of the points of the subdomain at each level. +c If a point is further than 500 km from the storm center, discard +c it. Otherwise, evaluate the gp height at the point to determine +c if it is a max or a min for the given level. Store the max and +c min height at each level in an array. +c ------------------------------------------------------------------ + +c ! We will want to speed things up for finer resolution grids. +c ! We can do this by skipping some of the points in the +c ! loop for the evaluation of parameter B. +c +c if ((dx+dy)/2. > 0.20) then +c bskip = 1 +c else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then +c bskip = 2 +c else if ((dx+dy)/2. <= 0.10) then +c bskip = 3 +c endif + + bskip = 1 ! Don't do any skipping for now.... + + zmax = -9999999.0 + zmin = 9999999.0 + zdiff = 0.0 + lnp = 0.0 + + levloop: do k = kbeg,kend + + if (kbeg == 7) then + ! processing upper layers (600-300 mb) + kix = k - 6 + else + ! processing lower layers (900-600 mb) + kix = k + endif + + lnp(kix) = log(plev(kix)) + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_vth, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_vth' + print *,'!!! for a non-global grid.' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_VTH....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j,' k= ',k + & ,' clayer= ',clayer + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon(ip)= ',glon(ip),' glat= ',glat(j) + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! EXITING GET_CPS_VTH....' + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + + tmp1 = zmax(kix) + tmp2 = cpshgt(ip,j,k) + tmp3 = zmin(kix) + + zmax(kix) = max(tmp1,tmp2) + zmin(kix) = min(tmp3,tmp2) + +c zmax(kix) = max(zmax(kix),cpshgt(ip,j,k)) +c zmin(kix) = min(zmin(kix),cpshgt(ip,j,k)) + + enddo iloop + enddo jloop + + zdiff(kix) = zmax(kix) - zmin(kix) + + enddo levloop + +c ------------------------------------------------------------------ +c Now calculate the vertical derivative of the gp height, that is, +c d(dz)/d(ln(p)). Here, zdiff is the gp height perturbation at a +c given level, calculated in the loop above; dz is the vertical +c change in that perturbation from one level to the next. +c ------------------------------------------------------------------ + + dz = 0.0 + dlnp = 0.0 + dzdlnp = 0.0 + + do k = 2,7 + dz(k) = zdiff(k) - zdiff(k-1) + dlnp(k) = log(plev(k)) - log(plev(k-1)) + dzdlnp(k) = dz(k) / dlnp(k) + enddo + +c ------------------------------------------------------------------ +c Now call a correlation routine to get the slope of a regression +c line. The independent variable that we input is dlnp, the change +c in log of pressure with height. The dependent variable is +c dzdlnp, the vertical change in the height perturbation with +c respect to the change in pressure. The slope that is returned +c defines whether we've got a cold core or warm core system. +c See Hart (MWR, April 2003, Vol 131, pp. 585-616) for more +c details, specifically his Fig. 3 and the discussion surrounding. +c Note that in the call to calccorr, we are sending only 6 of the +c 7 elements of the dlnp and dzdlnp arrays, beginning with the +c 2nd element of each. That's because the first array value for +c each of those arrays is empty, since in the loop just above, we +c start with kbeg+1, not kbeg. +c ------------------------------------------------------------------ + + call calccorr(lnp(2),zdiff(2),6,R2,vth_slope) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ In get_cps_vth, values for vth follow for ' + & ,'lead time= ',ifhours(ifh),':',ifclockmins(ifh),' ' + & ,storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' ... clayer = ',clayer + print *,' ' + endif + + do k = kbeg,kend + + if (kbeg == 7) then + kix = k - 6 + else + kix = k + endif + + if ( verb .ge. 3 ) then + print *,' ' + write (6,31) k,plev(kix),zmax(kix),zmin(kix),zdiff(kix) + if (kix > 1) then + write (6,32) plev(kix),log(plev(kix)) + & ,plev(kix-1),log(plev(kix-1)) + write (6,33) dz(kix),dlnp(kix),dzdlnp(kix) + else + write (6,34) + endif + endif + + enddo + + 31 format (1x,' +++ k= ',i2,' press= ',f8.1,' zmax= ',f7.2 + & ,' zmin= ',f7.2,' zdiff= ',f7.2) + 32 format (1x,' ln(',f7.1,')= ',f9.6,' ln(',f7.1,')= ',f9.6) + 33 format (1x,' dz= ',f10.2,' dlnp= ',f13.6,' dzdlnp= ',f12.3) + 34 format (1x,' --- First level... no derivatives done...') +c + return + end +c +C---------------------------------------------------- +C +C---------------------------------------------------- + subroutine calccorr(xdat,ydat,numpts,R2,slope) +c +c This subroutine is the main driver for a series of +c other subroutines below this that will calculate the +c correlation between two input arrays, xdat and ydat. +c +c INPUT: +c xdat array of x (independent) data points +c ydat array of y (dependent) data points +c numpts number of elements in each of xdat and ydat +c +c OUTPUT: +c R2 R-squared, the coefficient of determination +c slope Slope of regression line +c +c xdiff array of points for xdat - xmean +c ydiff array of points for ydat - ymean +c yestim array of regression-estimated points +c yresid array of residuals (ydat(i) - yestim(i)) + + USE verbose_output + + implicit none + + real xdat(numpts),ydat(numpts) + real xdiff(numpts),ydiff(numpts) + real yestim(numpts),yresid(numpts) + real xmean,ymean,slope,yint,R2 + integer numpts,i + +c + call getmean(xdat,numpts,xmean) + call getmean(ydat,numpts,ymean) +c + call getdiff(xdat,numpts,xmean,xdiff) + call getdiff(ydat,numpts,ymean,ydiff) +c + call getslope(xdiff,ydiff,numpts,slope) + yint = ymean - slope * xmean +c + call getyestim(xdat,slope,yint,numpts,yestim) + call getresid(ydat,yestim,numpts,yresid) +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * CPS Thermal wind regression details * ' + print *,' *--------------------------------------------------* ' + endif + + call getcorr(yresid,ydiff,numpts,R2) + + if ( verb .ge. 3 ) then + print *,' i ydat xdat ydiff xdiff e' + & ,' e2 ydiff2' + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + do i = 1,numpts + write(6,'(2x,i3,2x,f7.2,2x,f7.4,2x,f7.2,2x,f7.4,3(2x,f7.2))') + & i,ydat(i),xdat(i),ydiff(i) + & ,xdiff(i),yresid(i),yresid(i)*yresid(i) + & ,ydiff(i)*ydiff(i) + enddo + + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + print *,' ' + write (6,'(1x,a13,f9.3,3x,a5,f7.2)') ' means: y: ',ymean + & ,' x: ',xmean + + write (6,*) ' ' + write (6,30) 'slope= ',slope,' y-intercept = ',yint + 30 format (2x,a7,f10.3,a23,f10.3) + if (slope .gt. 0.0) then + write(6,40) 'Regression equation: Y = ',yint,' + ',slope + else + write(6,40) 'Regression equation: Y = ',yint,' - ' + & ,abs(slope) + endif + 40 format (2x,a27,f8.2,a3,f8.2,'X') +c + print *,' ' + write (6,'(1x,a17,f7.4,5x,a7,f7.4)') ' R2(r_squared) = ',R2 + & ,' r = ',sqrt(R2) + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * End of regression details * ' + print *,' *--------------------------------------------------* ' + endif + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getmean(xarr,inum,zmean) +c +c This subroutine is part of the correlation calculation, +c and it simply returns the mean of the input array, xarr. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c +c OUTPUT: +c zmean mean of data values in xarr + + implicit none + + real xarr(inum) + real xsum,zmean + integer i,inum +c + xsum = 0.0 + do i = 1,inum + xsum = xsum + xarr(i) + enddo +c + zmean = xsum / float(MAX(inum,1)) +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getdiff(xarr,inum,zmean,zdiff) +c +c This subroutine is part of the correlation calculation, +c and it returns in the array zdiff the difference values +c between each member of the input array xarr and the +c mean value, zmean. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c zmean mean of input array (xarr) +c +c OUTPUT: +c zdiff array containing xarr(i) - zmean + + implicit none + + real xarr(inum),zdiff(inum) + real zmean + integer i,inum +c + do i = 1,inum + zdiff(i) = xarr(i) - zmean + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + + subroutine getslope(xarr,yarr,inum,slope) +c +c This subroutine is part of the correlation calculation, +c and it returns the slope of the regression line. +c +c INPUT: +c xarr input array of xdiffs (x - xmean) +c yarr input array of ydiffs (y - ymean) +c inum number of points in x & y arrays +c +c OUTPUT: +c slope slope of regression line + + real xarr(inum),yarr(inum) + real slope,sumxy,sumx2 + integer i,inum + +c First sum up the xarr*yarr products.... + + sumxy = 0.0 + do i = 1,inum + sumxy = sumxy + xarr(i) * yarr(i) + enddo + +c Now sum up the x-squared terms.... + + sumx2 = 0.0 + do i = 1,inum + sumx2 = sumx2 + xarr(i) * xarr(i) + enddo + +c Now get the slope.... + + slope = sumxy / sumx2 + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getyestim(xarr,slope,yint,inum,yestim) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the predicted y-values using the +c regression equation that has been calculated. +c +c INPUT: +c xarr array of x data points +c slope slope of the calculated regression line +c yint y-intercept of the calculated regression line +c inum number of input points +c +c OUTPUT: +c yestim array of y pts estimated from regression eqn. + + implicit none + + real xarr(inum),yestim(inum) + real slope,yint + integer i,inum +c + do i = 1,inum + yestim(i) = yint + xarr(i) * slope + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getresid(yarr,yestim,inum,yresid) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the residual values between the +c input y data points and the y-estim predicted y values. +c +c INPUT: +c yarr array of y data points +c yestim array of y pts estimated from regression eqn. +c inum number of input points +c +c OUTPUT: +c yresid array of residuals (ydat(i) - yestim(i)) + + implicit none + + real yarr(inum),yestim(inum),yresid(inum) + integer i,inum +c + do i = 1,inum + yresid(i) = yarr(i) - yestim(i) + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getcorr(yresid,ydiff,inum,R2) +c +c This subroutine is part of the correlation calculation, +c and it does the actual correlation calculation. +c +c INPUT: +c yresid array of residuals (ydat(i) - yestim(i)) +c ydiff array of points for ydat - ymean +c inum number of points in the arrays +c +c OUTPUT: +c R2 R-squared, the coefficient of determination + + USE verbose_output + + implicit none + + real yresid(inum),ydiff(inum) + real R2,sumyresid,sumydiff + integer i,inum +c + sumyresid = 0.0 + sumydiff = 0.0 + + do i = 1,inum + sumyresid = sumyresid + yresid(i) * yresid(i) + sumydiff = sumydiff + ydiff(i) * ydiff(i) + enddo + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,30) 'Sum of y-residuals squared (e2) = ',sumyresid + write (6,30) 'Sum of y-diffs squared (ydiff2) = ',sumydiff + write (6,*) ' ' + 30 format (1x,a35,f15.2) + endif + +c if (sumydiff == 0.0) then +c R2=1.0 +c else +c R2 = 1 - sumyresid / sumydiff +c endif +c PENG 05/14/2018 Bug-fixed for R2 calculation with FENS job crashed. + if (sumyresid .lt. sumydiff) then + if (sumydiff .le. 0.000001) then + R2 = 1.0 + else + R2 = 1 - sumyresid / sumydiff + endif + else + R2=0.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- +c subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo +c & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +cPENG----2018-06-07 ------------------------ + subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag + & ,wcore_mean_val,wcore_point_max,igvpret) + +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. Here, we are only looking +c at the mid-to-upper tropospheric warm anomaly at the center of +c the storm. The temperature data that we are searching through in +c the tmean array should be the 300-500 mb mean temperature data. +c The criteria in this algorithm are based loosely on Vitart's +c criteria for warm core checking, but the nuts & bolts of the +c subroutine use algorithms from this tracker, including the barnes +c analysis. First, we locate the warm core with the find_maxmin +c routine. Then we use the check_closed_contour routine to see if +c there is a closed temperature contour surrounding the warm core. +c +c INPUT: +c inp +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c inp contains input date and model number information +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c ist integer storm number (internal to the tracker) +c ifh integer index for lead time +c trkrinfo derived type containing grid info on user boundaries +c fixlon array containing found fix longitudes +c fixlat array containing found fix latitudes +c valid_pt Logical; bitmap indicating if valid data at that pt. +c maxstorm maximum # of storms to be handled +c +c OUTPUT: +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c igvpret Return code for this subroutine. +c +c LOCAL: +c wcore_mean_val barnes-averaged value of the temperature at the +c location where the tracker found the warm core. +c wcore_point_max max temperature found at a gridpoint near the +c location where the tracker found the warm core using +c barnes analysis. + + USE set_max_parms; USE grid_bounds; USE trkrparms; USE contours + USE tracked_parms; USE gen_vitals; USE def_vitals; USE inparms + USE phase + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo,wcore_trkrinfo + type (cint_stuff) wcore_contour_info + type (datecard) inp + + character*1 get_last_contour_flag,wcore_flag + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dx,dy,wcore_mean_val,wcore_mean_lon,wcore_mean_lat + real wcore_point_max,tlastcont,rlastcont,tlastout,rlastout + integer imax,jmax,igvpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer icount,maxstorm,ip,ifmret,ifilret,ifix,jfix,icccret + integer num_check_conts + logical(1) valid_pt(imax,jmax),compflag,wcore_mask(imax,jmax) + logical(1) output_file_open +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of get_vtt_phase *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for warm core at hour ',i4,':',i2.2) + write (6,103) wcore_depth + 103 format (1x,'* Warm core depth threshold (wcore_depth) = ',f7.2) + print *,'*-------------------------------------------------*' + endif + +c ------------------------------------------------------------ +cPENG----2018-06-07 ------------------------ + wcore_mean_val = -99.0 + wcore_point_max = -99.0 + + wcore_mask = .false. + wcore_mean_lon = -999.0 + wcore_mean_lat = -999.0 + wcore_trkrinfo = trkrinfo ! set equal to values from trkrinfo... + wcore_trkrinfo%contint = wcore_depth ! ...except use the warm + ! core contour interval specified by + ! the user in the extrkr.sh script. + +c ------------------------------------------------------------ +c First, call find_maxmin to locate the warm core + + call find_maxmin (imax,jmax,dx,dy,'tmp' + & ,tmean,'max',ist,fixlon(ist,ifh),fixlat(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,compflag + & ,wcore_mean_lon,wcore_mean_lat,wcore_mean_val + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + + if (verb .ge. 3) then + print *,' ' + print *,'After call to find_maxmin for wcore, ifmret= ',ifmret + print *,' wcore_mean_val= ',wcore_mean_val + endif + +c ------------------------------------------------------------ +c Once find_maxmin returns a value and a location for the +c barnes-averaged value of a warm core, then make a call to +c fix_latlon_to_ij to (1) get the actual gridpoint value of the +c temperature (the value stored in wcore_mean_val is an +c area-averaged value coming from the barnes analysis), and +c (2) to get the (i,j) indeces for this gridpoint to be used in +c the call to check_closed_contour below. + + if (wcore_mean_lat > -99.0 .and. wcore_mean_lon > -990.0) then + call fix_latlon_to_ij (imax,jmax,dx,dy,tmean,'max' + & ,valid_pt,wcore_mean_lon,wcore_mean_lat + & ,wcore_mean_val,ifix,jfix,wcore_point_max,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Warm core stats: ' + write (6,105) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_mean_lon,360.-wcore_mean_lon + & ,wcore_mean_lat,wcore_mean_val + write (6,106) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,ifix,jfix,wcore_point_max + endif + + else + ! Search went out of regional grid bounds.... + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN get_vtt_phase. The call to ' + print *,'!!! fix_latlon_to_ij returned a non-zero return ' + print *,'!!! code, which means that the search for the fix' + print *,'!!! i and j went out of bounds for a regional ' + print *,'!!! grid. This should have been caught in a ' + print *,'!!! previous call to find_maxmin for one of the ' + print *,'!!! various fix parms. In any event, we will not' + print *,'!!! search for a warm core for this storm and ' + print *,'!!! lead time.' + print *,' ' + write (6,115) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,'U',-999.99,-9999.99 + endif + + igvpret = 95 + wcore_flag = 'u' + return + endif + endif + + 105 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' mean_lon: ',f7.2,'E' + & ,1x,'(',f7.2,'W)',2x,'mean_lat: ',f7.2,2x + & ,'wcore_mean_val(K): ',f12.3) + 106 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' ifix: ',i5,2x + & ,' jfix: ',i5,2x,'wcore_point_max(K): ',f12.3) + + +c ------------------------------------------------------------ +c The Vitart scheme specifies that the temperature must decrease +c by at least 1.0C in all directions from the warm core center +c within a distance of 8 deg. A rigorous check of this criterion +c is performed here by utilizing the check_closed_contour routine. +c If we have a closed contour in the temperature field +c surrounding the warm core (using a 1 deg K interval), that +c criterion is satisfied. For diagnostic purposes, we set the +c value of num_check_conts to 999 in order to keep searching for +c all contours surrounding the warm core, and this allows us to +c get an idea of the "depth" or magnitude of the warm core when +c the tlastcont and rlastcont values are returned. + + wcore_contour_info%numcont = maxconts + num_check_conts = 999 + + get_last_contour_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,tmean + & ,valid_pt,wcore_mask,wcore_flag,'max',wcore_trkrinfo + & ,num_check_conts,wcore_contour_info,get_last_contour_flag + & ,tlastcont,rlastcont,icccret) + + if (wcore_flag == 'y') then + tlastout = tlastcont + rlastout = rlastcont/0.539638 + else + tlastout = -999.0 + rlastout = -9999.0 + endif + + if ( verb .ge. 3 ) then + write (6,115) storm(ist)%tcv_storm_id,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_flag,tlastout,rlastout + + 115 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2 + & ,' wcore_flag= ',a1,2x,' Temp of last contour(K) = ' + & ,f7.2,2x,'Radius of last contour(km) = ',f8.2) + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_sfc_center (xmeanlon,xmeanlat,clon + & ,clat,ist,ifh,calcparm,xsfclon,xsfclat + & ,maxstorm,igscret) +c +c ABSTRACT: This subroutine computes a modified lat/lon fix position +c to use as the input center position for the subroutines that +c follow which calculate surface-wind related values. The reason +c for this is that since we are concerned with the positioning of +c low-level wind features (e.g., rmax), we want the center position +c to be based solely on low-level features. We'll use mslp and the +c min in the sfc wind speed. If a center fix was unable to be made +c at this forecast hour for mslp and low-level winds, then we will +c stick with just using the mean position we got using all the other +c parameters. +c +c INPUT: +c xmeanlon The mean center longitude computed from all the various +c parameter fixes found in array clon +c xmeanlat The mean center latitude computed from all the various +c parameter fixes found in array clat +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Index for storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c (if a parameter fix could not be made at this forecast +c hour, then calcparm is set to false for this time for +c that parameter). +c maxstorm Maximum number of storms that can be tracked +c +c OUTPUT: +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c igscret Return code from this subroutine + + USE set_max_parms + USE verbose_output + + implicit none + + integer ist,ifh,ipct,igscret,maxstorm + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmeanlon,xmeanlat + real xsfclon,xsfclat,xlonsum,xlatsum + logical(1) calcparm(maxtp,maxstorm) + + ipct = 0 + xlonsum = 0.0 + xlatsum = 0.0 + + ! Do NOT include MSLP for the surface center at this time. +c if (calcparm(9,ist)) then +c ipct = ipct + 1 +c xlonsum = xlonsum + clon(ist,ifh,9) +c xlatsum = xlatsum + clat(ist,ifh,9) +c endif + + if (calcparm(10,ist)) then +c ! NOTE: Put double weighting on surface wind center if +c ! the tracker was able to find a center for it.... +c ipct = ipct + 2 +c xlonsum = xlonsum + 2.*clon(ist,ifh,10) +c xlatsum = xlatsum + 2.*clat(ist,ifh,10) + ! Just use single weighting for the sfc wcirc fix + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,10) + xlatsum = xlatsum + clat(ist,ifh,10) + endif + + if (calcparm(11,ist)) then + ! This is for the sfc vorticity center.... + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,11) + xlatsum = xlatsum + clat(ist,ifh,11) + endif + + if (ipct > 0) then + xsfclon = xlonsum / float(ipct) + xsfclat = xlatsum / float(ipct) + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In get_fract_wind_cov, CANNOT get modified fix ' + print *,'!!! position because the parameter fixes for mslp' + print *,'!!! and the sfc winds could not be obtained at this' + print *,'!!! forecast hour. ist= ',ist,' ifh= ',ifh + print *,'!!! We will use the fixlon and fixlat values for' + print *,'!!! this forecast hour.' + endif + + xsfclon = xmeanlon + xsfclat = xmeanlat + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ In get_sfc_center, modified fix (mslp + sfc_winds)' + print *,'+++ position follows: ' + print *,'+++ ' + print *,'+++ mslp: lon: ',clon(ist,ifh,9),' lat: ' + & ,clat(ist,ifh,9) + print *,'+++ sfc_winds: lon: ',clon(ist,ifh,10),' lat: ' + & ,clat(ist,ifh,10) + print *,'+++ sfc_vorticity: lon: ',clon(ist,ifh,11),' lat: ' + & ,clat(ist,ifh,11) + print *,'+++ multi-parm mean: lon: ',xmeanlon,' lat: ' + & ,xmeanlat + print *,'+++ sfc-only mean: lon: ',xsfclon,' lat: ',xsfclat + endif + + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,er_wind,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm + & ,trkrinfo,igwsret) +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure of the low level winds of a cyclone. +c The algorithm will search out at specified distances from the +c storm center along arcs in each quadrant of the storm, +c evaluating the winds every 15 degrees along the arc. In each +c arc, start 7.5 degrees in, then make stops at 22.5, 37.5, +c 52.5, 67.5, and 82.5 degrees. At each of those points, we +c will bilinearly interpolate the winds to the points along those +c arcs. Then we compute a quadrant average of the wing magnitude, +c as well as the mean Vt and Vr values. This will be done +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 earth-relative +c quadrants: NE, SE, SW and NW. For the storm-relative estimates, +c these mean values of the wind will be computed for the same +c relative quadrants (front-right, back-right, back-left, front- +c left, but with respect (positive clockwise) to the +c direction of storm motion. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated +c er_wind: Quadrant winds in earth-relative framework +c sr_wind: Quadrant winds in storm-relative framework +c er_vr: Quadrant radial winds in earth-relative framework +c sr_vr: Quadrant radial winds in storm-relative framework +c er_vt: Quadrant tangential winds in earth-relative framework +c sr_vt: Quadrant tangential winds in storm-relative framework + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,num_qtr_azim=6 + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igvtret,ipct,maxstorm,iazim,azimuth_ct + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,xsfclon,xsfclat,wmag,wmag_sum + real vr,vt,vr_sum,vt_sum + logical(1) valid_pt(imax,jmax) +c + data rdist/10.,25.,50.,75.,100.,125.,150.,200.,250.,300.,350. + & ,400.,450.,500./ + + igwsret = 0 + + er_wind = 0.0 + sr_wind = 0.0 + er_vr = 0.0 + er_vt = 0.0 + sr_vr = 0.0 + sr_vt = 0.0 + +c ----------------------------------------------------------------- +c Now determine the angle that the storm took getting from the +c last position to the current one. If this is the initial time, +c use the observed direction of motion from the TC Vitals. This +c may not match up with the model storm's initial direction of +c motion, but it is all we have available to us in order to get +c a heading estimate for the initial time. This storm heading +c information will be used for the storm-relative profiles. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_wind_structure, fhr= ',fhreal(ifh) + & ,' ',storm(ist)%tcv_storm_id + & ,' ',storm(ist)%tcv_storm_name + print '(a25,a23,f9.3)',' In get_wind_structure, ' + & ,' model storm heading = ',st_heading + print *,' ' + endif + + endif + +c ----------------------------------------------------------------- +c Get the profiles for the earth-relative coordinate system. +c Start with NE, then SE, SW, and NW. First go through +c radiusloop, which goes from one radial distance to the next, +c then do the quadloop, which goes through each quadrant, and +c then within each quadrant, the qtr_azimloop goes through for +c six points along an arc, spaced 15 degrees apart, starting at +c 7.5 degrees clockwise from the north. +c ----------------------------------------------------------------- + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *****************************************************' + print *,' Wind Structure: distbear bilin interp starts here.' + print *,' *****************************************************' + print *,' ' + endif + + radiusloop1: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- ER structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop1: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + ! In each quadrant, run through six points along an + ! arc and evaluate the winds. + + qtr_azimloop1: do iazim = 1,num_qtr_azim + + bear = ((iquad-1) * 90.) + ((iazim-1) * 15.) + 7.5 + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' earth-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,' ' + print '(5(a10,f7.2))',' sfclat= ',xsfclat + & ,' sfclon= ',xsfclon + & ,' rdist= ',rdist(idist),' targlat= ',targlat + & ,' targlon= ',targlon + print '(19x,a8,f7.2,35x,a9,f7.2)','sfclon= ',360.-xsfclon + & ,'targlon= ',360.-targlon + endif + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop1 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + er_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + er_vr(iquad,idist) = vr_sum / float(azimuth_ct) + er_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + er_wind(iquad,idist) = -999.0 + er_vr(iquad,idist) = -999.0 + er_vt(iquad,idist) = -999.0 + endif + + enddo quadloop1 + + enddo radiusloop1 + +c ----------------------------------------------------------------- +c Get the profiles for the storm-relative coordinate system. +c Start with the front-right quadrant and go clockwise through +c back-right, back-left and front-left. +c ----------------------------------------------------------------- + + radiusloop2: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- SR structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop2: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + qtr_azimloop2: do iazim = 1,num_qtr_azim + +c temp_bear = st_heading + ((iquad-1) * 90.) + 45. + + temp_bear = st_heading + ((iquad-1) * 90.) + & + ((iazim-1) * 15.) + 7.5 + bear = mod(temp_bear,360.) + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' storm-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop2 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + sr_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + sr_vr(iquad,idist) = vr_sum / float(azimuth_ct) + sr_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + sr_wind(iquad,idist) = -999.0 + sr_vr(iquad,idist) = -999.0 + sr_vt(iquad,idist) = -999.0 + endif + + enddo quadloop2 + + enddo radiusloop2 +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,calcparm,wfract_cov,pdf_ct_bin,pdf_ct_tot,maxstorm + & ,trkrinfo,igfwret) +c +c ABSTRACT: This subroutine determines the fractional areal coverage +c of winds exceeding various thresholds within specified arcs +c (e.g., 200 km, 400 km, etc) in each quadrant of a storm. The bins +c that are used go as follows: (1) 0-100; (2) 0-200; (3) 0-300; +c (4) 0-400; (5) 0-500. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real wfract_cov(numquad+1,numbin,numthresh) + real area_total_quad_bin(numquad,numbin) + real area_exceed_quad_bin(numquad,numbin,numthresh) + real xintlon,xintlat + real :: windthresh(numthresh) = (/17.5,25.74,32.94/) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,conv_ms_knots,vmagkts + real rads,ri,dell,vmag,xarea,grdintincr,xsfclon,xsfclat + real sum_exceed_area(numbin,numthresh) + real sum_total_area(numbin,numthresh) + integer pdf_ct_bin(16) + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igfwret,ipct,i,j,numinterp,ixoa,ixaa,iq,ib,it,ii + integer jlatfix,ilonfix,npts,ibeg,iend,jbeg,jend,ngridint,ni,nj + integer itret,igiret,idistbin,ipdfbin,pdf_ct_tot,maxstorm + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) + character got_pdf*6 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*5 :: cbin(5) = + & (/'0-100','0-200','0-300','0-400','0-500'/) + character*2 :: cthresh(3) = (/'34','50','64'/) +c + igfwret = 0 + conv_ms_knots = 1.9427 + rads = 500.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + + wfract_cov = 0.0 + area_total_quad_bin = 0.0 + area_exceed_quad_bin = 0.0 + sum_exceed_area = 0.0 + sum_total_area = 0.0 + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_fract_wind_cov from call to ' + print *,'!!! get_ij_bounds, stopping processing for storm' + print *,'!!! number ',ist + endif + + igfwret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_fract_wind_cov calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igfwret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + +c When evaluating the winds at a gridpoint, keep in mind that each +c gridpoint represents area around it. There are 2 special cases +c we need to watch out for. The first is for cases in which the +c area of a gridpoint straddles across a distance threshold, so +c that some of the gridpoint's area is in the "<200" bin, while +c some is in the "<100" bin. The other is for the case in which +c the area of a gridpoint straddles between 2 adjacent quadrants +c (e.g., a gridpoint exactly to the north of the center would have +c half its area in the NW quadrant and half in the NE quadrant). +c +c To properly "partition" and assign gridpoint areas, we need to +c interpolate the current grid down to a fine resolution. +c +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the guidelines that +c will be used, keeping in mind that we want the final grid spacing +c to be on the order of between 0.05 and 0.10 degree (finer than +c 0.05 deg is superfluous, and coarser than 0.10 deg is too coarse). +c +c Original grid size (deg) # of interps +c ------------------------- ------------ +c 0.8 <= g 4 +c 0.4 <= g < 0.8 3 +c 0.2 <= g < 0.4 2 +c 0.1 <= g < 0.2 1 +c g < 0.1 0 + + + if ((dx+dy)/2. >= 0.8) then + numinterp = 4 + else if ((dx+dy)/2. < 0.8 .and. (dx+dy)/2. >= 0.4) then + numinterp = 3 + else if ((dx+dy)/2. < 0.4 .and. (dx+dy)/2. >= 0.2) then + numinterp = 2 + else if ((dx+dy)/2. < 0.2 .and. (dx+dy)/2. >= 0.1) then + numinterp = 1 + else + numinterp = 0 + endif + + grdintincr = (dx+dy)/2. + do i = 1,numinterp + grdintincr = 0.5 * grdintincr + enddo + +c Now loop through the points in this subdomain, determine if any +c are within 500 km of the center, and then determine what quadrant +c the point is in relative to the center, and then calculate the +c fractional area coverage for winds. + + pdf_ct_tot = 0 + pdf_ct_bin = 0 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_fract_wind_cov, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_fract_wind_cov' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle iloop ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > (rads+(0.75*((dx+dy)/2.)*dtk*cos(glat(j)*dtr)))) + & then + + ! If the distance is greater than "rads" (500 km at initial + ! writing) plus another 3/4 of a gridpoint, then cycle. + ! The extra 3/4 of a gridpoint is to allow for the case of + ! some portion of the area around a gridpoint (whose + ! center point > 500 km) being within the 500 km arc... + ! although that is only factored in for grids with spacing + ! >= 0.1 deg. For smaller grids, where no interpolation is + ! done in this subroutine, then the distance to that point + ! is considered representative and the point is ignored if + ! it is not less than 500 km from the center. + + cycle iloop + + else + + ! First interpolate the area surrounding each grid point to + ! get fine resolution of lats & lons for determining how to + ! partition the area of a gridpoint among quadrants as well + ! as among distance thresholds. + + vmag = sqrt (u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + vmagkts = vmag * conv_ms_knots + + if (numinterp > 0) then + + grdintincr = ((dx+dy)/2.) / 2**numinterp ! "grid spacing" + ! of interpolated grid + ngridint = (2**numinterp) / 2 + + got_pdf = 'notyet' + + njloop: do nj= ngridint,-ngridint,-1 + + xintlat = glat(j) + float(nj) * grdintincr + + niloop: do ni= -ngridint,ngridint + + xintlon = glon(ii) + float(ni) * grdintincr + + call calcdist (xintlon,xintlat,xsfclon + & ,xsfclat,xdist,degrees) + + if (xdist <= 350. .and. got_pdf == 'notyet') then + ! The got_pdf flag is needed because in these loops + ! for niloop & njloop, we are actually looking at + ! tiny areas around the same grid point. So we + ! want to make sure we only count each gridpoint + ! once. + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + got_pdf = 'got_it' + endif + + if (xdist < 500.) then + + ! Compute area of this fraction of a grid box + xarea = (grdintincr * 111195) * + & (grdintincr * 111195 + & * cos(xintlat * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Go through a loop of the bins. The purpose of + ! this is that these "bins" all go from the + ! the center out to a specified radius, they are + ! NOT 100-km wide bins. So if we are dealing with + ! a point at r = 250 km, then that falls in the + ! 0-300 km bin, but it also falls in the 0-400 and + ! 0-500 km bins as well. So we need to run through + ! this binloop multiple times to get the area data + ! into multiple bins. Here are the bins & indices: + ! 1: 0-100 km + ! 2: 0-200 km + ! 3: 0-300 km + ! 4: 0-400 km + ! 5: 0-500 km + + binloop: do ib = idistbin,numbin + + if (xintlon >= xsfclon .and. + & xintlat >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (xintlon >= xsfclon .and. + & xintlat < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop + + endif + + enddo niloop + + enddo njloop + + else + + ! In this else statement is the case for a grid whose + ! resolution is already fine enough that we don't need + ! to interpolate any further. For example, we will have + ! the H*Wind data on a 0.05 degree grid, so that's already + ! fine enough. + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat + & ,xdist,degrees) + + if (xdist <= 350.) then + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + endif + + if (xdist < 500.) then + + ! Compute area of this grid box + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Why the binloop2? See explanation above in the "if" + ! part of this if-then block, where binloop is. + + binloop2: do ib = idistbin,numbin + + if (glon(ii) >= xsfclon .and. + & glat(j) >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (glon(ii) >= xsfclon .and. + & glat(j) < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop2 + + endif + + endif + + endif + + enddo iloop + + enddo jloop + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different quadrants, bins and thresholds... +c ------------------------------------------------- + + if ( verb .ge. 3 ) then + write (6,109) ' ' + & ,' ' + & ,' ' + write (6,109) ' Quadrant Bin Wind_Thresh ' + & ,'Fract_coverage (%) Area_exceeded' + & ,' Area_total' + write (6,109) ' -------- --- ----------- ' + & ,'------------------ -------------' + & ,' ----------' + write (6,109) ' ' + & ,' ' + & ,' ' + + do iq = 1,numquad + do ib = 1,numbin + do it = 1,numthresh + wfract_cov(iq,ib,it) = area_exceed_quad_bin(iq,ib,it) / + & area_total_quad_bin(iq,ib) + write (6,117) cquad(iq),cbin(ib),cthresh(it) + & ,wfract_cov(iq,ib,it)*100.0 + & ,area_exceed_quad_bin(iq,ib,it) + & ,area_total_quad_bin(iq,ib) + enddo + enddo + enddo + endif + + + 109 format (1x,a33,a37,a16) + 117 format (5x,a2,5x,a5,7x,a2,13x,f6.2,10x,f16.1,2x,f16.1) + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different bins and thresholds, but for the +c entire "disc" of the storm, that is, summing all +c quadrants together. +c ------------------------------------------------- + + do it = 1,numthresh + do ib = 1,numbin + do iq = 1,numquad + sum_total_area(ib,it) = sum_total_area(ib,it) + & + area_total_quad_bin(iq,ib) + sum_exceed_area(ib,it) = sum_exceed_area(ib,it) + & + area_exceed_quad_bin(iq,ib,it) + enddo + wfract_cov(5,ib,it) = sum_exceed_area(ib,it) + & / sum_total_area(ib,it) + enddo + enddo + + if ( verb .ge. 3 ) then + do ib = 1,numbin + do it = 1,numthresh + write (6,117) 'TT',cbin(ib),cthresh(it) + & ,wfract_cov(5,ib,it)*100.0 + & ,sum_exceed_area(ib,it) + & ,sum_total_area(ib,it) + enddo + enddo + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_ike_stats (imax,jmax,inp,dx,dy,ist,ifh + & ,fixlon,fixlat,xsfclon,xsfclat,valid_pt,calcparm + & ,ike,sdp,wdp,maxstorm,trkrinfo,igisret) +c +c ABSTRACT: This subroutine computes the Integrated Kinetic Energy +c (IKE) and Storm Surge Damage Potential (SDP) values, based on +c Powell (BAMS, 2007). At this time, we are only computing the IKE +c values for TS threshold (17.5 m/s) and above. We are not yet +c computing wind damage potential (WDP) since, per Mark Powell +c (4/2008), he is currently re-formulating an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c sdp Storm surge damage potential + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4 + integer npts,ipct,igisret,imax,jmax,ist,ifh,ilonfix,jlatfix + integer ibeg,jbeg,iend,jend,igiret,i,j,maxstorm,ii + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real ike(max_ike_cats) + real dx,dy,degrees,rads,ri,dell,xdist,vmag,xarea + real xsfclon,xsfclat,sdp,wdp + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) +c + igisret = 0 + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + rads = 400.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_ike_stats from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for storm ' + print *,'!!! number ',ist + endif + + igisret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_ike_stats calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igisret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + +c Search a grid of points near the storm center, evaluate if the +c storm is within the "rads" distance threshold. If so, compute +c the IKE values for all applicable thresholds (10, 18, 33 m/s). + + do j = jbeg,jend + do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ike_stats, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_ike_stats' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > rads) then + cycle + else + + vmag = sqrt(u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + + if (vmag > 10.0) then + ! Add gridpoint to IKE_10. Compute area first... + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + ike(1) = ike(1) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 18.0) then + ! Add gridpoint to IKE_ts. Area already computed for 10 + ike(2) = ike(2) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 33.0) then + ! Add gridpoint to IKE_h. Area already computed for 10 + ike(3) = ike(3) + (0.5 * (vmag**2) * xarea) + endif + + endif + + enddo + enddo + + ike(1) = ike(1) * 1.e-12 ! Convert from J to TJ + ike(2) = ike(2) * 1.e-12 ! Convert from J to TJ + ike(3) = ike(3) * 1.e-12 ! Convert from J to TJ + +c Compute the storm surge damage potential (sdp) + + if (ike(2) >= 0.0) then + sdp = 0.676 + (0.43 * sqrt(ike(2))) + & - (0.0176 * ((sqrt(ike(2)) - 6.5)**2) ) + else + sdp = -99.0 + endif + +c Print out the IKE and SDP statistics... + + if ( verb .ge. 3 ) then + print *,' IKE_10 (storm energy) = ',ike(1) + print *,' IKE_TS (tropical storm) = ',ike(2) + print *,' IKE_H (hurricane) = ',ike(3) + print *,' SDP = ',sdp + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine distbear (xlatin,xlonin,dist,bear,xlatt,xlont) +c +c ABSTRACT: Given an origin at latitude, longitude=xlato,xlono, +c this subroutine will locate a target point at a distance dist in +c km or nautical miles (depends on what you use for "rad_earth..." +c below), at bearing bear (degrees clockwise from north). +c Returns latitude xlatt and longitude xlont of target point. +c +c *** NOTE *** +c This subroutine was written to handle input lats & lons as this: +c All latitudes are in degrees, north positive and south negative. +c All longitudes are in degrees, west positive and east negative. +c *** **** *** +c +c However, for the longitudes, the rest of the tracker uses all +c 0-360 longitudes. Therefore, we need to convert the input lons +c and then once again convert the lons that are returned back to +c the calling routine. +c +c NOTE-- When origin is at north or south pole, bearing is no +c longer measured from north. Instead, bearing is measured +c clockwise from the longitude opposite that specified in xlono. +c Example-- if xlato=90., xlono=80., the opposite longitude is +c -100 (100 East), and a target at bearing 30. will lie on the +c -70. (70 East) meridian. +c +c AUTHOR: The core of this subroutine was written by Albion +c Taylor, another NOAA employee, in 1981. +c + USE trig_vals + + implicit none +c + real, parameter :: rad_earth_nm = 3440.170 ! radius of earth + real, parameter :: rad_earth_km = 6372.797 ! radius of earth + real xlato,xlono,dist,bear,xlatt,xlont,xlatin,xlonin + real cdist,sdist,clato,slato,clono,slono,cbear,sbear + real z,y,x,r,xlattz,xlontz,ddist,dbear,dxlato,dxlono +c + xlato = xlatin + xlono = xlonin + +cstr print *,' ' +cstr print *,'+++ At top of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlon= ',xlono,'E ',360.-xlono +cstr & ,'W xlat=',xlato +cstr print '(a6,f7.2,a8,f7.2)','dist= ',dist,' bear= ',bear + + if (xlono > 180.) then + ! Longitude input for this subroutine must be positive west + xlono = 360. - xlono + else + ! Longitude input for this subroutine must be negative east + xlono = -1. * xlono + endif + +cstr print '(a31,a8,f8.2)','After conversion for distbear, ' +cstr & ,' xlono= ',xlono + + ddist = dist + dbear = bear + dxlato = xlato + dxlono = xlono + + cdist = cos(ddist/rad_earth_km) + sdist = sin(ddist/rad_earth_km) + clato = cos(dtr*dxlato) + slato = sin(dtr*dxlato) + +cstr print *,'cdist= ',cdist,' sdist= ',sdist,' clato= ',clato +cstr & ,' slato= ',slato + + clono = cos(dtr*dxlono) + slono = sin(dtr*dxlono) + +cstr print *,'dxlono= ',dxlono,' clono= ',clono +cstr & ,' slono= ',slono + + cbear = cos(dtr*dbear) + sbear = sin(dtr*dbear) + +cstr print *,'cbear= ',cbear,' sbear= ',sbear + + z=cdist*slato + clato*sdist*cbear + y=clato*clono*cdist + sdist*(slono*sbear - slato*clono*cbear) + x=clato*slono*cdist - sdist*(clono*sbear + slato*slono*cbear) + +cstr print *,'z= ',z,' y= ',y,' x= ',x + + r = sqrt(x**2 + y**2) + +cstr print *,'r = sqrt(x**2 + y**2) = ',r + + xlattz = atan2(z,r)/dtr + +cstr print *,'xlattz = datan2(z,r)/dtr = ',xlattz + + xlatt = xlattz + + if (r <= 0.) go to 20 + + xlontz = atan2(x,y)/dtr + +cstr print *,'xlontz = atan2(x,y)/dtr = ',xlontz + +c xlont = xlontz + + ! Return the target longitude back to the calling routine + ! as a 0-360 positive east longitude.... + + xlont = mod(360.-xlontz,360.) + +c xlont = mod(360.+xlontz,360.) + +cstr print *,' ' +cstr print *,'At end of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlont= ',xlont,'E ' +cstr ,360.-xlont,'W xlatt=',xlatt + + return + 20 xlont=0. +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_uneven (targlat,targlon,dx,dy + & ,imax,jmax,trkrinfo,level,cparm,xintrp_val,ibiret) +c +c ABSTRACT: This subroutine performs a bilinear interpolation to get +c a data value at a given lat/lon that may be anywhere within a box +c defined by the four surrouding grid points. In the diagram below, +c remember that for our grids we are using in the tracker, the +c latitude index starts at the north pole and increases southward. +c The point "X" indicates the target lat/lon location of the value +c for which we are bilinearly interpolating. The values to and ta +c below are ratios that determine how geographically close the +c target location is to the point of origin (pt.1 (i,j)) in terms +c of both longitude (to) and latitude (ta). +c +c +c pt.1 pt.2 +c (i,j) (i+1,j) +c +c +c +c X +c +c pt.4 pt.3 +c (i,j+1) (i+1,j+1) +c + + USE grid_bounds; USE tracked_parms; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + character cparm*1 + real targlat,targlon,xintrp_val,dx,dy + real to,ta,d1,d2,d3,d4,z,eastlon + integer ie,iw,jn,js,ibiret,imax,jmax,level,nlev + + ibiret = 0 + +c -------------------------------------------------------------- +c For the latitudes and longitudes surrounding our target +c lat/lon location, convert the lat/lon values into i- and +c j-indices. +c -------------------------------------------------------------- + +c Find the j-indices for the points just to the north and the +c south of targlat.... + + if (targlat >= 0.0) then + ! For a northern hemisphere storm, jn is the j-index for the + ! point just to the *NORTH* (poleward) of targlat. + jn = int((glatmax - targlat)/dy + 1.) + js = jn + 1 + else + ! For a southern hemisphere storm, js is the j-index for the + ! point just to the *SOUTH* (poleward) of targlat. + js = ceiling((glatmax - targlat)/dy + 1.) + jn = js - 1 + endif + + ! Check to make sure that points are not being requested beyond + ! the northern or southern boundaries of the grid. This is most + ! likely to happen for a smaller, regional grid. + + if (jn > jmax .or. js > jmax) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jmax exceeded in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + + if (jn < 1 .or. js < 1) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jn < 0 or js < 0 in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + +c Find the i-indices for the points just to the east and the +c west of targlon.... + + ie = int((targlon - glonmin)/dx + 2.) + iw = ie - 1 + + ! Check for GM wrapping. Check ie to see if it is between the + ! most eastward gridpoint and the GM (i.e., on a 1-deg global + ! grid (360x181), it would be if targlon was between 359.0 (i=360) + ! and the GM (i=1, not i=361)). Similarly then, if we adjust ie + ! to then be 1, then we have a problem with iw, + ! since iw = 1 - 1 = 0. + + if (ie > imax) then + if (trkrinfo%gridtype == 'global') then + ie = ie - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ie > imax in subroutine ' + print *,'!!! bilin_int_uneven for a non-global grid. ' + print *,'!!! Returning to calling routine after ' + print *,'!!! assigning missing wind value of -99.' + print *,'!!! ie= ',ie,' imax= ',imax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + + if (iw < 1) then + if (trkrinfo%gridtype == 'global') then + iw = iw + imax + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: iw < 1 in subroutine bilin_int_uneven' + print *,'!!! for a non-global grid. Returning to calling ' + print *,'!!! routine after assigning missing wind value ' + print *,'!!! of -99. iw= ',iw + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' +++ Interpolating winds for cparm= ',cparm +ctmwc print '(6x,4(a4,i3))','jn= ',jn,' js= ',js,' iw= ',iw,' ie= ',ie +ctmwc endif + +c ---------------------------------------------------------------- +c Calculate the longitude (to) and latitude (ta) location ratios. +c Check for GM wrapping, as we can run into a problem here if +c interpolating for points that are just west of the GM, since we +c would be interpolating using values of longitude just west of +c GM (say, glon(iw)=359.5) and the GM (glon(ie) = 0.0). This +c makes for an incorrect "to" ratio below, with 0-359.5 in the +c denominator. We have to account for this.... +c ---------------------------------------------------------------- + + if (glon(iw) > 300.0 .and. + & (glon(ie) < 10. .and. glon(ie) >= 0.)) then + eastlon = 360. - glon(ie) + else + eastlon = glon(ie) + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,'glat(js)= ',glat(js),' glat(jn)= ',glat(jn) +ctmwc endif + + to = (targlon - glon(iw)) / (eastlon - glon(iw)) + ta = (targlat - glat(jn)) / (glat(js) - glat(jn)) + +c -------------------------------------------------------------- +c Copy the data values at the 4 known points into simple scalar +c variables +c -------------------------------------------------------------- + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cparm == 'u') then + d1 = u(iw,jn,nlev) + d2 = u(ie,jn,nlev) + d3 = u(ie,js,nlev) + d4 = u(iw,js,nlev) + else if (cparm == 'v') then + d1 = v(iw,jn,nlev) + d2 = v(ie,jn,nlev) + d3 = v(ie,js,nlev) + d4 = v(iw,js,nlev) + else if (cparm == 'm') then + d1 = lsmask(iw,jn) + d2 = lsmask(ie,jn) + d3 = lsmask(ie,js) + d4 = lsmask(iw,js) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in bilin_int_uneven.' + print *,'!!! Input cparm not recognized.' + print *,'!!! cparm= ',cparm + print *,'!!! EXITING....' + endif + + stop 95 + endif + + z = 1.9427 + +cstr print '(2x,4(a4,f8.2))',' d1= ',d1*z,' d2= ',d2*z +cstr & ,' d3= ',d3*z,' d4= ',d4*z + +c ------------------------------------------------------------- +c Compute the interpolated value +c ------------------------------------------------------------- + + xintrp_val = (1.-to) * (1.-ta) * d1 + & + to * (1.-ta) * d2 + & + to * ta * d3 + & + (1.-to) * ta * d4 + +cstr print '(2x,2(a11,f8.2))',' xintrp= ',xintrp_val,' (in kts)= ' +cstr & ,xintrp_val*z +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine sort_storms_by_pressure (gridprs,ifh,maxstorm,sortindex + & ,issret) +c +c ABSTRACT: This subroutine sorts storms by mslp. It is called by +c subroutine tracker just before the loop for "stormloop" is done +c for all the storms at a particular forecast hour. It is only +c called for the "midlat" and "tcgen" cases. The end result of +c this sort is an array (prsindex) that contains the indeces of +c the storms, arranged from lowest pressure to highest (and note +c that the "undefined" storms have a pressure of 9999.99 mb and +c thus get sorted to the bottom of the array). The purpose of +c doing this is so that we track the most intense storms first. +c Why go to the trouble? Imagine a scenario in which we are +c tracking a complex system in which there are 2 low pressure +c centers. Let's say that one is becoming dominant and +c intensifying, while the other is weakening. Now, let's assume +c that the weakening one eventually gets absorbed into the +c stronger, more dominant low. Now we only have 1 low, but if in +c the tracker stormloop, we first process the data for the +c weakening low, we will attribute the track to that storm, and +c then when we get to the point in the loop where we are trying +c to get the track for the stronger storm, we will (erroneously) +c stop the tracking for that storm since the storm center has +c already been attributed to the weaker storm. But by using this +c subroutine, we will track the stronger storm first, and thus +c avoid this problem. +c +c NOTE: The pressures used in the sort are those obtained at the +c previous forecast hour. At forecast hour = 0, just use the +c values as they were input to this routine, since they were +c found in first_ges_center from strongest to weakest already. +c +c INPUT: +c gridprs real array of storm mslp values +c ifh integer index for the current forecast hour +c maxstorm max num of storms that can be handled in this run +c +c OUTPUT: +c sortindex contains a sorted array of indeces. The orders +c sort routine does NOT rearrange the data. Rather, it +c returns this array of sorted indeces which point to +c the correct order of data values in the data array. +c issret return code from this subroutine +c + USE set_max_parms + USE verbose_output + + real, allocatable :: iwork(:) + real gridprs(maxstorm,maxtime) + integer ifh,maxstorm + integer sortindex(maxstorm) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: prstemp(:) +c + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iva /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub sort_storms_by_pressure allocating' + print *,'!!! prstemp or iwork arrays: ' + print *,'!!! iva= ',iva,' iwa= ',iwa + endif + + STOP 94 + return + endif + + if (ifh > 1) then + +c print *,' ' +c print *,'--- Before sort, original prs values follow:' +c print *,' ' + + do ist = 1,maxstorm + prstemp(ist) = gridprs(ist,ifh-1) +c write (6,81) ist,prstemp(ist)/100.0 + enddo + + imode = 2 + sortindex = 0 + call qsort (prstemp,sortindex,maxstorm) + +ccccc call orders (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) +ccccc call orders_4byte (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Pressure-sorted storm list:' + print *,' ' + + do ist = 1,maxstorm + if (prstemp(sortindex(ist))/100.0 < 9999.0) then + write (6,82) ist,sortindex(ist) + & ,prstemp(sortindex(ist))/100.0 + endif + enddo + + 81 format (1x,'ist= ',i5,' Original (unsorted) prstemp= ',f7.2) + 82 format (1x,'ist= ',i5,' sortindex(ist)= ',i5 + & ,' prstemp= ',f7.2) + endif + + else + do ist = 1,maxstorm + sortindex(ist) = ist + enddo + endif + + deallocate (prstemp); deallocate (iwork) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getvrvt (centlon,centlat,xlon,xlat + & ,udat,vdat,vr,vt,igvtret) +c +c ABSTRACT: This subroutine takes as input a u-wind and v-wind value +c at an input (xlon,xlat) location and returns the tangential and +c radial wind components relative to the input center lat/lon +c position (centlon,centlat). The only trick to this whole +c subroutine is figuring out the angle from the center point to the +c data point, and we do this by creating a triangle with the leg +c from the center point to the data point being the hypotenuse. +c +c NOTE: All longitudes must be in positive degrees east (0-360) !!! +c +c INPUT: +c centlon Longitude of center point +c centlat Latitude of center point +c xlon Longitude of pt at which vr & vt will be computed +c xlat Latitude of pt at which vr & vt will be computed +c udat u-value of wind at the point (xlon,xlat) +c vdat v-value of wind at the point (xlon,xlat) +c +c OUTPUT: +c vr Radial wind component at (xlon,xlat) wrt (centlon,centlat) +c vt Tang wind component at (xlon,xlat) wrt (centlon,centlat) +c igvtret Return code from this subroutine +c + USE trig_vals + USE verbose_output + + implicit none + + real centlon,centlat,xlon,xlat,udat,vdat,vr,vt,degrees,tmpxlon + real angle,xlondiff,xlatdiff,opp_dist,hyp_dist,sin_value + real cos_value,adj_dist,tmpangle,sin_angle,cos_angle + real uvrcomp,vvrcomp,uvtcomp,vvtcomp + integer igvtret +c + call calcdist(centlon,centlat,xlon,xlat,hyp_dist,degrees) + +c xxxx + + tmpxlon = xlon + + if (centlon > 330.0) then + + if (xlon > 360.0) then + + tmpxlon = xlon ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (xlon < 30.0) then + + tmpxlon = xlon + 360. ! In this case, the fix center is just + ! to the west of the GM with a lon (centlon) + ! > 330, while the point being evaluated + ! (xlon) is just east of the GM, but with a + ! lon (centlon) < 30. Need to adjust here to + ! to get the xlon in the 330+ frame of + ! reference. + + endif + + elseif (centlon >= 0 .and. centlon < 30.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = 360. - xlon + + endif + + elseif (centlon < 0.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = -1 * (360. - xlon) + + endif + + endif + + xlatdiff = abs(centlat - xlat) + xlondiff = abs(centlon - tmpxlon) + + if (centlon > 355.0) then + write (6,91) centlon,tmpxlon,hyp_dist,degrees,xlondiff + 91 format (1x,'centlon= ',f8.3,' tmpxlon= ',f8.3,' hyp_dist= ' + & ,f10.2,' degrees= ',f10.2,' xlondiff= ',f12.2) + endif + + if (xlondiff == 0 .and. xlatdiff > 0) then + + if (centlat > xlat) angle = 180 ! pt directly south of ctr + if (centlat < xlat) angle = 0 ! pt directly north of ctr + + else if (xlondiff > 0 .and. xlatdiff == 0) then + + if (centlon > tmpxlon) angle = 270 ! pt directly west of ctr + if (centlon < tmpxlon) angle = 90 ! pt directly east of ctr + + else + + ! This next part figures out the angle from the center point + ! (centlon,centlat) to the data point (tmpxlon,xlat). It does + ! this by setting up a triangle and then using inverse trig + ! functions to get the angle. Since this is a kludgy way to + ! do it that doesn't account for the curvature of the earth, + ! we'll do it 2 ways, using asin and then acos, then take the + ! average of those 2 for the angle. hyp_dist, calculated just + ! above, is the distance from the center pt to the data pt. + + opp_dist = xlatdiff/360. * ecircum + sin_value = opp_dist / hyp_dist + if (sin_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, sin_value > 1, setting to 1.' + print *,'!!! opp_dist= ',opp_dist,' hyp_dist= ',hyp_dist + print *,'!!! sin_value = ',sin_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + sin_value = 0.99999 + endif + sin_angle = asin(sin_value) / dtr + + call calcdist(centlon,centlat,tmpxlon,centlat,adj_dist,degrees) + cos_value = adj_dist / hyp_dist + if (cos_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, cos_value > 1, setting to 1.' + print *,'!!! adj_dist= ',adj_dist,' hyp_dist= ',hyp_dist + print *,'!!! cos_value = ',cos_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + cos_value = 0.99999 + endif + cos_angle = acos(cos_value) / dtr + + tmpangle = 0.5 * (sin_angle + cos_angle) + + ! The previous lines of code just calculated an angle between + ! 0 and 90. This next if structure adjusts that angle to + ! instead be between 0 and 360. + + if (centlat <= xlat .and. centlon <= tmpxlon) then + angle = 90 - tmpangle + else if (centlat > xlat .and. centlon <= tmpxlon) then + angle = 90 + tmpangle + else if (centlat >= xlat .and. centlon >= tmpxlon) then + angle = 270 - tmpangle + else if (centlat < xlat .and. centlon >= tmpxlon) then + angle = 270 + tmpangle + endif + + endif + + uvrcomp = udat * sin(angle * dtr) + vvrcomp = vdat * cos(angle * dtr) + vr = uvrcomp + vvrcomp + + uvtcomp = (-udat) * cos(angle * dtr) + vvtcomp = vdat * sin(angle * dtr) + vt = uvtcomp + vvtcomp + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcfunix (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in the new ATCF UNIX format. +c Unlike the old atcf DOS format in which you waited until the +c whole tracking was over to write the output for all forecast +c hours, with this atcfunix format, each time we are calling this +c subroutine, it is to only write out 1 record, which will be the +c fix info for a particular storm at a given time. Also, even +c though we have some data (GFS, NAM) at 6-hour intervals, Jim +c Gross informed me that TPC does not need the positions at such +c frequency, and keeping the reporting at 12 hour intervals is fine. +c +c While this new atcfunix format contains much more information than +c the old 1-line atcf dos message, for our purposes we will use the +c slots for mslp and wind radii. An example set of output records +c will look like the following: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c plastbar pressure of the outermost closed isobar +c rlastbar radius (nm) of the outermost closed isobar +c rmax radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c cps_vals real array with the values for the 3 cyclone phase +c space parameters: (1) is for Parameter B (thermal +c asymmetry); (2) is for lower level (600-900 mb) thermal +c wind; (3) is for upper level (300-600 mb) thermal wind. +c wcore_flag character for value of 300-500 mb warm core: y, n, or +c 'u' for undetermined. +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE phase + USE verbose_output + + type (datecard) inp + type (trackstuff) trkrinfo + + real cps_vals(3) + real outlon,outlat,rmax,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,irmax,output_fhr,ic,iplastbar,irlastbar + integer vradius(3,4),icps_vals(3) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + character comma_fill1*48,comma_fill2*31,comma_filler*79 + + if ( verb .ge. 3 ) then + print *,'TTT top of atcfunix, ist= ',ist,' ifh= ',ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcfunix. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'in output_atcfunix, tcv_storm_id= ' + & ,storm(ist)%tcv_storm_id + print *,'in output_atcfunix, tcv_storm_id(3:3)= ' + & ,storm(ist)%tcv_storm_id(3:3) + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' +!zhang case ('A','a'); basinid = 'NA' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + if (trkrinfo%want_oci) then + if (plastbar > 0.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -99 + endif + if (rlastbar > 0.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -99 + endif + else + iplastbar = -99 + irlastbar = -99 + endif + + if ( verb .ge. 3 ) then + print *, 'output: rlastbar=',rlastbar,' irlastbar=',irlastbar + print *, 'output: plastbar=',plastbar,' iplastbar=',iplastbar + endif + +c Now convert all of the cyclone phase space parameter values from +c real to integer. + + do ic = 1,3 + if (cps_vals(ic) > -9999.0) then + if (cps_vals(ic) >= 0.0) then + icps_vals(ic) = int(cps_vals(ic)*10. + 0.5) + else + icps_vals(ic) = int(cps_vals(ic)*10. - 0.5) + endif + else + icps_vals(ic) = -9999 + endif + enddo + + if (wcore_flag == 'y'.or. wcore_flag == 'Y') then + wcore_flag = 'Y' + elseif (wcore_flag == 'n' .or. wcore_flag == 'N') then + wcore_flag = 'N' + elseif (wcore_flag == 'u' .or. wcore_flag == 'U') then + wcore_flag = 'U' + else + wcore_flag = 'U' + endif + + comma_fill1 = ', 0, 0, , 0, , 0, 0, ,' + comma_fill2 = ' , , , 0, 0, 0, 0' + comma_filler = comma_fill1//comma_fill2 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + else + + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,'rmax= ',rmax,' irmax= ',irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,a79,', THERMO PARAMS' + & ,3(', ',i7),', ',a1,', ',i2,', DT, -999') + 91 format (a2,', ',a4,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,2(', ',i3),', ',a3) + +c bug fix for IBM: flush the output stream so it actually writes + flush(64) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each +c storm. This message contains the model identifier, the forecast +c initial date, and the positions for 0, 12, 24, 36, 48, 60 and 72 +c hours. In the case of the regional models (NGM, Eta), which +c only go out to 48h, zeroes are included for forecast hours +c 60 and 72. +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by Steve Lord for plotting purposes, and his +c plotting routines need the longitudes in 0 - 360, increasing +c westward. Thus, a necessary adjustment is made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + print *,'top of output_all' + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + intlon(ifh) = 0 + intlat(ifh) = 0 + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + endif + + enddo ifhloop + + print *,'before select case, atcfname= ' + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(5),intlon(5),intlat(9),intlon(9),intlat(13) + & ,intlon(13),intlat(17),intlon(17),intlat(21),intlon(21) + & ,0,0,storm(ist)%tcv_storm_id + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(3),intlon(3),intlat(5),intlon(5),intlat(7) + & ,intlon(7),intlat(9),intlon(9),intlat(11),intlon(11) + & ,intlat(13),intlon(13),storm(ist)%tcv_storm_id + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3),intlat(4) + & ,intlon(4),intlat(5),intlon(5),intlat(6),intlon(6) + & ,intlat(7),intlon(7),storm(ist)%tcv_storm_id + + case ('GDA','HDA') ! GDAS, HDAS + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),0,0,0,0,0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case default +c print *,'!!! ERROR in subroutine output_all. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + print *,'!!! Model name is not identified: ',atcfname + + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + 81 format (i2,a4,4i2.2,14i4,1x,a3) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm + & ,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each storm +c in ATCF format. This message contains the model identifier, the +c forecast initial date, and the positions for 12, 24, 36, 48 +c and 72 hours. This message also contains the intensity +c estimates (in knots) for those same hours. The conversion for +c m/s to knots is to multiply m/s by 1.9427 (3.281 ft/m, +c 1 naut mile/6080 ft, 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by the atcf system at TPC for plotting +c purposes, and the atcf plotting routines need the longitudes in +c 0 - 360, increasing westward. Thus, a necessary adjustment is +c made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real xmaxwind(maxstorm,maxtime) + real conv_ms_knots + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4,basinid*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + conv_ms_knots = 1.9427 + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + + intlon(ifh) = 0 + intlat(ifh) = 0 + + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + + endif + + enddo ifhloop + + basinid = ' ' + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid(1:2) = 'AL' + case ('E','e'); basinid(1:2) = 'EP' + case ('C','c'); basinid(1:2) = 'CP' + case ('W','w'); basinid(1:2) = 'WP' + case ('O','o'); basinid(1:2) = 'SC' + case ('T','t'); basinid(1:2) = 'EC' + case ('U','u'); basinid(1:2) = 'AU' + case ('P','p'); basinid(1:2) = 'SP' + case ('S','s'); basinid(1:2) = 'SI' + case ('B','b'); basinid(1:2) = 'BB' +cPENG case ('A','a'); basinid(1:2) = 'NA' + case ('A','a'); basinid(1:2) = 'AA' + case default; basinid(1:2) = '**' + end select + basinid(3:4) = storm(ist)%tcv_storm_id(1:2) + + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(5),intlon(5) + & ,intlat(9),intlon(9),intlat(13),intlon(13),intlat(17) + & ,intlon(17),0,0 + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,17)*conv_ms_knots) + 0.5) + & ,0 + & ,basinid,inp%byy + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble, ECMWF hi-res + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4),intlat(5) + & ,intlon(5),intlat(7),intlon(7) + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('GDA','HDA') ! GDAS, HDAS + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4) + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,0,0,0,0 + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case default +c print *,'!!! ERROR in subroutine output_atcf. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + end select + + enddo stormloop + + 82 format (i2,a4,4i2.2,10i4,5i3,1x,a4,i2.2) +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_hfip (outlon,outlat,inp,ist + & ,ifh,vmaxwind,xminmslp,vradius,rmax,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified ATCF UNIX format. +c The modification is to allow for sub-hourly output. That is, +c instead of just integer output hours, we can have output at +c 10, 15 or 20 past an hour. This necessitates a change in the +c "forecast hour" placeholder in the ATCF format. Instead of it +c being an I3, we'll make it an I5, with something like a lead time +c of 36.25h being rounded and truncated to 03625 for output. +c +c An example set of output records using the standard atcf format +c looks like the following: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c An example set of modified output records will look like the +c following, for the case of a lead time of 36:15 (36.25): +c +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifh index for the lead time array +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c rmax Radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms + USE verbose_output + + type (datecard) inp + + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,rmax + integer intlon,intlat,output_fhr,irmax,ileadtime + integer vradius(3,4) + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_hfip. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + ! ST: ifcsthour does not exist, so output_fhr is always + ! filled with invalid data here. However, output_fhr is + ! never used, so it is safe to remove. + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + ! output_fhr = ifcsthour + 3 + ileadtime = nint((fhreal(ifh) + 3.0) * 100.0) + else + ! output_fhr = ifcsthour + ileadtime = nint(fhreal(ifh) * 100.0) + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4),irmax + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4),irmax + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4),irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i5.5,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', 0, 0, ',i3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(69) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_fract_wind (outlon,outlat,xsfclon,xsfclat + & ,inp,ist,ifcsthour,vmaxwind,xminmslp,wfract_cov + & ,wfract_type,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values for the fractional areal coverage of various wind +c thresholds. In addition, this subroutine also writes out +c records to a file containing data on the PDF of wind magnitudes +c within r=350 km. +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with areal coverage thresholds. +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, NEE, 981, 857, 629, 810 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, NEE, 874, 732, 319, 610 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, NEE, 454, 327, 99, 270 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, AAE, 721, 721, 721, 721 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, AAE, 465, 465, 465, 465 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, AAE, 298, 298, 298, 298 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the pctgs for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind pctg info; all the +c other info is identical for each entry. +c +c Listed after the "XX" in each record is the radius from which +c the coverage is valid (000 km in this case); Next is the radius +c at which the coverage stops (100 km in this case). Next is the +c wind threshold (34, 50, 64). Next is an identifier for which +c quadrant the coverage starts in (first 2 characters are NE, SE, +c SW, NW); the last character indicates if the coverages are +c computed in the quadrants as earth-relative ("E") or +c storm-motion relative ("R"). The ones listed there as "AAE" +c are for the full disc (i.e., 4-quadrant average), earth-relative. +c Next are the wind coverage percentages, listed as percentage * 10 +c (e.g., 981 = 98.1%). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c wfract_cov percent areal coverage for various wind thresholds +c wfract_type 'earth' or 'storm' relative analysis +c pdf_ct_bin array for pdf of wind magnitudes within r=350 km +c pdf_ct_tot total count of pdf points for r < 350 km +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,pdfval + real wfract_cov(numquad+1,numbin,numthresh) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer :: windthresh(numthresh) = (/34,50,64/) + integer pdf_ct_bin(16) + integer intlon,intlat,output_fhr,intlon100,intlat100,pdf_ct_tot + integer maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (wfract_type == 'earth') then + wt = 'E' + else if (wfract_type == 'storm') then + wt = 'R' + else + wt = 'X' + endif + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'NE',wt + & ,int((1000.*wfract_cov(1,ib,it))+0.5) + & ,int((1000.*wfract_cov(2,ib,it))+0.5) + & ,int((1000.*wfract_cov(3,ib,it))+0.5) + & ,int((1000.*wfract_cov(4,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'AA',wt + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a6,i3.3,', ',i3.3,', ' + & ,i3,', ',a2,a1,4(', ',i4),', ',i4,a1,', ',i5,a1) + +c -------------------------------------------------- +c Now compute and write out the pdf values for the +c wind magnitude.... +c -------------------------------------------------- + + do ip = 1,16 + pdfval = float(pdf_ct_bin(ip)) / float(pdf_ct_tot) + write (76,85) atcfymdh,basinid,storm(ist)%tcv_storm_id(1:2) + & ,output_fhr,10*(ip-1),10*ip,pdf_ct_bin(ip) + & ,pdf_ct_tot,pdfval + enddo + + 85 format (1x,i10.10,3x,a2,a2,3x,i3,3x,i3.3,'_',i3.3,3x,i7,2x,i7 + & ,2x,f6.3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(73) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_wind_structure (outlon,outlat,xsfclon + & ,xsfclat,inp,ist,ifcsthour,vmaxwind,xminmslp,er_wind + & ,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values of the winds at specified distances along 45-degree +c radials in each storm quadrant. These are output +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 radials: NE, SE, +c SW and NW (45,135,225,315). For the storm-relative estimates, +c these radials will be computed at the same relative angles (i.e., +c 45,135,225,315), but with respect (positive clockwise) to the +c direction of storm motion. For example, for a storm moving with +c a heading of 280, the wind structure is evaluated at these +c radials: 325 (front-right; 45 deg CW from heading), 55 (back- +c right; 135 deg CW from heading), 145 (back-left; 225 deg CW from +c heading), 235 (front-left; 315 deg CW from heading). +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with wind values at the 13 specified distances +c (10, 25, 50, 75, 100, 150, 200, 250, 300, 350, 400, 450, 500 km) +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NEE, 1137, 1221, 854, 655, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SEE, 947, 982, 474, 396, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SWE, 645, 683, 328, 277, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NWE, 725, 753, 619, 429, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FRR, 1134, 1224, 852, 654, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BRR, 944, 984, 472, 393, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BLR, 649, 686, 321, 272, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FLR, 729, 756, 613, 421, etc., ... out to 500 km +c +c NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text. +c NOTE: These winds are in m/s coming into this routine and will +c be converted to knots*10 for output (e.g., 1221 = 122.1 kts) +c +c The "71" ID indicates earth-relative winds, the "72" ID indicates +c storm-relative winds. Here are the other IDs that will be used: +c 81: Tangential winds, earth-relative (m/s) +c 82: Tangential winds, storm-relative (m/s) +c 91: Radial winds, earth-relative (m/s) +c 92: Radial winds, storm-relative (m/s) +c +c Note that in this example, for this 36h forecast hour, there are +c 8 entries. This is so that we can include the wind values for +c the 4 different quadrants, for both the earth relative analyses +c (NEE, SEE, SWE, NWE) and the storm-relative analyses (FRR, BRR, +c BLR, FLR). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + integer ioutwind(numdist) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real outlon,outlat + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,id,intlon100,intlat100,ir + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*2 :: crel(4) = (/'FR','BR','BL','FL'/) + + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + +c Total wind (converted to knots*10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 71, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Total wind (converted to knots*10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 72, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 81, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 82, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 92, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a10,a2,a1,14(', ',i4) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(72) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_ike (outlon,outlat,xsfclon,xsfclat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,ike,sdp,wdp,maxstorm + & ,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the Integrated Kinetic Energy (IKE) and Storm Surge Damage +c Potential (SDP), based on Powell (BAMS, 2007). At this time, we +c are only computing the IKE values for TS threshold (17.5 m/s) and +c above. We are not yet computing wind damage potential (WDP) +c since, per Mark Powell (4/2008), he is currently re-formulating +c an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with WDP, SDP and IKE values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, 340, 560, 212, 174, 42, 93, 12, 0 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, WDP, SDP, I10, ITS, IH ,I2540,I4154, I55 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Values for WDP and SDP are multiplied by 10 in this routine +c before being written out. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c ike integrated kinetic energy, in units of TJ +c sdp storm surge damage potential +c wdp wind damage potential +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,sdp,wdp + real ike(max_ike_cats) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,intlon100,intlat100,maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (74,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, IKE',int((wdp*10)+0.5),int((sdp*10)+0.5) + & ,int(ike(1)+0.5),int(ike(2)+0.5),int(ike(3)+0.5) + & ,int(ike(4)+0.5),int(ike(5)+0.5),int(ike(6)+0.5) + & ,intlat100,clatns,intlon100,clonew +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a14,8(',',i5) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(74) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_phase (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,paramb,vtl_slope + & ,vtu_slope,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the three parameters that comprise Bob Hart's cyclone phase +c space (CPS). These parameters are his "parameter B", which +c assesses the left-right thermal asymmetry, and the upper +c troposphere (300-600 mb) and lower troposphere (900-600 mb) +c thermal wind values. +c +c LOCAL: +c +c Arrays: +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with paramb, vtl_slope and vtu_slope values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, 340, 560, 212 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, B, VTL, VTU +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c paramb thermal asymmetry +c vtl_slope thermal wind value for lower troposphere (900-600 mb) +c vtu_slope thermal wind value for upper troposphere (600-300 mb) +c +c OUTPUT: +c ioiret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + real outlon,outlat,paramb,vtl_slope,vtu_slope + real vmaxwind,conv_ms_knots,xminmslp + integer intlon,intlat,output_fhr + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (71,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 95, CPS',int(paramb+0.5),int(vtl_slope+0.5) + & ,int(vtu_slope+0.5) +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a14,3(',',i6)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(71) + + return + end +c +c----------------------------------------------------------------------- + subroutine output_atcf_gen1 (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different. Here's an example of the +c TPC standard atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c be the one that is pulled from the tcvitals or gen_vitals +c record: +c +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 50, NEQ, 155, 000, 000, 000 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 64, NEQ, 000, 000, 000, 000 +c +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd storm translation speed +c istmdir direction of storm movement +c plastbar pressure of last closed isobar +c rlastbar radius of last closed isobar +c rmax radius of max winds +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals; USE level_parms + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real outlon,outlat,plastbar,rlastbar,rmax + real vmaxwind,conv_ms_knots,xminmslp,mslp_outp_adj + real cps_vals(3) + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar,irmax + integer ivtl,ivtu,iparamb,output_fhr + integer vradius(3,4) + integer imeanzeta(nlevgrzeta),igridzeta(nlevgrzeta) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + + if ( verb .ge. 3) then + print *,'+++ Top of output_atcf_gen, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (rmax > -90.0) then + irmax = int(rmax + 0.5) + else + irmax = -99 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(66) + + return + end +c +c----------------------------------------------------------------------- +cPENG---2018-06-07------------------------------------------------- +c----------------------------------------------------------------------- +c subroutine output_atcf_gen (outlon,outlat,inp,ist +c & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm +c & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax +c & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + subroutine output_atcf_gen (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,wcore_mean_val,wcore_point_max + & ,imeanzeta,igridzeta + & ,imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + & ,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different. Here's an example of the +c TPC standard atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c be the one that is pulled from the tcvitals or gen_vitals +c record: +c +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 50, NEQ, 155, 000, 000, 000 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 64, NEQ, 000, 000, 000, 000 +c +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd storm translation speed +c istmdir direction of storm movement +c plastbar pressure of last closed isobar +c rlastbar radius of last closed isobar +c rmax radius of max winds +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real outlon,outlat,plastbar,rlastbar,rmax + real vmaxwind,conv_ms_knots,xminmslp + real cps_vals(3) + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar,irmax + integer ivtl,ivtu,iparamb,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + +cJ.Peng----2014-10-01----------------------------- + real wcore_mean_val,wcore_point_max + real imeantemp,igridtemp + & ,imeanushe,igridushe + & ,imeanrhum,igridrhum + + if ( verb .ge. 3) then + print *,'+++ Top of output_atcf_gen, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + + if (xminmslp == 999999.0) xminmslp = 0.0 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/100. + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (rmax > -90.0) then + irmax = int(rmax + 0.5) + else + irmax = -99 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- +c & ,imeanzeta(1),abs(igridzeta(1)),imeanzeta(2),abs(igridzeta(2)) + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100. + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd +c & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) +cJ.Peng----2010-10-01----------------------------------------------- +c & ,imeanzeta(1),abs(igridzeta(1)),imeanzeta(2),abs(igridzeta(2)) + & ,abs(imeanzeta(1)),abs(igridzeta(1)) + & ,abs(imeanzeta(2)),abs(igridzeta(2)) + & ,imeantemp,igridtemp,imeanushe,igridushe + & ,imeanrhum,igridrhum,wcore_mean_val,wcore_point_max + + endif + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4)) +cJ.Peng----2010-10-01----------------------------------------------- + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4) + & ,8(', ',f8.1)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(66) + + return + end +c +c----------------------------------------------------------------------- + subroutine output_atcf_sink (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta,igridzeta + & ,cps_vals,plastbar,rlastbar,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The "sink" in the subroutine name indicates that this output +c contains the whole kitchen sink of forecast storm info. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different, and the part after the radii +c data is different. Here's an example of the TPC standard +c atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c indicate the lat/lon at which the storm was *first* found in +c the model. The position may be either found within this run +c of the tracker, or that position may have been pulled from the +c tcvitals or gen_vitals record: +c +c 2000092500_F000_206N_0623W_13L, 2000092500, 03, GFSO, 036 +c , 243N, 675W, 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c , PLAS, RLAS, RMX, DIR, SPD, B, VTU, VTL +c , Z8MN, Z8MX, Z7MN, Z7MX +c +c As noted above, there is extra info at the end, after the +c "34, NEQ, 242, 163, 124, 208" radii info. Here is a key +c to indicate what these items are: +c +c PLAS: Pressure (mb) of last closed isobar +c RLAS: Radius of the last closed isobar in nm, 0 - 9999 nm. +c RMX: Radius of max winds, 0 - 999 nm. +c DIR: Direction of storm motion. +c SPD: Speed of storm motion (m/s * 10). +c B: Hart's CPS "Parameter B" thickness asymmetry value (m). +c VTL: Hart's CPS thermal wind (Lower, 900-600) value. +c VTU: Hart's CPS thermal wind (Upper, 600-300) value. +c Z8MN: Mean value of 850 mb zeta surrounding storm. +c Z8MX: Max value of 850 mb zeta near storm. +c Z7MN: Mean value of 700 mb zeta surrounding storm. +c Z7MX: Max value of 700 mb zeta near storm. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd speed of storm translation +c istmdir direction of storm motion +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c plastbar pressure of last closed isobar (pa) +c rlastbar radius of last closed isobar (nm) +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real cps_vals(3) + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar + integer iparamb,ivtl,ivtu,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1 + + if ( verb .ge. 3 ) then + print *,'+++ Top of output_atcf_sink, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4)) + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',2(i4,', '),4(i3,', '),2(i5,', '),4(i4,', '),a9) + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',2(i4,', '),i3,', ',2(i4,', '),3(i6,', '),4(i6,', ') + & ,a9) + +c write (68,87) gstm%gv_gen_date,gstm%gv_gen_lat +c & ,gstm%gv_gen_latns,gstm%gv_gen_lon +c & ,gstm%gv_gen_lonew,gstm%gv_gen_type +c & ,inp%bcc,inp%byy,inp%bmm,inp%bdd,inp%bhh +c & ,adjustr(atcfname),ifcsthour,intlat,clatns,intlon,clonew +c & ,int((vmaxwind*conv_ms_knots) + 0.5) +c & ,int(xminmslp/100.0 + 0.5) +c & ,'XX, 34, NEQ' +c & ,istmspd,istmdir,imeanzeta(1),igridzeta(1) +c & ,imeanzeta(2),igridzeta(2) +c +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,6(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(68) + + return + end + +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_tcvitals (xlon,xlat,inp,ist,iovret) +c +c ABSTRACT: This subroutine outputs a tcvitals record. The +c lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE inparms; USE set_max_parms + USE verbose_output + + type (tcvcard) stm + type (datecard) inp + real xlon,xlat +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "storm" +c components for this storm, then we will change the specific +c components that we need to. + + stm = storm(ist) + + stm%tcv_center = 'AEAR' + + stm%tcv_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + stm%tcv_latns = 'S' + else + stm%tcv_latns = 'N' + endif + + if (xlon >= 180.) then + stm%tcv_lon = 3600 - int(xlon * 10. + 0.5) + stm%tcv_lonew = 'W' + else + stm%tcv_lon = int(xlon * 10. + 0.5) + stm%tcv_lonew = 'E' + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) stm + endif + + write (65,21) stm + + 21 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + +c +c bug fix for IBM: flush the output stream so it actually writes + flush(65) + + return + end + +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_gen_vitals (xlon,xlat,inp,ist,istmspd,istmdir + & ,iovret) +c +c ABSTRACT: This subroutine outputs a modified vitals record. +c The lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c The storm identifier is different than that for a standard +c tcvitals. The storm identifier contains the date/time that +c the storm was first identified, and the lat/lon position at +c which it was first identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE gen_vitals; USE inparms; USE set_max_parms + USE verbose_output + + implicit none + + type (gencard) gstm + type (datecard) inp + real xlon,xlat + integer ist,iovret,istmspd,istmdir +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the vitals record. + + if (gstm%gv_gen_date /= 99999) then + + if (gstm%gv_gen_type /= 'FOF') then + ! If this is not a 'FOF' storm (found on the fly storm), then + ! it must be a TC vitals storm, or a tropical cyclone, and we + ! don't want to create a vitals record for a tropical cyclone, + ! since we will rely on reading them from the TC Vitals + ! database instead. + return + endif + + else + + ! This storm is new in this forecast/analysis and was found on + ! the fly in the first time level for this run and there was no + ! previous vitals record for this system + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = 0 + + gstm%gv_gen_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_gen_latns = 'S' + else + gstm%gv_gen_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_gen_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'W' + else + gstm%gv_gen_lon = int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'E' + endif + + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + gstm%gv_obs_ymd = inp%bcc * 1000000 + & + inp%byy * 10000 + & + inp%bmm * 100 + & + inp%bdd + + gstm%gv_obs_hhmm = inp%bhh * 100 + + gstm%gv_obs_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_obs_latns = 'S' + else + gstm%gv_obs_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_obs_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'W' + else + gstm%gv_obs_lon = int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'E' + endif + + gstm%gv_stdir = istmdir + gstm%gv_stspd = istmspd + + gstm%gv_depth = 'U' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) gstm + endif + + write (67,21) gstm + + 21 format (i10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1,'_',a3,1x,i8,1x + & ,i4.4,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x + & ,i3,4(1x,i4),1x,a1) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(67) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_tracker_mask (masked_outc,lb,ifh,ifcsthour + & ,imax,jmax,iotmret) +c +c ABSTRACT: This subroutine outputs a GRIB record that contains the +c "mask" used to mask out areas surrounding low pressure centers +c that have been found during the search at each forecast hour. This +c mask is written out purely for diagnostic purposes. The GRIB +c identifier given to the mask in the pds is 850 mb height (you can +c make it anything you want). This is only done for the "midlat" +c and "tcgen" cases, since the runs for those cases use a mask while +c the regular "tracker" run (that is, the run which strictly tracks +c only those storms in the TC vitals file) does not. +c +c INPUT: +c masked_outc logical array containing mask +c ifh integer counter for current forecast hour +c ifcsthour integer current forecast hour +c imax num points is i-direction of input grid +c jmax num points is j-direction of input grid +c +c OUTPUT: +c iotmret return code from this subroutine + + implicit none +c + integer ifh,imax,jmax,iotmret,kf,igoret,iix,jjx,ipret + integer ifcsthour + integer kpds(200),kgds(200) + logical(1) masked_outc(imax,jmax),lb(imax,jmax) + real xmask(imax,jmax) +c + if (ifh == 1) then + call baopenw (77,"fort.77",igoret) + print *,'baopenw: igoret= ',igoret + + if (igoret /= 0) then + print *,' ' + print *,'!!! ERROR in sub output_tracker_mask opening' + print *,'!!! **OUTPUT** grib files. baopenw return codes:' + print *,'!!! grib file 1 return code = igoret = ',igoret + STOP 95 + return + endif + endif + + xmask = 0.0 + do jjx = 1,jmax + do iix = 1,imax + if (masked_outc(iix,jjx)) then + xmask(iix,jjx) = 1.0 + else + xmask(iix,jjx) = 0.0 + endif + enddo + enddo + + kf = imax * jmax + +c kpds(5) = 7 +c kpds(6) = 100 +c kpds(7) = 850 +c kpds(22) = 0 + + kpds(1) = 7 ; kpds(2) = 80 + kpds(3) = 255 ; kpds(4) = 192 + kpds(5) = 7 ; kpds(6) = 100 + kpds(7) = 850 ; kpds(8) = 99 + kpds(9) = 7 ; kpds(10) = 20 + kpds(11) = 12 ; kpds(12) = 0 + kpds(13) = 1 ; kpds(14) = ifcsthour + kpds(15) = 0 ; kpds(16) = 10 + kpds(17) = 0 ; kpds(18) = 1 + kpds(19) = 2 ; kpds(20) = 0 + kpds(21) = 20 ; kpds(22) = 0 + kpds(23) = 0 ; kpds(24) = 0 + kpds(25) = 0 + kgds(1) = 0 ; kgds(2) = imax + kgds(3) = jmax ; kgds(4) = -90000 + kgds(5) = 0 ; kgds(6) = 128 + kgds(7) = 90000 ; kgds(8) = 359750 + kgds(9) = 250 ; kgds(10) = 250 + kgds(11) = 64 ; kgds(12) = 0 + kgds(13) = 0 ; kgds(14) = 0 + kgds(15) = 0 ; kgds(16) = 0 + kgds(17) = 0 ; kgds(18) = 0 + kgds(19) = 0 ; kgds(20) = 255 + + write(*,980) kpds(1),kpds(2) + write(*,981) kpds(3),kpds(4) + write(*,982) kpds(5),kpds(6) + write(*,983) kpds(7),kpds(8) + write(*,984) kpds(9),kpds(10) + write(*,985) kpds(11),kpds(12) + write(*,986) kpds(13),kpds(14) + write(*,987) kpds(15),kpds(16) + write(*,988) kpds(17),kpds(18) + write(*,989) kpds(19),kpds(20) + write(*,990) kpds(21),kpds(22) + write(*,991) kpds(23),kpds(24) + write(*,992) kpds(25) + write(*,880) kgds(1),kgds(2) + write(*,881) kgds(3),kgds(4) + write(*,882) kgds(5),kgds(6) + write(*,883) kgds(7),kgds(8) + write(*,884) kgds(9),kgds(10) + write(*,885) kgds(11),kgds(12) + write(*,886) kgds(13),kgds(14) + write(*,887) kgds(15),kgds(16) + write(*,888) kgds(17),kgds(18) + write(*,889) kgds(19),kgds(20) + write(*,890) kgds(21),kgds(22) +c + 980 format('tmow kpds(1) = ',i7,' kpds(2) = ',i7) + 981 format('tmow kpds(3) = ',i7,' kpds(4) = ',i7) + 982 format('tmow kpds(5) = ',i7,' kpds(6) = ',i7) + 983 format('tmow kpds(7) = ',i7,' kpds(8) = ',i7) + 984 format('tmow kpds(9) = ',i7,' kpds(10) = ',i7) + 985 format('tmow kpds(11) = ',i7,' kpds(12) = ',i7) + 986 format('tmow kpds(13) = ',i7,' kpds(14) = ',i7) + 987 format('tmow kpds(15) = ',i7,' kpds(16) = ',i7) + 988 format('tmow kpds(17) = ',i7,' kpds(18) = ',i7) + 989 format('tmow kpds(19) = ',i7,' kpds(20) = ',i7) + 990 format('tmow kpds(21) = ',i7,' kpds(22) = ',i7) + 991 format('tmow kpds(23) = ',i7,' kpds(24) = ',i7) + 992 format('tmow kpds(25) = ',i7) + 880 format('tmow kgds(1) = ',i7,' kgds(2) = ',i7) + 881 format('tmow kgds(3) = ',i7,' kgds(4) = ',i7) + 882 format('tmow kgds(5) = ',i7,' kgds(6) = ',i7) + 883 format('tmow kgds(7) = ',i7,' kgds(8) = ',i7) + 884 format('tmow kgds(9) = ',i7,' kgds(10) = ',i7) + 885 format('tmow kgds(11) = ',i7,' kgds(12) = ',i7) + 886 format('tmow kgds(13) = ',i7,' kgds(14) = ',i7) + 887 format('tmow kgds(15) = ',i7,' kgds(16) = ',i7) + 888 format('tmow kgds(17) = ',i7,' kgds(18) = ',i7) + 889 format('tmow kgds(19) = ',i7,' kgds(20) = ',i7) + 890 format('tmow kgds(20) = ',i7,' kgds(22) = ',i7) +c + print *,'just before call to putgb, kf= ',kf + call putgb (77,kf,kpds,kgds,lb,xmask,ipret) + print *,'just after call to putgb, kf= ',kf + if (ipret == 0) then + print *,' ' + print *,'+++ IPRET = 0 after call to putgb' + print *,' ' + else + print *,' ' + print *,'!!!!!! ERROR: IPRET NE 0 AFTER CALL TO PUTGB !!!' + print *,' ' + endif +c +c bug fix for IBM: flush the output stream so it actually writes + flush(6) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_next_ges (fixlon,fixlat,ist,ifh,imax,jmax + & ,dx,dy,modelid,valid_pt,readflag,maxstorm,istmspd + & ,istmdir,ctype,trkrinfo,ignret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. It does this by using two different +c methods and averaging the results from those two. The +c first method is a simple linear extrapolation made by +c basically drawing a line from the previous position +c through the current fix position and assuming straight +c line motion. The second method is to do a barnes +c smoothing of u & v in the vicinity of the storm at 850, +c 700 & 500 mb to get an average environmental wind +c vector at each level, and then move the storm according +c to the vector at each level. Then a weighted average is +c taken of all these positions from methods 1 & 2 to get +c the consensus for the guess position. NOTE: For a +c regional model and a storm that is relatively close to +c the model boundary, there is a strong possibility that +c the barnes analysis subroutine will fail due to trying +c to access grid points beyond the model's lateral +c boundary. In this case, the redlm & ridlm are halved +c and barnes is called again. If it still fails, then +c just use the result from method 1 as a default. +c +c INPUT: +c fixlon Array with longitudes of fix positions +c fixlat Array with latitudes of fix positions +c ist Storm number currently being processed +c ifh Forecast hour currently being processed +c imax Max number of pts in x-direction for this grid +c jmax Max number of pts in y-direction for this grid +c dx grid-spacing of the model in the i-direction +c dy grid-spacing of the model in the j-direction +c modelid Integer indicating what model's data is being processed +c valid_pt Logical; bitmap indicating if valid data at that pt. +c readflag Logical; Tells whether or not a variable was read in +c for this model +c maxstorm Max # of storms that can be handled in this run +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, eventually +c in the barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c istmspd The speed that the storm would have to move to get from +c the current position to the next guess position +c istmdir The direction in which the storm would have to move to +c get from the current position to the next guess position +c +c LOCAL: +c dt Number of seconds between successive forecast times +c for this particular model. +c dtkm Distance in meters of 1 degree latitude +c icutmax Max number of times to cut the ridlm and redlm in half, +c for use in calling barnes. If you're using a regional +c model and on the first call to barnes you try to access +c a point that's outside the model grid boundary, we'll +c call barnes again, but not before cutting the redlm and +c ridlm in half. icutmax says how many times to allow +c this cutting in half before giving up and just going +c with the extrapolation method. At first writing, we'll +c set icutmax to 2, so that it will allow the ridlm to +c get down to 500 km (originally 2000 km) and the redlm +c to 125 km (originally 500 km). +c *** NOTE: After testing the system, it was found that if +c we cut these radii, the u and v values that are +c calculated from barnes are too strongly influenced by +c the near-storm environment and, especially for asymmetric +c systems, resulted in u and v values being much too strong. +c As such, we will not allow these values to be cut, and if +c we hit the boundaries in barnes, we'll just use the +c extrapolation method, which has seemed to work just fine. +c +c OTHER: (slonfg, slatfg & storm defined in module def_vitals) +c slonfg Array containing first guess longitude positions +c slatfg Array containing first guess latitude positions +c storm Contains tcvitals information +c + USE radii; USE def_vitals; USE set_max_parms; USE grid_bounds + USE tracked_parms; USE level_parms; USE trig_vals; USE trkrparms + USE gen_vitals + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer icutmax,istmspd,istmdir,bskip,ileadtime,ifcsthour + integer ifh,ist,npts,ilonfix,jlatfix,ibeg,jbeg,iend,jend + integer igiret,ignret,icut,iuret,ivret,ibarnct,n,ix1,ix2 + integer icount,imax,jmax,modelid,maxstorm + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real barneswt,extrapwt,dtkm,dt,ucomp,vcomp,xdist,ydist,ydeg + real extraplat,avglat,cosfac,xdeg,extraplon,ylatdegmove_last + real xlondegmove_last,xnumh_last,ylatdegmove_last_perhour + real xlondegmove_last_perhour,xnumh_next,yoldavglat + real yoldcosfac,xdistmove_last,xdistmove_last_perhour + real ynewavglat,ynewcosfac,xdegnew,dx,dy,re,ri,ubar,vbar + real wgttot,uavg,vavg,reold,riold,barnlat,barnlon,wt_total + real tmp_fix_lon_curr,tmp_fix_lon_prev + character*1 :: in_grid, extrap_flag, barnes_flag + character(*) ctype +c logical(1) valid_pt(imax,jmax),readflag(14) +cPENG----2018-06-07 ------------------------ + logical(1) valid_pt(imax,jmax),readflag(19) +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c For updating the first guess, if Method 1 and Method 2 are both +c able to be done, give the following weights to the 2 methods. +c + data barneswt /0.50/, extrapwt /0.50/ +c +c ------------------------------- +c METHOD 1: LINEAR EXTRAPOLATION +c ------------------------------- +c First, just do a simple linear extrapolation from the previous +c fix position through the current fix position. If it's the +c first time (vt=0), then use the storm motion vector and storm +c speed information from the TC Vitals card. +c + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(ist)%tcv_stdir == -99 .or. + & storm(ist)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GET_NEXT_GES, at fcst hour = 0, either ' + print *,'!!! storm motion or storm speed = -99 on TCV card.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(ist)%tcv_stdir + print *,'!!! storm motion speed= ',storm(ist)%tcv_stspd + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + extrap_flag = 'n' + else + ucomp = sin(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + vcomp = cos(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(ist,ifh) + ydeg + avglat = 0.5 * (extraplat + fixlat(ist,ifh)) + if (avglat > 89.5) avglat = 89.0 + if (avglat < -89.5) avglat = -89.0 + cosfac = cos(avglat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(ist,ifh) + xdeg + endif + else + +c Do a simple linear extrapolation of the current motion of the +c storm. Follow a line from the fix position from the last fix +c through the current fix and extrapolate out. To figure out the +c new latitude, just see how many deg lat the storm moved since +c last time and add it to the current fix latitude. To calculate +c the new fix longitude, though, we need to see how many deg lon +c the storm moved since the last time, convert that to the +c distance (km) the storm travelled in the x-direction (at an +c average latitude between the current and previous latitudes), +c and then add that distance on to the current longitude and +c convert that distance to the num of degrees the storm has +c travelled in the x-direction (at an average latitude between +c the current and next(extrap) latitudes). +c +c UPDATE Feb 2009: To account for the possibility of using +c irregularly spaced forecast hours (e.g., 6,10,10.5,...etc), +c I had to modify this linear extrapolation. + + print *,' ' + print *,'xxxx get_next_ges, prev fix lon= ',fixlon(ist,ifh-1) + print *,'xxxx get_next_ges, curr fix lon= ',fixlon(ist,ifh) + print *,' ' + + if (fixlat(ist,ifh-1) > -900.0 .and. + & fixlon(ist,ifh-1) > -900.0) then + + ylatdegmove_last = fixlat(ist,ifh) - fixlat(ist,ifh-1) + + tmp_fix_lon_curr = fixlon(ist,ifh) + tmp_fix_lon_prev = fixlon(ist,ifh-1) + + if (tmp_fix_lon_prev < 0.0 .and. tmp_fix_lon_prev > -25.0) + & then + ! previous lon position is within 25 deg west of the GM + ! and is listed in negative degrees. + if (tmp_fix_lon_curr < 0.0 .and. tmp_fix_lon_curr > -25.0) + & then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 1 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both negative. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr < 25.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 2 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous time' + print *,' is negative, while lon for current time' + print *,' is positive, but within 0-25 deg East.' + print *,' All ok!' + endif + endif + + elseif (tmp_fix_lon_prev > 335.0 .and. + & tmp_fix_lon_prev <= 360.0) then + ! previous lon position is within 25 deg west of the GM + ! and is listed in positive degrees. + if (tmp_fix_lon_curr > 335.0 .and. + & tmp_fix_lon_curr <= 360.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 3 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both positive. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr <= 25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 4 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between 0 & 25. Current tmp_lon' + print *,' has been adjusted to be > 360 for the' + print *,' purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < 0.0 .and. + & tmp_fix_lon_curr >= -25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 5 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is west of the GM and' + print *,' is between 0 & -25. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < -335.0 .and. + & tmp_fix_lon_curr >= -360.0) then + tmp_fix_lon_curr = 720.0 - tmp_fix_lon_curr + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 6 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between -335 & -360. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + + endif + + endif + + xlondegmove_last = tmp_fix_lon_curr - tmp_fix_lon_prev + + xnumh_last = fhreal(ifh) - fhreal(ifh-1) + + ylatdegmove_last_perhour = ylatdegmove_last / xnumh_last + xlondegmove_last_perhour = xlondegmove_last / xnumh_last + + xnumh_next = fhreal(ifh+1) - fhreal(ifh) + + extraplat = fixlat(ist,ifh) + & + (ylatdegmove_last_perhour * xnumh_next) + + yoldavglat = 0.5 * (fixlat(ist,ifh) + fixlat(ist,ifh-1)) + yoldcosfac = cos (dtr * yoldavglat) + xdistmove_last = xlondegmove_last * dtk * yoldcosfac + + xdistmove_last_perhour = xdistmove_last / xnumh_last + + ynewavglat = 0.5 * (extraplat + fixlat(ist,ifh)) + ynewcosfac = cos(dtr * ynewavglat) + xdegnew = (xdistmove_last_perhour * xnumh_next) + & / (dtk * ynewcosfac) + extraplon = tmp_fix_lon_curr + xdegnew + + else + + if ( verb .ge. 3 ) then + print *,' ' + write(6,92) '!!! IN GET_NEXT_GES, at fcst hour = ' + & ,ifhours(ifh),ifclockmins(ifh) + print *,'!!! the lon and lat positions for the previous' + print *,'!!! forecast hour are -999, meaning that this is a' + print *,'!!! new storm, so we cannot use the extrap method.' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + 92 format (1x,a36,i4,':',i2.2) + + extrap_flag = 'n' + + endif + + endif + +c ------------------------------- +c METHOD 2: Barnes analysis +c ------------------------------- +c Do a barnes analysis on the u & v components of the wind near the +c storm to get an average u & v, then advect the storm according to +c the average wind vector obtained. The call to get_ij_bounds is +c needed in order to restrict the number of grid points that are +c searched in the barnes subroutine. See Abstract from this +c subroutine for further details. + + npts = ceiling(ridlm/(dtk*((dx+dy)/2))) + + call get_ij_bounds (npts,0,ridlm,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for ' + print *,'!!! storm number ',ist + endif + + ignret = 92 + return + endif + + if (verb >= 3) then + print *,' ' + print *,' +++ In get_next_ges after call to get_ij_bounds,' + print *,' getting bounds for the barnes analysis...' + print *,' glatmax= ',glatmax,' glatmin= ',glatmin + print *,' glonmax= ',glonmax,' glonmin= ',glonmin + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + print *,' ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + endif + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if ((dx+dy)/2 > 0.20) then + bskip = 1 + else if ((dx+dy)/2 > 0.10 .and. (dx+dy)/2 <= 0.20) then + bskip = 2 + else if ((dx+dy)/2 > 0.05 .and. (dx+dy)/2 <= 0.10) then + bskip = 3 + else if ((dx+dy)/2 > 0.03 .and. (dx+dy)/2 <= 0.05) then + bskip = 5 + else if ((dx+dy)/2 <= 0.03) then + bskip = 10 + endif + +c Calculate average wind at each level (currently: 850, 700 & 500) + + re = redlm + ri = ridlm + icut = 0 + + if (trkrinfo%type == 'midlat') then + icutmax = 2 + else + icutmax = 1 + endif + + radmaxloop: do while (icut <= icutmax .and. in_grid == 'n') + + ubar = 0.0; vbar = 0.0 + iuret = 0; ivret = 0 + wgttot = 0.0 + ibarnct = 0 + barnes_flag = 'n' + + levelloop: do n=1,nlevg + + select case (n) + case (1); ix1=3; ix2=4 ! For 850 mb readflags + case (2); ix1=5; ix2=6 ! For 700 mb readflags + case (3); ix1=12; ix2=13 ! For 500 mb readflags + end select + + if (readflag(ix1) .and. readflag(ix2)) then + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,u(1,1,n),valid_pt + & ,bskip,re,ri,uavg,icount,ctype,trkrinfo,iuret) + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,v(1,1,n),valid_pt + & ,bskip,re,ri,vavg,icount,ctype,trkrinfo,ivret) + + if (iuret /= 0 .or. ivret /= 0) then + +c ...barnes probably tried to access a pt outside the grid +c domain. So, reduce by half the distance from the center +c of the farthest pt that barnes tries to access, exit this +c loop, and try it again with the smaller re and ri. + + iuret = 96; ivret = 96 + reold = re + riold = ri + re = 0.5 * re + ri = 0.5 * ri + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: While attempting to use the barnes ' + print *,'method to update the first guess, the ' + print *,'algorithm tried to access a grid point that ' + print *,'does not have valid data, meaning that too ' + print *,'large a radius is being searched. So, the 2 ' + print *,'radii, re and ri, are being halved and, if the' + print *,'value of icutmax > 0, the algorithm will be ' + print *,'run again. Otherwise, if icutmax = 0, only ' + print *,'the extrapolation method will be used.' + print *,'iuret= ',iuret,' ivret= ',ivret,' icut= ',icut + print *,'Old re = ',reold,' New re = ',re + print *,'Old ri = ',riold,' New ri = ',ri + endif + + exit levelloop + + else + ubar = ubar + wgts(n) * uavg + vbar = vbar + wgts(n) * vavg + wgttot = wgttot + wgts(n) + ibarnct = ibarnct + 1 + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, ix1= ',ix1,' ix2= ',ix2 + print *,' uavg= ',uavg,' vavg= ',vavg + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' n= ',n,' wgts(n)= ',wgts(n),' wgttot= ' + & ,wgttot + print *,' ibarnct= ',ibarnct + print *,' ' + print *,' ' + endif + endif + + endif + + enddo levelloop + + if (ibarnct > 0 .and. wgttot > 0.0) then + barnes_flag = 'y' + in_grid = 'y' + ubar = ubar / wgttot + vbar = vbar / wgttot + barnlat = fixlat(ist,ifh) + (vbar * dt)/dtkm + cosfac = cos (dtr * 0.5 * (fixlat(ist,ifh) + barnlat)) + barnlon = fixlon(ist,ifh) + (ubar * dt)/(dtkm * cosfac) + + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, mean stats follow: ' + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' wgttot= ',wgttot + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' barnlon= ',barnlon,' barnlat= ',barnlat + print *,' dt= ',dt,' dtkm= ',dtkm,' cosfac= ',cosfac + endif + + +c This next if statement says that if we've had to reduce the +c size of the barnes analysis domain twice already, then we've +c only done the analysis on a much smaller area, and this +c doesn't give us as good a picture of the average winds in the +c area of the storm, so reduce the emphasis we place on the +c barnes method. + + if (icut >= 2) barneswt = barneswt / 2. + + else + barnes_flag = 'n' + endif + + icut = icut + 1 + + enddo radmaxloop + +c --------------------- +c Average the results +c --------------------- +c Now do a weighted average of the positions obtained from the +c linear extrapolation and the barnes analysis methods. + + if (extrap_flag == 'y' .and. barnes_flag == 'y') then + wt_total = barneswt + extrapwt + slatfg(ist,ifh+1) = (barneswt * barnlat + extrapwt * extraplat) + & / wt_total + + ! Note that in any of these statements just below, in order for + ! any of these to be > 360, the original fixlon must be close + ! to 360, i.e., in the far eastern part of the grid, as opposed + ! to being in the far western part (e.g., 0-2 deg East or so). + ! Conversely, for any of these to be < 0, the original fixlon + ! must be close to 0, i.e., in the far *western* part of the + ! grid. + +c yyyy + + if (fixlon(ist,ifh) > 330.0) then + + ! In this part of the IF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being 330+, to be consistent with the fixlon for + ! this time. + + if (extraplon > 330. .and. barnlon > 330.) then + + continue ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (extraplon > 330. .and. + & (barnlon >= 0.0 .and. barnlon < 30.)) then + + ! extraplon > 330, but barnlon is in the 0-30 range, so + ! we need to convert the barnlon value to be 360+ + + barnlon = barnlon + 360. + + elseif (extraplon > 330. .and. barnlon < 0.) then + + ! extraplon > 330, but barnlon is < 0, so + ! we need to convert the barnlon value to be positive... + + barnlon = barnlon + 360. + + elseif (barnlon > 330. .and. + & (extraplon >= 0.0 .and. extraplon < 30.)) then + + ! barnlon > 330, but extraplon is in the 0-30 range, so + ! we need to convert the extraplon value to be 360+ + + extraplon = extraplon + 360. + + elseif (barnlon > 330. .and. extraplon < 0.) then + + ! barnlon > 330, but extraplon is < 0, so + ! we need to convert the extraplon value to be positive... + + extraplon = extraplon + 360. + + endif + + elseif (fixlon(ist,ifh) >= 0. and. fixlon(ist,ifh) < 30.0) then + + ! In this part of the ELSEIF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being in the reference of >360 since that is what the + ! code below this is expecting with the computation of + ! slonfg for the next lead time. + + if ((extraplon >= 0. .and. extraplon < 60.) .and. + & (barnlon >= 0. .and. barnlon < 60.)) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon < 0. .and. extraplon > -60.) .and. + & (barnlon < 0. .and. barnlon > -60.)) then + + ! convert extraplon and barnlon to be positive + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon < 0.) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon < 0.) then + + extraplon = extraplon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon > 330.) then + + barnlon = barnlon + 360. + + elseif (barnlon >= 330. .and. extraplon < 60.) then + + extraplon = extraplon + 360. + + elseif (extraplon >= 330. .and. barnlon < 60.) then + + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon > 330.) then + + extraplon = extraplon + 360. + + endif + + else + + continue ! extraplon and barnlon do not need to be modified + ! since there should be no way that a storm + ! currently east of 30E and west of 30W could make + ! it to the Greenwich Mer in one forecast interval + + endif + + print *,' ' + print *,'+++ In get_next_ges, before averaging the 2 methods, ' + print *,' Raw (no conversion for GM wrap) barnlon= ' + & ,barnlon + print *,' Raw (no conversion for GM wrap) extraplon= ' + & ,extraplon + + slonfg(ist,ifh+1) = (barneswt * barnlon + extrapwt * extraplon) + & / wt_total + + if (slonfg(ist,ifh+1) > 360.) then + ! If we've GM-wrapped past 360, adjust it to be 0-360... + slonfg(ist,ifh+1) = slonfg(ist,ifh+1) - 360. + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'y' .and. barnes_flag == 'n') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_next_ges, barnes method was not ' + print *,'!!! done for updating the first guess for this ' + print *,'!!! storm. Only the linear extrapolation method ' + print *,'!!! was used.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + slatfg(ist,ifh+1) = extraplat + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 0.0,0.0,0.0 + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'n' .and. barnes_flag == 'y') then + slatfg(ist,ifh+1) = barnlat + if (barnlon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (barnlon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = barnlon + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 360.-barnlon,barnlat + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges, new position guess not' + print *,'!!! made. Could not get guess using either barnes' + print *,'!!! method or extrapolation method.' + print *,'!!! extrap_flag = ',extrap_flag + print *,'!!! barnes_flag = ',barnes_flag + print *,'!!! Storm number = ',ist,' ifh = ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + write (6,41) 0.0,0.0,0.0 + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 95 + endif + + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| Current fix & updated fix positions |' + print *,'-------------------------------------------------- ' + print *,'| In get_next_ges, current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',ist + print *,'| Return code from get_next_ges = ',ignret + print *,'| Storm Name = ',storm(ist)%tcv_storm_name + print *,'| Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + write (6,21) fixlat(ist,ifh) + write (6,23) 360.-fixlon(ist,ifh),fixlon(ist,ifh) + write (6,25) slatfg(ist,ifh+1) + write (6,27) 360.-slonfg(ist,ifh+1),slonfg(ist,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current fix lat is ',f7.2) + 23 format (' | Current fix lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') +c 41 format (' --- barnlon= ',f7.2,'W barnlat= ',f7.2) +c 43 format (' --- extraplon= ',f7.2,'W extraplat= ',f7.2) + + 41 format (' --- barnlon= ',f7.2,'E (',f7.2 + & ,'W) barnlat= ',f7.2) + 43 format (' --- extraplon= ',f7.2,'E (',f7.2 + & ,'W) extraplat= ',f7.2) + +c Now calculate the speed that the storm would have to move at in +c order to make it to the next forecast position. We will use +c this information in writing out the "gen_vitals" record, if this +c is requested. + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh) + & ,slonfg(ist,ifh+1),slatfg(ist,ifh+1),dist,degrees) + + ! convert distance from km to meters, then get speed in m/s. + + distm = dist * 1000. + stmspd = distm / dt + istmspd = int ((stmspd * 10) + 0.5) + + xincr = slonfg(ist,ifh+1) - fixlon(ist,ifh) + yincr = slatfg(ist,ifh+1) - fixlat(ist,ifh) + + if ( verb .ge. 3 ) then + print *,'iocheck, dist= ',dist,' distm= ',distm + print *,'iocheck, stmspd= ',stmspd,' istmspd= ',istmspd + print *,'iocheck, xincr= ',xincr,' yincr= ',yincr + endif + + if (xincr < 0.0 .and. slonfg(ist,ifh+1) < 30.0 .and. + & fixlon(ist,ifh) > 300.0) then + ! This means we have a storm moving east across the GM, and + ! so we are subtracting, for example, something like + ! 0.5 - 359.5, so redo xincr, but add 360 to slonfg first... + xincr = (slonfg(ist,ifh+1) + 360.0) - fixlon(ist,ifh) + else if (xincr > 300.0) then + ! This means we have a storm moving west across the GM, and + ! so we are subtracting, for example, something like + ! 359.5 - 0.5, so redo xincr, but add 360 to fixlon first... + xincr = slonfg(ist,ifh+1) - (fixlon(ist,ifh) + 360.0) + endif + + if (xincr == 0.0) then + if (yincr == 0.0) then + stmdir = 0.0 + else if (yincr > 0) then + stmdir = 360.0 + else if (yincr < 0) then + stmdir = 180.0 + endif + else if (xincr > 0.0) then + if (yincr == 0.0) then + stmdir = 90.0 + else + arct = atan(yincr/xincr) + stmdir = 90. - arct / dtr + endif + else if (xincr < 0.0) then + if (yincr == 0.0) then + stmdir = 270.0 + else + arct = atan(yincr/xincr) + stmdir = 270. - arct / dtr + endif + endif + + istmdir = int (stmdir + 0.5) + if (istmdir > 360) then + istmdir = 360 + else if (istmdir < 0) then + istmdir = 0 + endif + + if ( verb .ge. 3 ) then + print *,'iocheck, stmdir= ',stmdir,' istmdir= ',istmdir + endif + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine advect_tcvitals_from_hour0 (fixlon,fixlat,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. As of 11/2016, it is called only for the case in +c which we've got NetCDF data and no hour0 data, and so we want to +c simply take the TC Vitals data and advect the current position to +c a position at the next lead time. We can't use the code in +c subroutine get_next_ges because there are certain allocatable +c arrays in that subroutine that need to have been allocated first, +c and at this point prior to the first lead time in hour0, they +c haven't been allocated. +c +c INPUT: +c inctcv Index for storm number currently being processed +c ifh Forecast hour currently being processed +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c iatret Return code from this subroutine +c + USE def_vitals; USE trkrparms; USE tracked_parms + USE verbose_output; USE trig_vals; USE set_max_parms + USE gen_vitals + + type (trackstuff) trkrinfo + integer iatret,inctcv + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real ucomp,vcomp,xdist,ydist,ydeg,dt,extraplat + real cosfac + real dtkm +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c ------------------------------------------------------------------ +c Using the storm motion vector and storm translation speed as read +c from the TC Vitals card, do a simple linear extrapolation from the +c current observed (TC Vitals) position and advect the storm to a +c position at the next lead time. +c ------------------------------------------------------------------ + + iatret = 0 + + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(inctcv)%tcv_stdir == -99 .or. + & storm(inctcv)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In advect_tcvitals_from_hour0, at fcst hour= 0' + print *,'!!! either storm motion or storm speed = -99 on ' + print *,'!!! TCV card, ist= inctcv= ',inctcv,' ifh= ',ifh + print *,'!!! Storm name = ',storm(inctcv)%tcv_storm_name + print *,'!!! Storm ID = ',storm(inctcv)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(inctcv)%tcv_stdir + print *,'!!! storm motion speed= ',storm(inctcv)%tcv_stspd + print *,'... CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS ...' + print *,' ' + print *,'... Instead, we will simply use the current ' + print *,'... observed position from TC Vitals and hope that' + print *,'... it is close enough at the next lead time for ' + print *,'... the tracker to be able to still track it.' + print *,' ' + endif + extraplat = slatfg(inctcv,ifh) + extraplon = slonfg(inctcv,ifh) + else + ucomp = sin(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + vcomp = cos(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(inctcv,ifh) + ydeg + cosfac = cos(extraplat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(inctcv,ifh) + xdeg + endif + else + print *,' ' + print *,'!!! ERROR: In advect_tcvitals_from_hour0, the value of' + print *,' ifh is > 1, and this routine should only be called' + print *,' if ifh=1 (i.e., for hour0). STOPPING....' + print *,' ' + stop 95 + endif + + slatfg(inctcv,ifh+1) = extraplat + + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon >360 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon < 0 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + else + slonfg(inctcv,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| In advect_tcvitals_from_hour0, info on the ' + print *,'| positions for the current and next lead times ' + print *,'| follow: ' + print *,'-------------------------------------------------- ' + print *,'| current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',inctcv + print *,'| Return code from advect_tcvitals_from_hour0= ',iatret + print *,'| Storm Name = ',storm(inctcv)%tcv_storm_name + print *,'| Storm ID = ',storm(inctcv)%tcv_storm_id + write (6,420) gstorm(inctcv)%gv_gen_date + & ,gstorm(inctcv)%gv_gen_fhr + & ,gstorm(inctcv)%gv_gen_lat + & ,gstorm(inctcv)%gv_gen_latns,gstorm(inctcv)%gv_gen_lon + & ,gstorm(inctcv)%gv_gen_lonew,gstorm(inctcv)%gv_gen_type + write (6,21) fixlat(inctcv,ifh) + write (6,23) 360.-fixlon(inctcv,ifh),fixlon(inctcv,ifh) + write (6,25) slatfg(inctcv,ifh+1) + write (6,27) 360.-slonfg(inctcv,ifh+1),slonfg(inctcv,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current TC Vitals lat is ',f7.2) + 23 format (' | Current TC Vitals lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') + + + return + end +c +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getradii (xcenlon,xcenlat,imax,jmax,dx,dy,valid_pt + & ,cstormid,ifcsthr,vmaxwind,vradius,trkrinfo + & ,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) +c +c ABSTRACT: This subroutine looks through the wind data near an +c input storm center (fixlon,fixlat) and gets the radii of various +c surface winds in each of the 4 storm quadrants (NE,NW,SE,SW). +c The wind thresholds that are sought are gale force (34kt|17.5m/s), +c storm force (50kt|25.7m/s), and hurricane force (64kt|32.9m/s). +c This subroutine calls the Cray subroutine orders, which is a +c Cray-optimized sort routine. +c +c UPDATE (AUG 2001): The Cray subroutine orders was ported to the +c SP by NCEP personnel. On the SP version, some changes were +c apparently made so that the size of the arrays for calling +c arguments 2, 3 and 4 (iwork, dtemp and isortix in my calling +c routine) must be the same. This was not the case on the Crays, +c and this was causing the tracker to crash for cases far north +c on fine grids (GFDL 1/3 grid). +c +c UPDATE (AUG 2012): The call to the Cray subroutine orders was +c replaced with a call to qsort, which uses a quicksort sorting +c algorithm. While this is not the fastest sorting routine out +c there, we don't do a lot of sorting here, and qsort is simple +c and it is portable. +c +c UPDATE (April 2013): For the radii, we encountered a problem with +c radmax being too small. It was set at 650 km. Hurricane Sandy +c exceeded this in the models, so the values returned from getradii +c were close to the default radmax value of 650 km (350 nm), instead +c of much higher as they should have been. To fix it, we now use an +c iterative technique, where we start with radmax as a small value +c (450 km). If getradii returns a value for R34 in a quadrant that +c does not exceed 0.97*radmax, then that value is ok. If it does +c exceed 0.97*radmax, then we bump up radmax by 50 km and call +c getradii again, looking to diagnose radii only in those quadrants +c where the need_to_expand_r34 flag = 'n'. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c cstormid 3-character storm ATCF ID (e.g., 03L, 11E, etc) +c ifcsthr integer value for current forecast hour +c trkrinfo derived type containing various info on the storm +c need_to_expand_r34 1-character array that specifies which of the +c 4 quadrants still need to be expanded on this time +c through getradii in order to get an R34 value that is +c not right at the outermost boundary. +c vmaxwind max wind (in m/s) that was reported from the +c get_max_wind subroutine +c radmax input max radius (km) that will be used for this +c iteration of getradii. +c first_time_thru_getradii logical flag. It is used so that any +c checking for 50- or 64-kt radii is only done on the +c first time through getradii. Only the checking for +c 34-kt radii is done on multiple iterations. +c igrct integer that indicates what iteration of getradii this +c call is. +c +c OUTPUT: +c +c igrret return code from this subroutine +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c +c LOCAL: +c +c radmax the maximum radius to look for winds for the various +c thresholds. +c quadinfo This array contains the magnitude of the near-surface +c winds and the distance from the gridpoint to the fix +c position for each point in each quadrant that is within +c the maximum allowed radius, radmax. quadinfo is +c allocated within this subroutine, and is allocated as +c (quadrant, num_pts_in_quadrant, data_type), where +c data_type is either windspeed(1) or distance(2) from +c storm center to grid point. +c quadmax This array contains the max surface wind in each +c quadrant, plus the location of it and the distance from +c the storm center. This information is critical to +c identifying when this subroutine is malfunctioning. + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE level_parms + USE trkrparms + USE verbose_output + +c + type (trackstuff) trkrinfo +c + logical(1) valid_pt(imax,jmax) + logical(1) first_time_thru_getradii +c dimension iwork(257) + real, allocatable :: quadinfo(:,:,:),iwork(:) + real quadmax(4,4) + real exactdistnm,exactdistkm,radmax,degrees,cosarg + real rlonb,rlonc,rlatb,rlatc,vmaxwind + real pt_heading_rad,pt_heading,d + integer, allocatable :: isortix(:) + integer iwindix,ipoint,ifcsthr,igrct + integer quadct(4),vradius(3,4) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: dtemp(:) + real :: windthresh(3) = (/17.5,25.7,32.9/) + character cstormid*3 + character :: need_to_expand_r34(4)*1 + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *************************************************** ' + print *,' AT BEGINNING OF GETRADII, input radmax= ',radmax + print *,' *************************************************** ' + print *,' ' + print *,'xcenlon= ',xcenlon,' xcenlat= ',xcenlat + print *,'imax= ',imax,' jmax= ',jmax,' dx= ',dx,' dy= ',dy + endif + + igrret = 0 + +c ----------------------------------------------------------- +c PART 1: Define the maximum radius for which you'll search +c for the wind values, and then get the beginning and ending +c i and j points for that sub-region to search. Define this +c maximum radius (radmax) in terms of km. +c ----------------------------------------------------------- + +c radmax = 650.0 ! This value is in units of km. With April 2013 +c ! update, this is now defined in calling routine + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-A....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine getradii' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-B....' + stop 98 + endif + + igrret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmax is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmax/(dtk*dx))/cosfac) + numjpts = ceiling(radmax/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (ibeg < 1) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-C...' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jbeg < 1) jbeg = 1 + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in getradii calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Wind radii will not be calculated for this time.' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-D....' + stop 98 + endif + + igrret = 99 + return + endif + + if (iend > imax) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-E....' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getradii, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + +c ----------------------------------------------------------- +c PART 2: Within the area of grid points defined by jbeg, +c jend, ibeg and iend, (1) calculate all the wind speeds at +c each grid point, (2) calculate all of the distances from +c each grid point to the storm center, (3) assign each grid +c point to one of the 4 quadrants (NE,NW,SE,SW), (4) in each +c quadrant, sort the points, based on windspeed. +c ----------------------------------------------------------- + + jnum = jend - jbeg + 1 + inum = iend - ibeg + 1 +c numalloc = ((jnum * inum) / 2) + inum/2 + jnum/2 + numalloc = jnum * inum + inum/2 + jnum/2 + + if ( verb .ge. 3 ) then + print *,'in getradii, numalloc= ',numalloc,' radmax= ',radmax + endif + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + allocate (quadinfo(4,numalloc,2),stat=iqa) + + if (iqa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub getradii allocating quadinfo array.' + print *,'!!! iqa = ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-F....' + stop 98 + endif + + igrret = 94 + return + endif + + quadct = 0 + +c Calculate the distances and wind speeds at each grid point. If +c the distance is < radmax, include that wind info in the +c appropriate quadinfo array location for that quadrant. + + quadmax = 0.0 + + jloop: do j=jbeg,jend + iloop: do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point in question = ',i + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-G...' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub getradii' + print *,'!!! for a non-global grid. i= ',i + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-H....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + if (dist > radmax) cycle iloop + + if (valid_pt(ip,j)) then + + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + +cc print *,'i= ',i,' j= ',j,' dist= ',dist,' vmag= ',vmag + + ! Calculate the angle from the center point to this point + ! and then assign this point to the appropriate quadrant bin + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-xcenlon) * dtr + rlatb = xcenlat * dtr + d = degrees * dtr + +c write (6,59) 360.-xcenlon,xcenlat,360.-glon(ip),glat +c +c write (6,61) d/dtr,rlatc/dtr,360.-(rlonc/dtr),rlatb/dtr +c & ,360.-(rlonb/dtr),sin(rlatc),sin(rlatb),cos(d) +c & ,sin(d),cos(rlatb) +c +c +c 59 format (1x,'+++ gr, xcenlon= ',f8.3,'W xcenlat= ' +c & ,f8.3,' glon= ',f8.3,'W glat= ',f8.3) +c +c 61 format (1x,'+++ gr, d rlatc rlonc rlatb rlonb= ',5f9.4 +c & ,' sin(rlatc)= ',f8.6,' sin(rlatb)= ',f8.6 +c & ,' cos(d)= ',f8.6,' sin(d)= ',f8.6 +c & ,' cos(rlatb)= ',f8.6) + + if (d == 0.0) then + + pt_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d)) / + & (sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_heading_rad = acos(cosarg) + else + pt_heading_rad = 2*pi - acos(cosarg) + endif + + pt_heading = pt_heading_rad / dtr + + endif + + if (pt_heading >= 0.0 .and. pt_heading < 90.) then + ! NE quadrant + iq = 1 + else if (pt_heading >= 90.0 .and. pt_heading < 180.) then + ! SE quadrant + iq = 2 + else if (pt_heading >= 180.0 .and. pt_heading < 270.) then + ! SW quadrant + iq = 3 + else if (pt_heading >= 270.0 .and. pt_heading <= 360.) then + ! NW quadrant + iq = 4 + endif + +c write (6,73) xcenlat,360.-xcenlon,j,i,ip,glat(j) +c & ,360.-glon(ip),pt_heading,iq + + 73 format (1x,'+++ getradii clat clon: ',f6.2,' ',f7.2,'W',3i4 + & ,' plat plon: ',f6.2,' ',f7.2,'W Dir: ',f7.2 + & ,' Quad: ',i2) + + quadct(iq) = quadct(iq) + 1 + quadinfo(iq,quadct(iq),1) = vmag + quadinfo(iq,quadct(iq),2) = dist + if (vmag > quadmax(iq,4)) then + quadmax(iq,1) = glon(ip) + quadmax(iq,2) = glat(j) + quadmax(iq,3) = dist + quadmax(iq,4) = vmag + endif + + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After loop, quadct(1)= ',quadct(1),' quadct(2)= ' + & ,quadct(2) + print *,' quadct(3)= ',quadct(3),' quadct(4)= ' + & ,quadct(4) + print *,' ' + + write (6,110) cstormid,ifcsthr,'NE',quadmax(1,1),quadmax(1,2) + & ,quadmax(1,3)*0.539638,quadmax(1,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SE',quadmax(2,1),quadmax(2,2) + & ,quadmax(2,3)*0.539638,quadmax(2,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SW',quadmax(3,1),quadmax(3,2) + & ,quadmax(3,3)*0.539638,quadmax(3,4)*1.9427 + write (6,110) cstormid,ifcsthr,'NW',quadmax(4,1),quadmax(4,2) + & ,quadmax(4,3)*0.539638,quadmax(4,4)*1.9427 + print *,' ' + + 110 format (' quadmax: ',a3,1x,i3.3,1x,a2,1x,' lon: ',f6.2,'E',1x + & ,' lat: ',f6.2,' radius: ',f7.2,' nm',2x,' vmag: ' + & ,f6.2,' kts') + endif + +c Now go through each quadrant and put the wind speed distance info +c into a temporary array (dtemp), sort that array, and then scan +c through that array to find the various thresholds. + + quadrantloop: do k=1,4 + + if (need_to_expand_r34(k) == 'y') then + print *,'---> R34 search underway for quadrant ',k + & ,' radmax= ',radmax + continue + else + print *,'+ R34 okay for quadrant ',k,'... skipping...' + cycle quadrantloop + endif + + if (allocated(isortix)) deallocate (isortix) + if (allocated(dtemp)) deallocate (dtemp) + if (allocated(iwork)) deallocate (iwork) + allocate (isortix(quadct(k)),stat=iisa) + allocate (dtemp(quadct(k)),stat=idta) + allocate (iwork(quadct(k)),stat=iwa) + if (iisa /= 0 .or. idta /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii allocating isortix, dtemp' + print *,'!!! or iwork array for quadrant= ',k + print *,'!!! iisa = ',iisa,' idta= ',idta,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-I....' + stop 98 + endif + + itret = 94 + return + endif + +c ------------------- + + do m=1,quadct(k) + dtemp(m) = quadinfo(k,m,2) + enddo + + imode = 2 + isortix = 0 + + call qsort (dtemp,isortix,quadct(k)) + +ccccc call orders (imode,iwork,dtemp,isortix,quadct(k),1,8,1) +cccc call orders_4byte (imode,iwork,dtemp,isortix +cccc & ,quadct(k),1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' +c ************************************************************** +c--- mf 20100609 +c CAUSE OF SEG FAULT!!!!!!!! -- not sure still an issue if dtemp +c properly allocated +c + !print *,' dtemp(isortix(1)) = ',dtemp(isortix(1)) + print *,' dtemp(isortix(quadct(k)))= ' + & ,dtemp(isortix(quadct(k))) + print *,' isortix(1) = ',isortix(1) + print *,' isortix(quadct(k)) = ',isortix(quadct(k)) + endif + +c ! Uncomment these next lines to see a listing in the output of +c ! all wind values & distances in this quadrant less than radmax +c do iqq = 1,quadct(k) +c print *,' iqq= ',iqq,' vmag= ',quadinfo(k,isortix(iqq),1) +c & ,' dist= ',quadinfo(k,isortix(iqq),2) +c enddo + +c ------------------- + + if (quadct(k) < 2) then ! not enough members in array + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GETRADII, NOT ENOUGH MEMBERS IN ARRAY FOR' + print *,'!!! QUADRANT #',k,' .... # members = quadct(k)= ' + & ,quadct(k) + print *,'!!! SETTING ALL VRADII = 0 for quadrant = ',k + endif + + vradius(1,k) = 0 + vradius(2,k) = 0 + vradius(3,k) = 0 + cycle quadrantloop + endif + +c Within this quadrant, go through the sorted array of wind +c magnitudes and compare those wind values against the set +c wind thresholds to get the wind radii. The array has +c been sorted by distance from the storm center in order of +c closest (ipoint=1) to farthest (ipoint=quadct(k)). We +c analyze these wind values by starting at the farthest +c point and moving inward until we hit a point that has a +c wind value of at least 34-knot winds (17.5 m/s). When +c we find that point, we interpolate between that point and +c the next farthest out point to get the distance that would +c be for the exact 17.5 m/s value. We then continue searching +c through the wind values down closer to the storm center to +c see if we can find values for the 50- and 64-knot winds. + + iwindix = 1 + ipoint = quadct(k) + 1 + +c print *,'drp: quad= ',k,' quadct= ',quadct(k) + + threshloop: do while (iwindix <= 3 .and. ipoint > 1) + + if (iwindix > 1) then + if (first_time_thru_getradii) then + + ! We are only doing the wind radii for 50 and 64 kts on + ! the first time through subroutine getradii (we only + ! need to do the multiple call iterations for 34 kts). + ! + ! Make sure vmax for this lead time exceeds the radii + ! threshold being diagnosed. The check below avoids, + ! for example, reporting 50-kt wind radii when the max + ! wind diagnosed was only 44 kts. This can happen since + ! the radius for searching for radii is larger than the + ! radius for searching for the max wind. + if (vmaxwind >= windthresh(iwindix)) then + if (verb >= 3) then +c print *,' ' +c print *,' +++ vmaxwind of ',vmaxwind,' m/s exceeds' +c print *,' +++ threshold of ',windthresh(iwindix) +c print *,' +++ (m/s), so radii checking will continue' +c print *,' +++ for this threshold.' +c print *,' +++ igrct= ',igrct,' ipoint= ',ipoint +c & ,' iwindix= ',iwindix + continue + endif + continue + else + if (verb >= 3) then + print *,' ' + print *,' --- vmaxwind of ',vmaxwind,' m/s does NOT' + print *,' - - exceed threshold of ' + & ,windthresh(iwindix) + print *,' - - (m/s), so radii checking will NOT be ' + print *,' - - performed for this threshold.' + endif + iwindix = iwindix + 1 + cycle threshloop + endif + else + iwindix = iwindix + 1 + cycle threshloop + endif + endif + + ipoint = ipoint - 1 + + if (quadinfo(k,isortix(ipoint),1) < windthresh(iwindix)) then + cycle threshloop + else + if (ipoint == quadct(k)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In getradii, a max wind radius was' + print *,'!!! found at the maximum radius checked, so ' + print *,'!!! you may want to make sure that you are' + print *,'!!! checking at a far enough distance from ' + print *,'!!! the fix position, that is, you may want to' + print *,'!!! increase the value of radmax in subroutine' + print *,'!!! getradii. Currently, radmax (km) = ',radmax + print *,'!!! iwindix = ',iwindix,' quadrant= ',k + endif + + vradius(iwindix,k) = int( ((quadinfo(k,isortix(ipoint),2) + & * 0.5396) / 5.0) + 0.5) * 5 + else + +c Interpolate between the 2 closest distances to each wind +c threshold to get "exact" distance to that wind threshold +c radius, convert from km to nm, and then round to the +c nearest 5 nm (since TPC uses this precision). +c 7/23/98 UPDATE: Jim Gross has asked that values not be +c rounded to the nearest 5 nm, but rather only to the +c nearest 1 nm. + + exactdistkm = quadinfo(k,isortix(ipoint),2) + + & ( (quadinfo(k,isortix(ipoint),1) - windthresh(iwindix)) / + & (quadinfo(k,isortix(ipoint),1) - + & quadinfo(k,isortix(ipoint+1),1)) * + & ( (quadinfo(k,isortix(ipoint+1),2) - + & quadinfo(k,isortix(ipoint),2)) ) ) + + exactdistnm = exactdistkm * 0.5396 ! Convert km to nm + vradius(iwindix,k) = int(exactdistnm + 0.5) + +cc vradius(iwindix,k) = int( (exactdistnm / 5.0) + 0.5) * 5 + + + if ( verb .ge. 3 ) then + print *,'iwindix= ',iwindix,' exactdistnm = ' + & ,exactdistnm + print *,'vradius(iwindix,k) =',vradius(iwindix,k) + endif + + endif + +c The possibility exists, especially for coarse output +c grids, that there could be a jump over more than 1 wind- +c thresh category when going from 1 grid point to the next, so +c we need to account for this. For example, if 1 point has +c vmag = 15 m/s and the next point closer in has vmag = 28 +c m/s, then between those 2 points you have the thresholds +c for gale force AND storm force winds, so to be safe, we +c actually need to add 1 to ipoint and re-check the current +c point, if the wind value at that point is found to be +c greater than a wind threshold value (which it has if you've +c gotten to this point in threshloop). + + ipoint = ipoint + 1 + + iwindix = iwindix + 1 + + endif + + enddo threshloop + + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (idta /= 0 .or. iisa /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating isortix or' + print *,'!!! dtemp or work for quadrant= ',k + print *,'!!! idta= ',idta,' iisa= ',iisa,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-J....' + stop 98 + endif + + itret = 94 + return + endif + + enddo quadrantloop + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating quadinfo array.' + print *,'!!! iqa= ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-K....' + stop 98 + endif + + itret = 94 + return + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_max_wind (xcenlon,xcenlat,imax,jmax,dx,dy + & ,valid_pt,levsfc,vmax,trkrinfo,rmax,igmwret) +c +c ABSTRACT: This subroutine looks for the maximum near-surface wind +c near the storm center. This subroutine is only concerned with the +c value of the max wind, NOT where it's located radially with +c respect to the center. The value that's returned in vmax is the +c max wind speed in m/s, which are the units the data are stored in. +c However, when the max wind values are output in output_atcf, they +c will be converted from m/s to knots. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c levsfc integer holding the value of the array member that holds +c the near-surface winds in the u and v arrays (at orig +c writing, it's = 4). +c +c OUTPUT: +c +c vmax value of maximum near-surface wind near the storm ctr +c rmax radius of max winds +c igmwret return code from this subroutine +c +c LOCAL: +c +c radmaxwind the maximum radius to look for a max wind near the +c storm center. You have to allow this to be bigger for +c model grids with coarse resolution (ECMWF 2.5 degree). + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real radmaxwind,degrees,dx,dy,rmax + logical(1) valid_pt(imax,jmax) +c + igmwret = 0 + rmax = -99.0 + + if ((dx+dy)/2. <= 1.25) then + if ((dx+dy)/2. <= 0.25) then + radmaxwind = 300.0 + else + radmaxwind = 300.0 + endif + else + radmaxwind = 500.0 + endif + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmaxwind is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmaxwind/(dtk*dx))/cosfac) + numjpts = ceiling(radmaxwind/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_max_wind calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Value of vmax will be set to 0 for this time.' + endif + + vmax = 0.0 + igmwret = 99 + return + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + + print *,' ' + print *,'In get_max_wind, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + + vmax = 0.0 + do j=jbeg,jend + do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point = ',i + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + + if (dist > radmaxwind) cycle + + if (valid_pt(ip,j)) then + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + if (vmag > vmax) then + vmax = vmag + rmax = dist * 0.539638 ! convert from km to nm + endif + endif + + enddo + enddo + + if ( verb .ge. 3 ) then + print *,'At end of get_max_wind, vmax= ',vmax,' rmax= ',rmax + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine fixcenter (clon,clat,ist,ifh,calcparm,geslon,geslat + & ,inp,stderr,fixlon,fixlat,xvalues,maxstorm,ifret) +c +c ABSTRACT: This subroutine loops through the different parameters +c for the input storm number (ist) and calculates the +c center position of the storm by taking an average of +c the center positions obtained for those parameters. +c First we check to see which parameters are within a +c max error range (errmax), and we discard those that are +c not within that range. Of the remaining parms, we get +c a mean position, and then we re-calculate the position +c by giving more weight to those estimates that are closer +c to this mean first-guess position estimate. +c +c INPUT: +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c geslon Initial guess longitude for this storm at this fcst hour +c geslat Initial guess latitude for this storm at this fcst hour +c inp contains the input date and model number information +c xvalues The actual max or min data values for each parameter +c maxstorm max # of storms to be handled in this run +c +c INPUT/OUTPUT: +c stderr Standard deviation of the position "error" of the parms +c relative to the guess storm position. As long as the +c distance of a parm center to the guess center is <= +c errpmax, it is included in the std dev calculation. +c +c OUTPUT: +c fixlon Best approximation of storm center's longitude +c fixlat Best approximation of storm center's latitude +c ifret Return code from this subroutine +c +c LOCAL: +c storm Contains tcvitals info for the storms (def_vitals) +c trkerr_avg Sum/avg of the track errors for all parms for this +c fcst hour, regardless of whether or not the error was +c > errmax. It's used for getting the std deviation of +c the position error for this forecast time, to be used +c as part of the errmax calculation for the next fcst +c time. +c iclose Number of parameters whose position estimates are +c found to be within a distance errmax of the guess pos +c wtpos The weight given to each position estimate. It's +c based on the distance from the average position. +c errdist The "error" of the parameter center position relative +c to the storm's guess position. +c avgerr Average "error" of the parameter center positions +c relative to the storm's guess position. +c use4next Logical; If a parm center has been calculated but its +c distance from the guess position is > errmax, we don't +c use this center in calculating the new guess position, +c however we will use this position in calculating the +c standard deviation of the current time's guess +c positions, to be used in calculating the new errmax +c for the next forecast time. So in this subroutine, +c calcparm may be set to FALSE if errdist > errmax, but +c use4next will not be set to FALSE (Actually, it is +c only set to FALSE if errdist > errpmax, which is +c defined in error_parms and is roughly 600km). +c stderr_close Standard deviation of position errors for parms that +c have center estimates that are within a distance +c errmax of the guess position. +c clon_fguess These are the first-guess mean position estimates, +c clat_fguess which are the means of the position estimates that +c are within a distance errmax. These first-guess mean +c positions will be refined by giving more weight to +c individual parameter estimates that are closer to +c this first-guess mean position. +c dist_from_mean Contains the "error" distance of each parameter +c from the first-guess mean position (clon_fguess, +c clat_fguess). NOTE: If a parameter is not within +c a distance errmax of the guess position for this +c time (geslon,geslat), then there will be NO +c dist_from_mean calculated for that parm. +c + USE error_parms; USE set_max_parms; USE inparms; USE def_vitals + USE atcf; USE gen_vitals; USE tracked_parms + USE verbose_output + + type (datecard) inp + + real clon(maxstorm,maxtime,maxtp),temp_clon(maxtp) + real clat(maxstorm,maxtime,maxtp),temp_clat(maxtp) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real trkerr(maxtp),errdist(maxtp),xvalues(maxtp) + real stderr(maxstorm,maxtime),devia(maxtp),wtpos(maxtp) + real dist_from_mean(maxtp) + real degrees,errtmp + integer gt345_ct,lt15_ct + logical(1) calcparm(maxtp,maxstorm),use4next(maxtp) + character charparm(maxtp)*8,charmaxmin(maxtp)*8 +c + data charparm /'zeta 850','zeta 700','circ 850','NOT USED' + & ,'circ 700','NOT USED',' gph 850',' gph 700',' MSLP' + & ,'circ sfc','zeta sfc',' thk 5-8',' thk 2-5',' thk 2-8'/ + data charmaxmin /' Max ',' Max ',' Min ','NOT USED' + & ,' Min ','NOT USED',' Min ',' Min ',' Min ' + & ,' Min ',' Max ',' Max ',' Max ',' Max '/ +c + ifret=0 +c +c We need to judge whether each parameter position is reasonable, +c so we'll check to make sure that the dist from each parameter's +c estimate to the guess position is less than a maximum allowable +c error. If it's the first forecast time, use the initial error max +c (defined as errinit in error_parms) as errmax. Otherwise, the +c max error criterion is that the distance error must not exceed 3 +c times the previous forecast time's standard deviation (after a +c small growth factor has been applied). +c UPDATE 3/5/98: During testing, it was found that just using the +c previous time's stdev made errmax too "jumpy" (i.e., at vt=48h, +c errmax could = 380, and then at vt=54h, errmax could jump down +c to 190, so we've changed it so that it uses an average of the +c stdev's from the 3 previous forecast times to maintain some +c continuity between successive forecast times). +c + if (ifh == 1) then + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR' ) then + errmax = err_gfs_init + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errmax = err_ecm_max + errinit = err_ecm_max + else + errmax = err_reg_init + errinit = err_reg_init + endif + else + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR') then + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errinit = err_ecm_max + else + errinit = err_reg_max + endif + + if (ifh >= 4) then + xavg_stderr = (stderr(ist,ifh-3) + stderr(ist,ifh-2) + & + stderr(ist,ifh-1)) / 3.0 + else if (ifh == 3) then + xavg_stderr = (stderr(ist,ifh-2) + stderr(ist,ifh-1)) / 2.0 + else if (ifh == 2) then + xavg_stderr = stderr(ist,ifh-1) + endif + +c The following errmax statement was replaced by the ensuing 4 +c lines due to a compiler bug on some other platforms: +c errmax = amin1(amax1(3.0*xavg_stderr*errpgro,errinit) +c & ,errpmax) + + errtmp = 3.0*xavg_stderr*errpgro + errmax = max(errtmp,errinit) + errtmp = errpmax + errmax = min(errmax,errtmp) + + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (ifh > 1) then + print '(a42,f8.2,a15,f8.2)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = ' + & ,stderr(ist,ifh-1),' xavg_stderr= ',xavg_stderr + else + print '(a45,a18)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = N/A' + & ,' xavg_stderr= N/A' + endif + print *,'At beg of fixcenter, errpgro = ',errpgro + print *,'At beg of fixcenter, errinit = ',errinit + print *,'At beg of fixcenter, errpmax = ',errpmax + print *,'At beg of fixcenter, ifh= ',ifh,' errmax= ',errmax + endif + + trkerr_avg = 0.0 + iclose = 0; itot4next = 0 + clonsum = 0.0; clatsum = 0.0 + errdist = 0.0 + use4next = .FALSE. + gt345_ct = 0 + lt15_ct = 0 + +c For each parm, check to see if the estimated center is within +c distance errmax of the guess center. If it's within errmax, +c then use that parm for locating the center. If it's NOT +c within errmax, but IS within errpmax, then we still use this +c in calculating the standard deviation of the parameters for +c helping to determine the errmax for the next forecast hour. + +c OLD NOTE: For calculating the std dev to be used for the next +c OLD forecast hour, do NOT use vmag 850, vmag 700 or vmag sfc, since +c OLD those parms are always guaranteed to be within a short range of +c OLD the guess, due to the nature of the algorithm (see subroutine +c OLD get_uv_center for further details on that). + + do ip=1,maxtp + + if (ip == 4 .or. ip == 6) then ! Parms 4 & 6 not defined. + calcparm(ip,ist) = .FALSE. + cycle + endif + if (calcparm(ip,ist)) then + call calcdist (geslon,geslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + errdist(ip) = dist + if (dist <= errpmax) then + use4next(ip) = .TRUE. + trkerr_avg = trkerr_avg + dist + itot4next = itot4next + 1 + endif + if (dist <= errmax) then + iclose = iclose + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 + endif + clonsum = clonsum + clon(ist,ifh,ip) + clatsum = clatsum + clat(ist,ifh,ip) + else + calcparm(ip,ist) = .FALSE. + endif + endif + + enddo + + if (iclose > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (clonsum) + clon_fguess = (clonsum + (360.*float(lt15_ct)))/ iclose + else + clon_fguess = clonsum / float(iclose) + endif + if (clon_fguess >= 360.0) then + clon_fguess = clon_fguess - 360. + endif + clat_fguess = clatsum / float(iclose) + endif + +c Print out a table listing of the locations of the fixes for +c the individual parameters. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'--------------------------------------------------' + write (6,95) 'Individual fixes follow..., fhr= ',ifhours(ifh) + & ,ifclockmins(ifh),' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + write (6,97) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,'Model name = ',atcfname + print *,'Values of -99.99 indicate that a fix was unable to be' + print *,'made for that paramater. Parameters 4 & 6 are not' + print *,'used. Vorticity data values are scaled by 1e5.' + print *,'Circulation data values are scaled by 1e-6.' + print *,'errdist is the distance that the position estimate is' + print *,'from the guess position for this time. MSLP value ' + print *,'here may differ from that in the atcfunix file since ' + print *,'the one here is that derived from the area-averaged ' + print *,'barnes analysis, while that in the atcfunix file is ' + print *,'from a specific gridpoint.' + write (6,21) geslon,360.-geslon,geslat + write (6,*) ' ' + write (6,23) + write (6,25) + endif + + if (geslat > 0.0) then + charmaxmin(1) = ' Max ' + charmaxmin(2) = ' Max ' + charmaxmin(3) = ' Max ' + charmaxmin(5) = ' Max ' + charmaxmin(10) = ' Max ' + charmaxmin(11) = ' Max ' + else + charmaxmin(1) = ' Min ' + charmaxmin(2) = ' Min ' + charmaxmin(3) = ' Min ' + charmaxmin(5) = ' Min ' + charmaxmin(10) = ' Min ' + charmaxmin(11) = ' Min ' + endif + + do ip=1,maxtp + if (ip == 1 .or. ip == 2 .or. ip == 11) then + ! This IF block allows vorticity values to be + ! written out and scaled up by 1e5 ... + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + elseif (ip == 3 .or. ip == 5 .or. ip == 10) then + ! This IF block allows circulation values to be + ! written out and scaled down by 1e-6 ... + + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + else + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + endif + enddo + + 21 format (' Guess location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 23 format (' parm# parm Max/Min Lon_fix(E) Lon_fix(W)' + & ,' Lat_fix Max/Min_value calcparm errdist(km)') + 25 format (' ----- ---- ------- ---------- ----------' + & ,' ------- ------------- -------- ----------') + 27 format (2x,i2,4x,a8,2x,a8,3x,f7.2,5x,f7.2,4x,f7.2,7x,f9.2 + & ,6x,L2,7x,f7.2) + 95 format (1x,a33,1x,i4,':',i2.2,a2,a4,a1,a9) + 97 format (' Gen ID (if available): ',i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3) + + +c If number of parameter centers close enough (iclose) > 0, then +c calculate the center by taking an average of all the parameter +c center positions that are within distance errmax from the guess +c position (geslon,geslat). Get a first-guess mean position, and +c then re-calculate the position estimate by giving more weight +c to those positions that are closer to the first-guess mean +c position. + + dist_from_mean = 0.0 + + if (iclose > 0) then + +c Get distances from first-guess mean position.... + + do ip=1,maxtp + if (calcparm(ip,ist)) then + call calcdist (clon_fguess,clat_fguess,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + dist_from_mean(ip) = dist + endif + enddo + +c Get the mean distance of each parameter estimate from +c the first-guess mean position + + call avgcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,iaret) + + if (iaret == 0) then + + call stdevcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,stderr_close,isret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After stdevcalc, xmn_dist_from_mean= ' + & ,xmn_dist_from_mean,' stderr_close= ' + & ,stderr_close,' isret= ',isret + endif + + endif + if (iaret /= 0 .or. isret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER -- Error occurred in either' + print *,'!!! avgcalc or stdevcalc. Storm number = ',ist + print *,'!!! RCC from avgcalc = ',iaret + print *,'!!! RCC from stdevcalc = ',isret + print *,'!!! Center fix will NOT be made, and processing' + print *,'!!! for this storm is ending. The probable cause' + print *,'!!! is that no calcparms were valid for this storm' + print *,'!!! at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + if (calcparm(1,ist) .or. calcparm(2,ist) .or. calcparm(7,ist) + & .or. calcparm(8,ist) .or. calcparm(9,ist) + & .or. calcparm(11,ist) .or. calcparm(3,ist) + & .or. calcparm(10,ist) .or. calcparm(5,ist) + & .or. calcparm(12,ist) .or. calcparm(13,ist) + & .or. calcparm(14,ist)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In fixcenter, STOPPING PROCESSING for this' + print *,'!!! storm. The reason is that none of the fix' + print *,'!!! locations for parms z850, z700, zeta 850,' + print *,'!!! zeta 700, MSLP, wcirc_850, wcirc_700, ' + print *,'!!! wcirc_sfc, sfc zeta or the various levels ' + print *,'!!! of thicknesses were within a ' + print *,'!!! reasonable distance of the guess location.' + print *,'!!! ist= ',ist,' ifh= ',ifh + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'!!! Forecast hour: ',i4,':',i2.2) + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now re-calculate the mean position by giving more weight +c to those position estimates that are closer to the first +c guess mean position. Note that if stderr_close < 5.0, we +c force it to be 5.0; we do this to avoid getting very +c large numbers for devia values, which could make the +c weights (wtpos) equal to 0. This occurred during testing +c when only 2 parameters were valid, and so, of course, the +c standard deviation from the mean of those 2 parameters +c was close to 0, which gave devia values around 6000, and +c then wtpos values of 0, leading to a divide by 0 crash +c later on in subroutine wtavrg. + + kprm=0 + + if (stderr_close > 0.0) then + if (stderr_close < 5.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Since stderr_close had a value less than' + print *,'5, stderr_close has been forced to be equal' + print *,'to 5 in order to avoid dividing by zero later' + print *,'on in subroutine wtavrg.' + endif + + stderr_close = 5.0 + endif + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + devia(kprm) = dist_from_mean(ip) / stderr_close + wtpos(kprm) = exp(-devia(kprm)/3.) + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + + if ( verb .ge. 3 ) then + write (6,113) ip,kprm,dist_from_mean(ip),devia(kprm) + & ,wtpos(kprm),temp_clon(kprm) + & ,360.-temp_clon(kprm),temp_clat(kprm) + endif + + endif + enddo + 113 format (1x,'ip= ',i2,' kprm= ',i2,' dist_from_mean= ',f7.3 + & ,' devia= ',f7.3,' wtpos= ',f8.5,2x,3(2x,f7.2)) + else +c +c This next if statement is for the case in which only 1 +c parameter is valid, for which the stderr_close will = 0 +c (obviously), but as long as we have 1 valid parameter, +c continue processing, and set the weight for that parm = 1. +c The else portion is for the case in which stderr_close +c = 0 with NO parms being close. +c + if (iclose == 1) then + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + wtpos(kprm) = 1 + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + endif + enddo + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, stderr_close not > 0' + print *,'!!! stderr_close = ',stderr_close + print *,'!!! The probable cause is that no calcparms were' + print *,'!!! valid for this storm at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + endif +c + if (kprm > 0) then + call wtavrg_lon (temp_clon,wtpos,kprm,fixlon(ist,ifh),iwtret1) + call wtavrg (temp_clat,wtpos,kprm,fixlat(ist,ifh),iwtret2) + if (iwtret1 == 0 .and. iwtret2 == 0) then + if (verb .ge. 3) then + print *,' ' + write (6,173) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + 173 format ('At end of fixcenter: ',a4,' fhr= ',i4,':',i2.2 + & ,' Fix position= ',f7.2,'E (',f6.2,'W)',2x,f7.2) + print *,' ' + endif + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER in call to wtavrg.' + print *,'!!! Return Codes from wtavrg calls follow: ' + print *,'!!! RCC from wtavrg for long fix: ',iwtret1 + print *,'!!! RCC from wtavrg for lat fix: ',iwtret2 + print *,'!!! This means a divide by zero would have ' + print *,'!!! been attempted, which means that the ' + print *,'!!! weights in wtpos are not > 0. Check in' + print *,'!!! subroutine fixcenter where devia values' + print *,'!!! are calculated to see if something is ' + print *,'!!! wrong there. Values of wtpos array follow:' + print *,'!!! ',wtpos + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + print *,' ' + endif + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, kprm NOT > 0' + print *,'!!! This means that, for whatever reason, the ' + print *,'!!! calcparm logical flag was set to .FALSE. for' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: IN FIXCENTER, No storms are within errmax ' + print *,'!!! OR the calcparm logical flag was set to .FALSE. ' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now calculate the average error of all the parms that are within +c a radius errpmax (defined in error_parms, ~600km), and the std +c dev of those errors. This standard deviation will be used in +c calculating the maximum allowable error for the next forecast +c time. + + if (itot4next > 0 .and. ifret /= 95) then + trkerr_avg = trkerr_avg / float(itot4next) + call stdevcalc (errdist,maxtp,use4next,trkerr_avg + & ,stderr(ist,ifh),isret) + if (isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in FIXCENTER calculating std deviation ' + print *,'!!! for use in next forecast hours errmax.' + print *,'!!! ist= ',ist,' ifh= ',ifh,' itot4next= ' + & ,itot4next + endif + + ifret = 95 + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine avgcalc (xdat,kmax,valid,xavg,iaret) +c +c ABSTRACT: This subroutine just calculates a straight average of +c the parameters in the input array (xdat). The logical array +c (valid) indicates whether or not to include a particular array +c member or not in the calculation. + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) +c + iaret = 0 +c + xsum = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + xsum = xsum + xdat(i) + ict = ict + 1 + endif + enddo +c + if (ict > 0) then + xavg = xsum / float(ict) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in avgcalc, ict NOT > 0' + endif + + xavg = xdat(1) + iaret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg (xdat,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xdat) using the input weights +c in the input array (wt). It is used to calculate the center lat +c and lon fix positions. +c + USE verbose_output + + real xdat(kmax),wt(kmax) +c + iwtret = 0 +c + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xdat(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg, wtot NOT > 0' + endif + + iwtret = 95 + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg_lon (xlon,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xlon) using the input weights +c in the input array (wt). This subroutine is specifically used +c to find the center lon fix positions. It contains code to +c account for wrapping around the Greenwich Meridian. +c + + USE verbose_output + + real xlon(kmax),wt(kmax) + integer gt345_ct,lt15_ct +c + iwtret = 0 + gt345_ct = 0 + lt15_ct = 0 + +c First check to see if we have lons that are both to the left +c and the right of the greenwich meridian + + do i = 1,kmax + if (xlon(i) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (xlon(i) < 15.) then + lt15_ct = lt15_ct + 1 + endif + enddo + + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some lons that are in the 300's (west of the GM), and + ! some that are in the 0's (east of the GM). We need to + ! standardize these if we want to get a meaningful average. + do i = 1,kmax + if (xlon(i) < 15.) then + xlon(i) = xlon(i) + 360.0 + endif + enddo + endif + + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xlon(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg_lon, wtot NOT > 0' + endif + + iwtret = 95 + endif + + if (xwtavg >= 360.0) then + xwtavg = xwtavg - 360.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine stdevcalc (xdat,kmax,valid,xavg,stdx,isret) + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) + + isret = 0 + + stdx = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + stdx = stdx + (xdat(i) - xavg)**2 + ict = ict + 1 + endif + enddo + + if (ict > 0) then + stdx = sqrt(stdx/float(ict)) + if (stdx == 0.0) then +c This can happen if you have just 2 points; The mean position +c will be exactly in the middle of the 2 points and so the +c standard deviation around that mean point will be 0. And +c since the calling routine will quit if the returned standard +c deviation is 0, we must force it to be 1 so the program +c continues running. Theoretically, it could also happen with +c 3 or more points, but the likelihood of the distances working +c out to exactly equidistant for 3 points is not that good. + stdx = 1.0 + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in stdevcalc, ict NOT > 0' + endif + + isret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,fxval,trkrinfo + & ,cmodel_type,maxmin,igwcret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the wind circulation near the storm center. This center fix is +c done differently than for the other parms. With this fix, +c we limit the area that is searched. This subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the +c original guess position for this lead time and the 5 other parm +c fixes that have already been made for this lead time. That +c modified guess position is passed into this subroutine as uvgeslon +c and uvgeslat, and that's where the searching for the wind +c circulation is centered. +c +c This subroutine works by converting the winds to Vt and Vr at +c each grid point evaluated, relative to each candidate center point +c that is being evaluated at the time in the loop. We then compute +c the circulation at each of 24 azimuths surrounding the storm +c center, where circulation = Vt * (length of a 1/24 arc, in meters) +c This process is repeated for 7 successive radii and the results +c are summed up over all radii, approximating a solid disk +c circulation. The point at which the circulation is maximized +c (NHEM) or minimized (SHEM) is the center of circulation. +c +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c + + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + character(*) cmodel_type,maxmin + integer, parameter :: numdist=7,numazim=24 + integer imax,jmax,ist,level,igwcret,icvpret,idist,iazim + real rdist(numdist),vr(numazim,numdist),vt(numazim,numdist) + real vt_mean(numdist),circul_band(numdist) + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + real rads,ri,uvgeslon,uvgeslat,dx,dy,ctlon,ctlat,fxval + real temp_grid_minlon,temp_guesslon,rlatt,rlont,bear + real targlon,targlat,xintrp_u,xintrp_v,vt_azim_sum,degrees + real circ_diff,circ_diff_sum,hemisphere,wind_mag_ctr,dist + real xmin_circ_diff_mean,xmax_circ_diff_mean,tlon,tlat + real dell,fmax,fmin,grid_buffer,circ_diff_mean + real circumference,arclength + real circul_disk,xmax_circul_disk,xmin_circul_disk + integer ibiret1,ibiret2,igvtret,azimuth_ct,igiret,npts + integer igibret + integer circ_diff_ct,ir,nhalf,bskip1,bskip2,iskip,nlev + integer ilonfix,jlatfix,ibeg,iend,jbeg,jend,i,j,k,iix,jix + logical(1) cflag, valid_pt(imax,jmax) + +c---------------- +c + + print *,' ' + print *,'top of get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' cmodel_type= ',cmodel_type + print *,' maxmin= ',maxmin + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' cflag= ',cflag + print *,' ctlon= ',ctlon,' ctlat= ',ctlat + print *,' fxval= ',fxval + print *,' igwcret= ',igwcret + + igwcret = 0 + + grid_maxlat = glatmax + grid_minlat = glatmin + grid_maxlon = glonmax + grid_minlon = glonmin + + rads = rads_wind_circ + ri = ri_wind_circ + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of get_wind_circulation, rads= ',rads + & ,' ri= ',ri,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+15; fmin = 1.0e+15 + ctlon = 0.0; ctlat = 0.0 + +c Distances checked and the radial intervals are a function of +c the grid resolution.... + + if (dell > 0.50) then + rdist(1) = 50. + rdist(2) = 85. + rdist(3) = 120. + rdist(4) = 155. + rdist(5) = 190. + rdist(6) = 225. + rdist(7) = 260. + else + rdist(1) = 35. + rdist(2) = 65. + rdist(3) = 95. + rdist(4) = 125. + rdist(5) = 155. + rdist(6) = 185. + rdist(7) = 215. + endif + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + print *,' in get_wind_circulation, nlev= ',nlev + + if (uvgeslat >= 0.0) then + hemisphere = 1.0 + else + hemisphere = -1.0 + endif + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend + & ,igibret) + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (uvgeslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = uvgeslon - 360. + else + temp_guesslon = uvgeslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = uvgeslon + endif + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + +c For the wind circulation analysis, we will want to speed things +c up for finer resolution grids. We can do this by skipping some +c of the points in the wind circulation analysis. + + if (dell > 0.20) then + bskip1 = 1 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 3 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 5 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 8 + bskip2 = 3 + else if (dell <= 0.03) then + bskip1 = 10 + bskip2 = 4 + endif + +c bskip1 = 1 +c bskip2 = 1 + + jix = 0 + +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to first loop, ' + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop1: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = uvgeslat + dell*float(j) + + iix = 0 + + iloop1: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop1 + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT from call in ' + print *,'!!! get_wind_circulation: icvpret= ',icvpret + endif + cycle iloop1 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist,degrees) + if (dist .gt. rads) cycle iloop1 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop1: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop1: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + ! These calls to bilin_int_uneven pass a variable "level" + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop1 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop1 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop1 +cc and radiusloop1). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,' ' +cc print *,'1st run, wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'1st run, ir= ',ir,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +c print *,'1st run, circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'1st run, circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'1st run, xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif + + enddo iloop1 + + enddo jloop1 + + if (uvgeslat >= 0.0) then + write (6,61) 360.-ctlon,ctlat,xmax_circul_disk + else + write (6,63) 360.-ctlon,ctlat,xmin_circul_disk + endif + + 61 format (' After first run, Wind Circulation (NHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmax_circul_disk = ',f15.1) + 63 format (' After first run, Wind Circulation (SHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmin_circul_disk = ',f15.1) + +c If nhalf is specified as 0, then don't go through any more +c iterations of this routine, just exit with the value that we +c already got the first time through the loop, above. + + if (dell > 0.50) then + nhalf = 4 + else if (dell > 0.20 .and. dell <= 0.50) then + nhalf = 3 + else if (dell > 0.10 .and. dell <= 0.20) then + nhalf = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + nhalf = 1 + else if (dell <= 0.05) then +c nhalf = 0 + nhalf = 1 +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'In get_wind_circulation, dell is < 0.05 deg, so ' +c print *,'nhalf is set to 0 and only the first iteration of' +c print *,'the search loop is done.' +c print *,' dell= ',dell,' nhalf= ',nhalf +c endif + endif + + if (nhalf < 1) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +c npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only do this once +c for this grid-refinement (even though the grid is redefined +c nhalf times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). Cut the value of +c rads in half (only do this once) so that any points beyond +c rads/2 are not considered as potential centers. + + rads = 0.5 * rads + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igibret) + + if (igibret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_wind_circulation from call to ' + print *,'!!! get_ij_bounds just before nhalf loop. ' + print *,'!!! Stopping processing for storm number ',ist + endif + igwcret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + kloop: do k = 1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: get_wind_circ kloop, k= ',i2,' ' + & ,i2.2,':',i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'get_wind_circ nhalf loop, k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip,' rads= ',rads + endif + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to loop k= ',k + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist + & ,degrees) + if (dist .gt. rads) cycle iloop2 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop2: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop2: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop2 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop2 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop2 +cc and radiusloop2). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,'kloop k= ',k,' wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'kloop k= ',k,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +cc print *,'kloop k= ',k,' circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'kloop k=',k,' circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0.0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'kloop k= ',k,' xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean + + enddo iloop2 + + enddo jloop2 + + if ( verb .ge. 3 ) then + if (uvgeslat >= 0.0) then + print *,'---> xmax_circul_disk= ',xmax_circul_disk + write (6,71) k,360.-ctlon,ctlat,xmax_circul_disk + else + print *,'---> xmin_circul_disk= ',xmin_circul_disk + write (6,73) k,360.-ctlon,ctlat,xmin_circul_disk + endif + endif + + enddo kloop + + 71 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (NHEM: Max) = ' + & ,f15.1) + 73 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (SHEM: Min) = ' + & ,f15.1) + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,xval,trkrinfo,igucret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the minimum in the wind speed near the storm center. This center +c fix is done differently than for the other parms. With this fix, +c we severely limit the area that is searched, because we do not +c want to confuse a wind minimum out on the periphery of a storm +c with the center wind minimum. Therefore, this subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the guess +c position for this time and the 5 other parm fixes. That modified +c guess position is passed into this subroutine as uvgeslon and +c uvgeslat, and that's where the searching for the wind minimum +c is done. To get the wind minimum, the u and v data are first +c interpolated down to a fine grid (see details below for exact +c figures), and then a single-pass barnes analysis is done on that +c fine grid. The reason that we first interpolate the data (which +c is different from how we do the other parms) is that if we just +c use the original grid resolution, we may not be able to +c accurately pick out a minimum in the wind field at the center. +c + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real, allocatable :: uold(:,:),vold(:,:),unew(:,:),vnew(:,:) + real, allocatable :: rlonold(:),rlatold(:),rlonnew(:),rlatnew(:) + real, allocatable :: vmag(:,:) + real :: dx,dy + real :: grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + character*1 :: gotlat + logical(1) cflag, valid_pt(imax,jmax) + logical(1), allocatable :: lbi(:,:) +c + gotlat = 'n' +c +c ----------------------------------------------------------------- +c INTERPOLATE INPUT GRID TO SMALLER GRID +c ----------------------------------------------------------------- +c +c Get beginning and ending j points (on the input grid) for a +c smaller array that surrounds the storm. It is this smaller array +c that we will interpolate to a finer grid. +c +c Calculate number of pts to either side of this j to search +c + npts = ceiling(rads_vmag/(dtk*((dx+dy)/2.))) +c + call get_ij_bounds (npts,0,ritrk_vmag,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij D, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij D, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center from call to ' + print *,'!!! get_ij_bounds, stopping processing for ' + print *,'!!! storm number ',ist + endif + + igucret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, and our gridtype is NOT' + print *,'!!! global, so we are going to redefine ibeg to 1.' + print *,' ' + endif + + ibeg = 1 + endif + endif + + if (jbeg < 1) jbeg = 1 + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center calculating ibeg, iend, jbeg' + print *,'or jend. ibeg= ',ibeg,' iend= ',iend,' jbeg= ',jbeg + print *,'jend= ',jend + print *,'uv center will not be calculated for this time.' + endif + + igrret = 99 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, and our gridtype is' + print *,'!!! NOT global, so we will redefine iend to imax.' + print *,' ' + endif + + iend = imax + endif + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif +c + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the grid sizes for +c some of the typical grids that will be used: +c +c Original grid size # of interps Final grid size +c -------------------- ------------ --------------------- +c 1.00 deg (111.19 km) 3 0.125 deg (13.9 km) +c 1.25 deg (138.99 km) 3 0.156 deg (17.4 km) +c 2.50 deg (277.99 km) 4 0.156 deg (17.4 km) + + if ((dx+dy)/2. > 1.2) then + numinterp = 4 + else if ((dx+dy)/2. > 0.50 .and. (dx+dy)/2. <= 1.2) then + numinterp = 3 + else if ((dx+dy)/2. > 0.25 .and. (dx+dy)/2. <= 0.50) then + numinterp = 2 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.25) then + numinterp = 1 + else if ((dx+dy)/2. <= 0.10) then + numinterp = 0 + endif + + dell = (dx+dy)/2. + imxold = iend - ibeg + 1 + jmxold = jend - jbeg + 1 + +c -------------------------------------------------------------- +c Before interpolating, make sure that all the original +c points have valid data. If they don't then exit the +c subroutine. NOTE: This is NOT checking to see if ALL the pts +c on the complete & full input grid have valid data; it only +c checks those points that are within the box returned from +c get_ij_bounds. + + do i=ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_uv_center, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! PROCESSING WILL STOP. ' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + stop 94 + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_uv_center' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + do j=jbeg,jend + if (.not. valid_pt(ip,j)) goto 975 + enddo + + enddo + +c ------------------------------------ +c Now begin the interpolation process + + allocate (uold(imxold,jmxold),stat=iuo) + allocate (vold(imxold,jmxold),stat=ivo) + allocate (rlonold(imxold),stat=iloo) + allocate (rlatold(jmxold),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. iloo /= 0 .or. ilao /= 0) goto 970 + + do intnum = 1,numinterp + + if (intnum == 1) then + + do i=ibeg,iend + + ik = i + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ik = i + imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i < 1' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i + endif + + igucret = 92 + return + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ik = i - imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i > imax' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i,' imax= ',imax + endif + + igucret = 92 + return + endif + endif + + rlonold(i-ibeg+1) = glon(ik) + do j=jbeg,jend + uold(i-ibeg+1,j-jbeg+1) = u(ik,j,nlev) + vold(i-ibeg+1,j-jbeg+1) = v(ik,j,nlev) + if (gotlat == 'n') then + rlatold(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatold once + enddo + + else + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate (rlatold) + allocate (uold(imxnew,jmxnew),stat=iuo) + allocate (vold(imxnew,jmxnew),stat=ivo) + allocate (rlonold(imxnew),stat=iloo) + allocate (rlatold(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 970 + + gotlat = 'n' + do i=1,imxnew + rlonold(i) = rlonnew(i) + do j=1,jmxnew + uold(i,j) = unew(i,j) + vold(i,j) = vnew(i,j) + if (gotlat == 'n') then + rlatold(j) = rlatnew(j) + endif + enddo + gotlat = 'y' + enddo + + imxold = imxnew + jmxold = jmxnew + deallocate (unew); deallocate (vnew) + deallocate (rlonnew); deallocate (rlatnew) + + endif + + dell = 0.5 * dell + imxnew = 2 * imxold - 1 + jmxnew = 2 * jmxold - 1 + + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + + call bilin_int_even (imxold,jmxold,uold + & ,imxnew,jmxnew,unew,ibiret) + call bilin_int_even (imxold,jmxold,vold + & ,imxnew,jmxnew,vnew,ibiret) +c call lin_int (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int_lon (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int (jmxold,jmxnew,rlatold,rlatnew,iliret) + + chk_lonspc_old = rlonold(imxold) - rlonold(imxold - 1) + chk_latspc_old = rlatold(jmxold) - rlatold(jmxold - 1) + chk_lonspc_new = rlonnew(imxnew) - rlonnew(imxnew - 1) + chk_latspc_new = rlatnew(jmxnew) - rlatnew(jmxnew - 1) + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, intnum= ',intnum + print *,'imxold= ',imxold,' imxnew= ',imxnew + print *,'jmxold= ',jmxold,' jmxnew= ',jmxnew + print *,'Grid boundaries of modified uv grid: ' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ' + & ,grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ' + & ,grid_minlon + endif + + enddo + +c ------------------ + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate(rlatold) + + if (numinterp == 0) then + + ! No interpolations were done for this fine mesh grid, but we + ! need to fill some of these arrays and define variables for + ! subsequent subroutine calls just below here that require + ! the variables imxnew, jmxnew, and the arrays unew and vnew. + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional ' + print *,'grid; iend should not > imax here !!!' + endif + + igucret = 99 + return + endif + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional' + print *,'grid; ibeg should not < 1 here !!!' + endif + + igucret = 99 + return + endif + endif + + imxnew = iend - ibeg + 1 + jmxnew = jend - jbeg + 1 + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + gotlat = 'n' + + do i=ibeg,iend + + ip = i + + if (i > imax) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i - imax ! Wrapping past GM + endif + + if (i < 1) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i + imax ! Wrapping past GM + endif + + rlonnew(i-ibeg+1) = glon(ip) + do j=jbeg,jend + unew(i-ibeg+1,j-jbeg+1) = u(i,j,nlev) + vnew(i-ibeg+1,j-jbeg+1) = v(i,j,nlev) + if (gotlat == 'n') then + rlatnew(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatnew once + enddo + + endif + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,'Grid boundaries of modified uv grid in get_uv_center:' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ',grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ',grid_minlon + endif + + allocate (vmag(imxnew,jmxnew),stat=ivm) + allocate (lbi(imxnew,jmxnew),stat=ilb) + if (ivm /= 0 .or. ilb /= 0) goto 972 + call calc_vmag (unew,vnew,imxnew,jmxnew,vmag,icvret) + deallocate (unew); deallocate (vnew) + + lbi = .TRUE. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to find_maxmin, imxnew= ',imxnew + & ,'jmxnew= ',jmxnew,' ist= ',ist + write (6,171) dell,uvgeslon,360.-uvgeslon,uvgeslat + 171 format (' dell= ',f7.3,' uvgeslon= ',f8.3,'E (',f8.3,'W)' + & ,' uvgeslat= ',f8.3) + endif + +c Note that in the next call, I pass the 'global' argument to +c find_maxmin. This defines what type of grid it is, so that the +c proper grid_buffer can be chosen. This grid_buffer is designed +c to avoid having a center be chosen too close to the grid +c boundary. However, in the case of vmag here, we are only using +c a small subgrid, and we want to make sure we use *all* points +c in that subgrid for searching, and that will occur if we set that +c calling argument to 'global' as opposed to 'regional'. + + call find_maxmin (imxnew,jmxnew,dell,dell,'vmag' + & ,vmag,'min',ist,uvgeslon,uvgeslat,rlonnew,rlatnew,lbi + & ,trkrinfo,cflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,'global',ifmret) + deallocate (vmag); deallocate (lbi) + deallocate (rlonnew); deallocate (rlatnew) +c + if (ifmret == 0) then + goto 995 + else + igucret = ifmret + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center in call to find_maxmin' + print *,'!!! storm num = ',ist,' igucret = ',igucret + endif + + goto 998 + endif +c + 970 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either uold, vold,' + print *,'!!! rlonold or rlatold in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 971 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either unew, vnew,' + print *,'!!! rlonnew or rlatnew in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 972 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either vmag or lbi in ' + print *,'!!! subroutine get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! ivm= ',ivm,' ilb= ',ilb + endif + + igucret = 97 + goto 998 + + 975 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Inside get_uv_center, at least one of the points' + print *,'!!! is not a valid data point. This point may be ' + print *,'!!! outside the valid data bounds of a regional grid' + print *,'!!! i= ',i,' j= ',j + print *,'!!! Storm number = ',ist + endif + + igucret = 98 + goto 998 +c + 995 continue + igucret = 0 +c + 998 continue + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_guess (guesslon,guesslat,clon,clat + & ,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) +c +c ABSTRACT: The purpose of this subroutine is to get a modified +c first guess lat/lon position before searching for the +c minimum in the wind field. The reason for doing this is +c to better refine the guess and avoid picking up a wind +c wind minimum far away from the center. So, use the +c first guess position (and give it strong weighting), and +c then also use the fix positions for the current time +c (give the vorticity centers stronger weighting as well), +c and then take the average of these positions. +c +c INPUT: +c guesslon guess longitude for this forecast time +c guesslat guess latitude for this forecast time +c clon array with center longitude fixes for the various parms +c clat array with center latitude fixes for the various parms +c calcparm logical; tells whether or not a parm has a valid fix +c at this forecast hour +c ist index for current storm +c ifh index for current forecast hour +c maxstorm max # of storms that can be handled +c +c OUTPUT: +c uvgeslon contains modified guess longitude position at which to +c look for the wind minimum +c uvgeslat contains modified guess latitude position at which to +c look for the wind minimum +c igugret return code for this subroutine (0=normal) +c---- +c + USE set_max_parms; USE level_parms; USE error_parms + USE verbose_output + + logical(1) calcparm(maxtp,maxstorm) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real uvgeslon, uvgeslat + real guesslon,guesslat,degrees + integer gt345_ct,lt15_ct + + sumlon = 0.0 + sumlat = 0.0 + ict = 0 + gt345_ct = 0 + lt15_ct = 0 + +c NOTE: We need to be careful in this routine when averaging +c the longitudes together, in case we cross the greenwich +c meridian, because then we may be averaging 345+ lons with +c lons that are less than 15, giving incorrect results. +c Therefore, check for this, and if it occurs, add 360 onto +c any of the <15 lons (add it twice for those lons being +c counted twice (guesslon and the vorticity centers)). + +c Weight the uv guess position by counting the storm's guess +c position twice. + + sumlon = sumlon + 2.*guesslon + sumlat = sumlat + 2.*guesslat + ict = ict + 2 + + if (guesslon > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (guesslon < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct.... + endif + + do ip = 1,maxtp + if ((ip > 2 .and. ip < 7) .or. ip == 10) then + cycle ! because 3-6 are for 850 & 700 u & v and 10 is + ! for surface wind magnitude. + else + if (calcparm(ip,ist)) then + call calcdist (guesslon,guesslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + + if (dist < uverrmax) then +c +c Give the vorticity centers 2x weighting as well +c + if (ip == 1 .or. ip == 2 .or. ip == 11) then + sumlon = sumlon + 2.*clon(ist,ifh,ip) + sumlat = sumlat + 2.*clat(ist,ifh,ip) + ict = ict + 2 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct... + endif + else + sumlon = sumlon + clon(ist,ifh,ip) + sumlat = sumlat + clat(ist,ifh,ip) + ict = ict + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 ! Only 1 for non-zeta parms + endif + endif + + endif + + endif + endif + enddo +c + if (ict > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (sumlon) + uvgeslon = (sumlon + (360.*float(lt15_ct)))/ ict + else + uvgeslon = sumlon / ict + endif + if (uvgeslon >= 360.0) then + uvgeslon = uvgeslon - 360. + endif + uvgeslat = sumlat / ict + igugret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_guess, ict not > 0, ict= ',ict + print *,'!!! vmag center will not be calculated for this' + print *,'!!! storm -- at least not at this level' + print *,'!!! Storm number = ',ist + endif + + igugret = 91 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calc_vmag (xu,xv,imx,jmx,wspeed,icvret) +c +c ABSTRACT: This subroutine calculates the magnitude of the wind +c speed for an array of points, given real u and real v arrays. +c + real xu(imx,jmx),xv(imx,jmx),wspeed(imx,jmx) +c + do i=1,imx + do j=1,jmx + wspeed(i,j) = sqrt( xu(i,j)*xu(i,j) + xv(i,j)*xv(i,j) ) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_even (imxold,jmxold,xold + & ,imxnew,jmxnew,xnew,ibiret) +c +c ABSTRACT: This subroutine does a bilinear interpolation on a +c grid of evenly spaced data. Do NOT attempt to use this subroutine +c with data that are not evenly spaced or you will get unpredictable +c results. +c + real xold(imxold,jmxold), xnew(imxnew,jmxnew) +c +c +c --------------------------------------------------------------------- +c Latitude ----> | +c | +c L O e O e O e O e O | O: original point from input array +c o | +c n e 1 2 1 2 1 2 1 e | 1: interpolated, primary inter. pt +c g | +c i O 2 O 2 O 2 O 2 O | e: interpolated edge point +c t | +c u e 1 2 1 2 1 2 1 e | 2: interpolated, secondary inter. pt +c d | +c e O 2 O 2 O 2 O 2 O | Interpolations are done in the order +c | as indicated above; First, the input +c | e 1 2 1 2 1 2 1 e | 'O' pts are placed onto the new, +c | | larger grid. From that, the '1' pts +c | O 2 O 2 O 2 O 2 O | can be interpolated. Next, the edge +c | | (e) pts are interpolated using an +c v e 1 2 1 2 1 2 1 e | interpolation of two 'O' pts and one +c | '1' pt. Finally, the '2' pts are +c O e O e O e O e O | done using the 2 surrounding '0' and +c | '1' pts. Bilinear interpolation is +c | made incredibly easier by the fact +c | that the grid is evenly spaced. +c --------------------------------------------------------------------- +c NOTE: Remember that the arrays that are read in are indexed as +c (lon,lat), so that in the diagram above, pt (1,1) is at the upper +c left and pt (imax,jmax) is at the lower right, and each column is +c a new latitude and each row is a new longitude. +c +c ----------------------------------------------------------------- +c Put original (O) values from input array into new, expanded array +c ----------------------------------------------------------------- +c + do i=1,imxold + do j=1,jmxold + xnew(2*i-1,2*j-1) = xold(i,j) + enddo + enddo +c +c ---------------------------------------------- +c Interpolate to get primary interior (1) points +c ---------------------------------------------- +c + do i=1,imxold-1 + do j=1,jmxold-1 + xnew(2*i,2*j) = 0.25 * (xnew(2*i-1,2*j-1) + xnew(2*i+1,2*j-1) + & + xnew(2*i+1,2*j+1) + xnew(2*i-1,2*j+1)) + enddo + enddo +c +c --------------------------- +c Interpolate edge (e) points +c --------------------------- +c +c ... Northernmost 'e' points ... +c + j=1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,2)) + enddo +c +c ... Southernmost 'e' points ... +c + j = 2*jmxold - 1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,j-1)) + enddo +c +c ... Westernmost 'e' points ... +c + i=1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(2,2*j)) + enddo +c +c ... Easternmost 'e' points ... +c + i = 2*imxold - 1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(i-1,2*j)) + enddo +c +c ------------------------------------------------ +c Interpolate to get secondary interior (2) points +c ------------------------------------------------ +c + do j=2,2*jmxold-2 + istep = mod(j+1,2) + do i=istep+2,2*imxold-2,2 + xnew(i,j) = 0.25 * (xnew(i-1,j) + xnew(i,j-1) + xnew(i+1,j) + & + xnew(i,j+1)) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points +c + do i=1,ioldmax-1 + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int_lon (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. This particular +c routine is specifically used for interpolating +c longitudes, and it factors in the possibility of +c interpolating across the greenwich meridian. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points, and make the +c necessary adjustment when interpolating a longitude between, +c for example, 359.5 and 0.0. +c + do i=1,ioldmax-1 + if (xnew(2*i-1) > 350. .and. xnew(2*i+1) < 10.) then + xnew(2*i) = 0.5 * (xnew(2*i-1) + (360. + xnew(2*i+1))) + else + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + endif + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) +c +c ABSTRACT: This subroutine finds the maximum and mean zeta values +c at 850 & 700 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms + USE trkrparms; USE level_parms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07 ------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanzeta,dx,dy,re,ri,parmlon,parmlat + integer igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) + integer n,ix1,ix2,ilev,npts,imax,jmax,igzvret,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_zeta_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_zeta_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max zeta values at 850 and 700 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_zeta_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_zeta_loop: do n=1,2 + + gridpoint_maxmin = -99.0 + xmeanzeta = -99.0 + compflag = .true. + + select case (n) + case (1); ilev=850 ! For 850 mb + case (2); ilev=700 ! For 700 mb + end select + + if (zeta(ilonfix,jlatfix,n) > -9990.0) then + + ! ------------------------------------------- + ! We have valid zeta data for this level, so + ! we first call barnes now to get the mean zeta + ! surrounding our found center position. + ! ------------------------------------------- + + if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,n),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanzeta + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds + imeanzeta(n) = int ((xmeanzeta * 1e6) + 0.5) + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_zeta_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for zeta values will not be done.') + exit report_zeta_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + exit report_zeta_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) n,ilev,xmeanzeta,imeanzeta(n) + 621 format (1x,'+++ RPT_MEAN_ZETA: n= ',i2,' lev= ',i4 + & ,' xmeanzeta= ',f9.6,' imeanzeta (*1e6)= ',i8) + write (6,*) ' --- mean zeta raw = ',xmeanzeta + endif + + ! ----------------------------------------------- + ! Call fix_latlon_to_ij to get the nearest actual + ! raw (grid) zeta data value, not the mean value. + ! ----------------------------------------------- + + call fix_latlon_to_ij (imax,jmax,dx,dy + & ,zeta(1,1,n),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanzeta,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + igridzeta(n) = int ((gridpoint_maxmin * 1e6) + 0.5) + else + igridzeta(n) = -99 + endif + + if ( verb .ge. 3 ) then + write (6,623) n,ilev,gridpoint_maxmin,igridzeta(n),ifilret + 623 format (1x,'+++ RPT_GRID_ZETA: n= ',i2,' lev= ',i4 + & ,' grid zeta= ',f9.6,' igrid zeta (*1e6)= ',i8 + & ,' ifilret= ',i3) + write (6,*) ' --- grid zeta raw= ',gridpoint_maxmin + endif + + enddo report_zeta_loop + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get 850 & 700 zeta for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine find_maxmin (imax,jmax,dx,dy,cparm,fxy,maxmin,ist + & ,guesslon,guesslat,rlonv,rlatv,valid_pt,trkrinfo + & ,compflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,cmodel_type,ifmret) +c +c This routine finds the location (clon,clat) of and value of the +c the max or min of fxy in the vicinity of slon,slat. The value of +c the input flag maxmin determines whether to look for a max or a +c min value. The max/min is determined by finding the point which +c gives the max/min value of a single point barnes analysis of fxy +c with e-folding radius re (km) and influence radius ri (km). The +c initial search is restricted to a radius rads around the point +c (slon,slat) on a grid with lon,lat spacing dx and dy. The location +c is refined by reducing the spacing of the search grid by a factor +c of two, nhalf times. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c maxmin Char string indicating whether to search for a max or min +c ist Number of the storm being processed +c guesslon Guess longitude of the storm +c guesslat Guess latitude of the storm +c rlonv Array containing longitude values of input grid points +c rlatv Array containing latitude values of input grid points +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c trkrinfo derived type detailing user-specified grid info +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c +c INPUT/OUTPUT: +c compflag Logical; continue processing this storm or not (would be +c set to FALSE if, for example, the guess position is +c outside the domain of a regional grid) +c +c OUTPUT: +c ctlon Center longitude of storm found for this parameter +c ctlat Center latitude of storm found for this parameter +c xval Max or Min value found at the (ctlon,ctlat) +c ifmret Return code from this subroutine +c +c UPDATE DEC 2009: For the HFIP HRH testing, it was found that +c due to the very limited domain size of some of the models, the +c barnes scheme was allowing points close to the grid boundaries +c to erroneously be selected as the center point. We add in a +c buffer (grid_buffer) here to prevent this from occurring. + + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + character(*) maxmin,cparm,cmodel_type + logical(1) compflag, valid_pt(imax,jmax) + real fxy(imax,jmax),rlonv(imax),rlatv(jmax) + real ctlon,ctlat,degrees,dx,dy,guesslon,guesslat,xval + real rads,re,ri,dell,fmax,fmin,rlatt,rlont,dist,ftemp,ritmp + real vmag_latmax,vmag_latmin,vmag_lonmax,vmag_lonmin,retmp + real tlon,tlat,grid_buffer,temp_grid_minlon,temp_guesslon + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + integer imax,jmax,ist,bskip1,bskip2,iskip,ifmret,npts,maxvgrid + integer ibeg,iend,jbeg,jend,ilonfix,jlatfix,igiret,icount,iret + integer ibct,ibarnes_loopct,i,j,k,iix,jix,jvlatfix,ivlonfix + integer nhalf,icvpret + integer date_time(8) + character (len=10) big_ben(3) +c + ifmret = 0 + nhalf = 5 +c +c ----------------------------------------------------------- +c Set initial parms for use in find_maxmin. +c Different radii used for V magnitude than for other parms, +c see discussion in module radii for more details. +c + if (cparm == 'vmag') then + +c NOTE: The maxvgrid variable determines what size grid to send +c to subroutine barnes. e.g., maxvgrid = 8 means send an +c 8x8 grid; maxvgrid = 12 means send a 12x12 grid. For +c ultra-fine mesh grids (finer than 0.04 deg, or 1/25 deg), +c we expand to 12 in order to sample a few more points +c around each grid point. + + if ((dx+dy)/2. > 0.04) then + maxvgrid = 8 + else + maxvgrid = 12 + endif + + rads = rads_vmag; re = retrk_vmag; ri = ritrk_vmag + re = (float(maxvgrid)/4) * ((dx+dy)/2. * dtk) ! Basically, this +c sets re equal to half the distance from the gridpoint +c in question to the farthest point that will be +c sampled when the (maxvgrid x maxvgrid) grid is passed +c on to subroutine barnes. Thus, just ignore the +c parameter retrk_vmag, and use this instead. + else if ((dx+dy)/2. < 1.26 .and. (dx+dy)/2. >= 0.40) then + rads = rads_most; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.40 .and. (dx+dy)/2. >= 0.10) then + rads = rads_fine; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.10) then + rads = rads_hres; re = retrk_hres; ri = ritrk_most + else + rads = rads_coarse; re = retrk_coarse; ri = ritrk_coarse + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of find_maxmin, rads= ',rads,' re= ',re + & ,' ri= ',ri,' cparm= ',cparm,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+12; fmin = 1.0e+12 + ctlon = 0.0; ctlat = 0.0 + + if (npts == 0) npts = 1 + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if (dell > 0.20) then + bskip1 = 2 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 4 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 6 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 10 + bskip2 = 5 + else if (dell <= 0.03) then + bskip1 = 15 + bskip2 = 5 + endif + + if (cparm == 'vmag') then + bskip1 = 1 + bskip2 = 1 + endif + +c If input parm is vmag, we've already done the minimizing by +c interpolating to the fine mesh grid, so we'll simply send the +c bounds that were input to this subroutine to barnes +c as boundaries for the array to search. For all other parms, +c however, no minimizing has been done yet, so we need to call +c get_ij_bounds to set the boundaries for a much smaller grid that +c surrounds the storm (as opposed to having subroutine barnes +c search the entire global grid). + + if (cparm == 'vmag') then + + if ( verb .ge. 3 ) then + print *,'In find_maxmin, jmax= ',jmax,' imax= ',imax + endif + + ibeg=1; jbeg=1; iend=imax; jend=jmax + vmag_latmax = rlatv(1) ! N-most lat of vmag subgrid + vmag_latmin = rlatv(jmax) ! S-most lat of vmag subgrid + vmag_lonmin = rlonv(1) ! W-most lon of vmag subgrid + vmag_lonmax = rlonv(imax) ! E-most lon of vmag subgrid + + if ( verb .ge. 3 ) then + write (6,44) vmag_latmax,vmag_lonmin,360.-vmag_lonmin + & ,imax,jmax + write (6,46) vmag_latmin,vmag_lonmax,360.-vmag_lonmax + endif + + 44 format (' vmag_latmax= ',f8.3,' vmag_lonmin= ',f8.3 + & ,'E (',f8.3,'W) imax= ',i4,' jmax= ',i4) + 46 format (' vmag_latmin= ',f8.3,' vmag_lonmax= ',f8.3 + & ,'E (',f8.3,'W)') + + if (vmag_lonmin > 330. .and. vmag_lonmax < 30.) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: For a case of find_maxmin, our vmag' + print *,'!!! subgrid is straddling the GM. The code should' + print *,'!!! be able to handle this, but if strange errors' + print *,'!!! are occurring, check into the code either here' + print *,'!!! in find_maxmin or get_uv_ctr.' + print *,' ' + endif + endif + + npts = ceiling(rads/(dtk*dell)) + + else + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,guesslon,guesslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to ' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + ifmret = 92 + return + endif + + endif + +c +c --------------------------------------------------------------- +c + if ( verb .ge. 3 ) then + print *,' ' + write (6,39) guesslon,360.-guesslon,guesslat + 39 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + if (cparm == 'vmag') then + print *,'ilonfix= (unused) jlatfix= (unused)' + & ,' npts= ',npts + print *,'ilonfix and jlatfix are meaningless for computing' + print *,'vmag, so ignore the large values you see for them.' + else + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + endif + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + + if ( verb .ge. 3 ) then + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: find_maxmin 1 ',i2.2,':',i2.2,':',i2.2) + endif + + ibct=0 + ibarnes_loopct = 0 + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (guesslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = guesslon - 360. + else + temp_guesslon = guesslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = guesslon + endif + + jix = 0 + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + jloop: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = guesslat + dell*float(j) + + iix = 0 + +c vlat(jix) = rlatt + + iloop: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c if (cparm == 'vmag') then +c print *,' ' +c print '(a16,i6,a4,i6,2(a8,f8.3),a12,f8.3)' +c & ,'in find_max, i= ',i +c & ,' j= ',j,' rlatt= ',rlatt,' rlont= ',rlont +c & ,' 360-rlont= ',360.-rlont +c endif + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT: icvpret= ',icvpret + endif + + cycle iloop + endif + + call calcdist(rlont,rlatt,temp_guesslon,guesslat,dist,degrees) + if (dist .gt. rads) cycle iloop + + if (cparm == 'vmag') then + +c This next bit of code gets the ij coordinates for an 8x8 +c box around the current point under consideration. These ij +c coordinates are sent to barnes so that barnes only loops +c 64 times, as opposed to nearly 10,000 if the whole 97x97 +c array were sent. So, fix rlatt to the grid point just +c northward of rlatt and fix rlont to the grid point just +c eastward of rlont. Note that this makes for a modified +c barnes analysis in that we're sort of specifying ahead of +c time exactly which grid points will be included and we'll +c be excluding some points that would be near the periphery +c of each (rlont,rlatt)'s range, but as long as we're consis- +c tent and do it this way for each point, it's well worth the +c trade-off in cpu time. Parameter maxvgrid determines what +c size array to send to barnes (maxvgrid=8 means 8x8) + + jvlatfix = int((vmag_latmax - rlatt)/dy + 1.) + ivlonfix = int((rlont - temp_grid_minlon)/dx + 2.) +c ivlonfix = int((rlont - vmag_lonmin)/dx + 2.) + + ibeg = ivlonfix - (maxvgrid/2) + iend = ivlonfix + (maxvgrid/2 - 1) + jbeg = jvlatfix - (maxvgrid/2 - 1) + jend = jvlatfix + (maxvgrid/2) + + if (ibeg < 1 .or. jbeg < 1 .or. + & iend > imax .or. jend > jmax) then + + ! DO NOT quit if we find a boundary outside the grid + ! bounds. Rather, just set the J violating bound(s) to + ! the min or max limit, and for I bounds, allow the + ! program to continue down to subsequent code below, + ! provided it's a global grid. + +c print *,'!!! ' +c print *,'!!! Before vmag adjustments, boundaries are: ' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt,' dx= ',dx +c print *,'!!! temp_grid_minlon= ',temp_grid_minlon +c print *,'!!! vmag_latmax= ',vmag_latmax +c print *,'!!! ivlonfix = ',ivlonfix,' jvlatfix = ',jvlatfix +c print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax +c print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Vmag will not be computed for' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Vmag will not be computed for ' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,'!!! ' + print *,'!!! *AFTER* vmag adjustments, boundaries are: ' + print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax + print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + endif + + endif + + endif + + if (cparm == 'vmag') then + ri = re * 3 +c print '(a36,f10.4,a6,f10.4)' +c & ,' + before call to vmag barnes, re= ',re,' ri= ',ri + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,bskip1,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes...' + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After 1st findmax loop, # calls to barnes = ',ibct + print *,'Total # of barnes loop iterations = ',ibarnes_loopct + endif + +c + 55 format ('i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ',f7.3 + & ,' barnval= ',f11.5) + 56 format ('k= ',i3,' i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ' + & ,f7.3,' barnval= ',f11.5) + + if (ctlon < 0.) then + ! We have grid-wrapped to find the ctlon, which was found to be + ! < 0, so for reporting purposes and for the start of the next + ! loop, set ctlon to positive degress east. + ctlon = ctlon + 360. + endif + + if (cparm == 'zeta') then + + if ( verb .ge. 3 ) then + print *,'!!! Zeta check, fmax= ',fmax,' fmin= ',fmin + write (6,61) 360.-ctlon,ctlat,fmax*100000.,fmin*100000. + endif + + else + + if ( verb .ge. 3 ) then + write (6,63) 360.-ctlon,ctlat,fmax,fmin + endif + + endif + 61 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 63 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax = ',e16.3,' fmin = ',e16.3) + 111 format (i2,' rlont= ',f7.2,'W rlatt= ',f7.2,' zeta= ',f13.8) + +c Through interpolation, the grid for vmag has already been +c minimized considerably, we don't need to go through the 2nd part +c of this subroutine, which halves the grid spacing. + + if (nhalf < 1 .or. cparm == 'vmag') then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c ------------------------------------------------------------- +c If the grid spacing is +c fine enough (I've chosen 0.2-deg as a min threshold), there is +c no need to halve the grid more than 3 times, as halving a +c 0.2-deg grid 3 times gives a resolution of 0.025-deg (2.7 km), +c or a max error in the position estimate of 2.7/2 = 1.35 km. + + if ((dx+dy)/2. <= 0.2) then + if ((dx+dy)/2. <= 0.05) then + nhalf = 1 + else + nhalf = 2 + endif + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +ctpm npts = 3 + npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only +c do this once for this grid-refinement (even though the grid is +c redefined 3 times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to get_ij_bounds' + print *,'!!! just before nhalf loop. Stopping processing' + print *,'!!! for storm number ',ist + endif + + ifmret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + + if ( verb .ge. 3 ) then + print *,' ' + endif + + if ((dx+dy)/2. <= 1.25 .and. ri >= 300 .and. re >= 150) then + retmp = re + ritmp = ri + re = re * 0.5 + ri = ri * 0.5 + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re has been reduced' + print *,'from ',retmp,' to ',re,', and ri has been reduced ' + print *,'from ',ritmp,' to ',ri + endif + + else + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re and ri have NOT ' + print *,'been changed. re = ',re,' ri = ',ri + endif + + endif + + ibct=0 + ibarnes_loopct = 0 + do k=1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: find_maxmin kloop, k= ',i2,' ',i2.2,':' + & ,i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat + fmax = -1.0e+15; fmin = 1.0e+15 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'find_maxmin nhalf loop, cparm= ',cparm,' k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,iskip,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes, k= ',k + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop2 + enddo jloop2 + + if ( verb .ge. 3 ) then + if (cparm == 'zeta') then + write (6,71) k,360.-ctlon,ctlat,fmax*100000.,fmin*100000. + else + write (6,73) k,360.-ctlon,ctlat,fmax,fmin + endif + endif + + enddo + + 71 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 73 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax = ',e16.3,' fmin = ',e16.3) + + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ppp after 2nd findmax loop, # calls to barnes = ' + & ,ibct + print *,'ppp Total # of barnes loop iterations = ' + & ,ibarnes_loopct + endif + + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine barnes(flon,flat,rlon,rlat,iimax,jjmax,iibeg,jjbeg + & ,iiend,jjend,fxy,defined_pt,bskip,re,ri,favg,icount,ctype + & ,trkrinfo,iret) +c +c ABSTRACT: This routine performs a single-pass barnes anaylsis +c of fxy at the point (flon,flat). The e-folding radius (km) +c and influence radius (km) are re and ri, respectively. +c +c NOTE: The input grid that is searched in this subroutine is most +c likely NOT the model's full, original grid. Instead, a smaller +c subgrid of the original grid is searched. The upper left and +c lower right grid point indices are passed into this subroutine +c (iibeg, jjbeg, iiend, jjend) for this subgrid. These indices are +c determined in the subroutine get_ij_bounds, and the purpose of +c doing it this way is to limit the number of points for which the +c subroutine has to calculate distances (for a global 1 deg grid, +c the number of loop iterations is reduced from 65160 to somewhere +c around 600). +c +c NOTE: This subroutine will immediately exit with a non-zero +c return code if it tries to access a grid point that does not have +c valid data. This would happen in the case of a regional grid, if +c you try to access a point near the edge of the grid (remember that +c because of the interpolation for the regional grids, there will be +c areas around the edges that have no valid data). +c +c INPUT: +c flon Lon value for center point about which barnes anl is done +c flat Lat value for center point about which barnes anl is done +c rlon Array of lon values for each grid point +c rlat Array of lat values for each grid point +c iimax Max number of pts in x-direction on input grid +c jjmax Max number of pts in y-direction on input grid +c iibeg i index for grid point to start barnes anlysis (upp left) +c jjbeg j index for grid point to start barnes anlysis (upp left) +c iiend i index for last grid point in barnes anlysis (low right) +c jjend j index for last grid point in barnes anlysis (low right) +c fxy Real array of data on which to perform barnes analysis +c defined_pt Logical; bitmap array used for regional grids +c bskip integer to indicate number of grid points to skip during +c a barnes loop, in order to speed processing +c re input e-folding radius for barnes analysis +c ri input influence radius for searching for min/max +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, in +c this barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c favg Average value about the point (flon,flat) +c iret Return code from this subroutine +c + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real fxy(iimax,jjmax), rlon(iimax), rlat(jjmax) + real degrees + integer bskip + logical(1) defined_pt(iimax,jjmax) + character(*) ctype + +c -------------------------- + + res = re*re + wts = 0.0 + favg = 0.0 + + icount = 0 + + do jix=jjbeg,jjend,bskip + do iix=iibeg,iiend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > iimax) then + if (trkrinfo%gridtype == 'global') then + i = iix - iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i,' imax= ',iimax + print *,' ' + endif + + stop 97 + endif + endif + + icount = icount + 1 + + call calcdist(flon,flat,rlon(i),rlat(j),dist,degrees) + + if (dist .gt. ri) cycle + + if (defined_pt(i,j)) then + if (fxy(i,j) >-999.01 .and. fxy(i,j) <-998.99) then + ! This is a patch. Even though this (i,j) is a valid + ! point, its zeta value has been set to -999 because a + ! neighboring point in subroutine rvcal was found + ! to be out of the grid boundaries. + cycle + endif + wt = exp(-1.0*dist*dist/res) + wts = wts + wt + favg = favg + wt*fxy(i,j) + else + if (ctype == 'vitals') then + continue + else +carw print *,' ' +carw print *,'!!! UNDEFINED PT OUTSIDE OF GRID IN BARNES....' +carw print *,'!!! i= ',i,' j= ',j +carw print *,'!!! flon= ',flon,' flat= ',flat +carw print *,'!!! rlon= ',rlon(i),' rlat= ',rlat(j) +carw print *,'!!! re= ',re,' ri= ',ri +carw print *,'!!! EXITING BARNES....' +carw print *,' ' +carw iret = 95 +carw return + endif + endif + + enddo + enddo + + if (wts > 1.0E-5) then + favg = favg/wts + else + favg = 0.0 + endif + iret = 0 +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,rglatmax,rglatmin,rglonmax,rglonmin,geslon,geslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) +c +c ----------------------------------------------------------- +c ABSTRACT: This subroutine figures out, based on ri, dx and dy and +c the guess latitude and longitude positions, the farthest reaching +c grid points that are searchable by an analysis subroutine. The +c purpose is to return indices for a subgrid that is much smaller +c than the original, full grid. This smaller subgrid can then be +c passed to a subsequent analysis or interpolation subroutine, and +c work can be done on this smaller array. This can help save time, +c especially in the barnes analysis subroutine, as work will only +c be done on, say, a 20 x 20 array (400 pts) instead of on a +c 360 x 181 array (65160 pts). It's crucial, however, to make sure +c that the ibeg, jbeg, iend and jend are extended far enough out to +c fully encompass any search that would be done. Below is a +c diagram showing the different grids.... +c +c Full Global or Regional Model Grid (Grid F) -----------> +c ---------------------------------------------------------------- +c | | (ibeg,jbeg) | +c | | x = ij position that the | (Grid R) | +c | | geslat/geslon is fixed to. ._______________. | +c | | | | | +c | | Even though only the points | (Grid B) | | +c | | within Grid B will be checked | . . . . k | | +c v | later on for a max/min (in the | . . . . . | | +c | case of a subsequent call to | . . x . e | | +c | find_maxmin), the barnes anal- | . . . . . | | +c | ysis will include all pts sur- | . . . . . | | +c | rounding these Grid B points | | | +c | that are within a radius of ri. ._______________. | +c | So in the case of pt. k, that ri | +c | radius may extend all the way to the Grid R | | +c | boundary, thus we need to include those (iend,jend) | +c | points within our ibeg-jbeg-iend-jend bounds. | +c | | +c ---------------------------------------------------------------- +c +c Remember that the grids we deal with start north and increase +c south, so the northernmost latitude on the input grid will have +c a j index of 1. +c +c INPUT: +c npts Num pts from x to edge of max/min search grid (Grid B) +c (i.e., You define the size of Grid B by the value of +c npts that you pass into this subroutine). +c nhalf Number of times the grid spacing will be halved +c ri Radius of influence (for use in barnes analysis) +c imax Number of points in x-direction on original grid +c jmax Number of points in y-direction on original grid +c dx Input grid spacing in i-direction on original grid +c dy Input grid spacing in j-direction on original grid +c rglatmax Value of northern-most latitude on original grid +c rglatmin Value of southern-most latitude on original grid +c rglonmax Value of eastern-most longitude on original grid +c rglonmin Value of western-most longitude on original grid +c geslat Value of latitude of guess position of storm +c geslon Value of longitude of guess position of storm +c +c OUTPUT: +c ilonfix i index on full, input grid that storm is fixed to +c jlatfix j index on full, input grid that storm is fixed to +c ibeg i index for top left of sub-array (Grid R) of input grid +c iend i index for bot right of sub-array (Grid R) of input grid +c jbeg j index for top left of sub-array (Grid R) of input grid +c jend j index for bot right of sub-array (Grid R) of input grid +c igiret Return code from this subroutine +c + USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + real tmpangle +c + igiret = 0 +c +c -------------------------------------- +c GET BEGINNING AND ENDING J POINTS.... +c +c (1) Calculate number of searchable, max/min pts, that is, the pts +c from x to the edge of Grid B. +c (2) Calculate number of pts beyond the last search point in Grid +c B, but are within the bounds of Grid R and thus can be +c included in the barnes analysis. +c (3) Add (1) and (2) to get the max number of pts to subtract/add +c to x to get jbeg and jend. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Beginning of get_ij_bounds...' + print *,' geslat= ',geslat,' geslon= ',geslon + print *,' ' + endif + + +c If nhalf > 0: This occurs in the case of a call from fmax, when +c the grid spacing is halved nhalf times. In this case, we have to +c do extra work to figure out the maximum possible grid point. For +c this case: +c jhlatpts = # of grid pts to last possible search pt (from x to +c edge of Grid B in above diagram), plus a cushion. +c jripts = # of searchable grid points within radius ri of last +c possible search pt (num pts between edge of Grid B +c and edge of Grid R in above diagram), plus a cushion +c jbmaxlatpts = # of pts (in j direction) from x to the edge of +c Grid R to include in this subgrid. +c +c If nhalf = 0: In this case, the grid spacing will not be reduced, +c so the number of pts in j direction from x to the edge of Grid +c B will be the input parameter npts, and just multiply it by 2, +c and add 2 for a cushion to get jmaxlatpts. Typically, this sub +c is called from find_maxmin, and in that sub, the first time that +c this sub is called, nhalf will = 0. Then, after a first-shot +c center is found, the grid spacing will be cut in order to rerun +c barnes on a smaller grid, and that's when nhalf will be sent +c here as 3. +c + if (nhalf > 0) then + rdeg = 0.0 + do i = 1,nhalf + rdeg = rdeg + float(npts) * (1./(float(i)*2)) * (dx+dy)/2 + enddo + jhlatpts = ceiling(rdeg/dy) + 1 + jripts = ceiling((ri + 1.)/(dtk*dx)) + 1 + jbmaxlatpts = jhlatpts + jripts + else + jbmaxlatpts = npts * 2 + 2 + endif +c +c +c Roughly fix geslat to the grid point just poleward of geslat. +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' +++ Near top of get_ij_bounds, ' + print *,' +++ geslat= ',geslat,' geslon= ',geslon + print *,' +++ rglatmax= ',rglatmax,' rglatmin= ',rglatmin + print *,' +++ rglonmax= ',rglonmax,' rglonmin= ',rglonmin + print *,' +++ imax= ',imax,' jmax= ',jmax + print *,' +++ dx= ',dx,' dy= ',dy,' nhalf= ',nhalf + print *,' +++ npts= ',npts + if(nhalf>0) then + print *,' +++ jhlatpts= ',jhlatpts,' jripts= ',jripts + else + print *,' +++ nhalf<=0 so jhlatpts and jripts unused' + endif + print *,' +++ jbmaxlatpts= ',jbmaxlatpts + endif + + if (geslat >= 0.0) then + jlatfix = int((rglatmax - geslat)/dy + 1.) + else + jlatfix = ceiling((rglatmax - geslat)/dy + 1.) + endif + + if ( verb .ge. 3 ) then + print *,' +++ jlatfix= ',jlatfix + endif + + jbeg = jlatfix - jbmaxlatpts + jend = jlatfix + jbmaxlatpts + if (jbeg > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jbeg > jmax' + print *,'!!! jbeg = ',jbeg,' jmax= ',jmax + endif + + igiret = igiret + 1 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jend < 1, jend = ',jend + endif + + igiret = igiret + 1 + return + endif + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' +++ jbeg= ',jbeg,' jend= ',jend + endif + + ! If using a global grid, avoid using the pole points, or else + ! you'll get a cosfac = 0 and then a divide by zero!!! + + if (jend == jmax .and. rglatmin == -90.0) then + jend = jmax - 2 + endif + if (jbeg == 1 .and. rglatmax == 90.0) then + jbeg = 3 + endif + +c ----------------------------------------- +c NOW GET BEGINNING AND ENDING I POINTS.... +c +c Using the map factor (cos lat), figure out, based on ri, the +c max distance beyond the last search point in x-direction (in +c degrees) that could be searched at this guess latitude (geslat) +c (i.e., in the diagram above, the max num pts from pt. e eastward +c to the edge of Grid R). Calculate how many grid points that is, +c add 2 to it for a cushion, & add the number of points (npts) +c within the defined search grid (Grid B) to get ibmaxlonpts. +c +c April, 2007: A min statement was put on the calculation to +c derive dlon, since with that cosine in there, the values of +c of dlon could get pretty ridiculous as you approach the poles. +c Also, the cosine factor (cosfac) used to be computed at the +c most poleward latitude possible given the jend here. For +c similar concerns with cosines near the poles, I've scrapped +c this to instead compute the cosine factor at the input +c guess latitude. - tpm + + cosfac = cos (geslat * dtr) + tmpangle = cosfac * dtk + dlon = min((ri /tmpangle ),20.0) +c dlon = min((ri / (cosfac * dtk)),20.0) +c + if (nhalf > 0) then + ihlonpts = ceiling(rdeg/dx) + 1 + ibmaxlonpts = ihlonpts + ceiling(dlon/dx) + 2 + else + ibmaxlonpts = npts + ceiling(dlon/dx) + 2 + endif + + if ( verb .ge. 3 ) then + if(nhalf>0) then + print *,' +++ rdeg= ',rdeg,' ri= ',ri,' cosfac= ',cosfac + print *,' +++ dtr= ',dtr,' dtk= ',dtk,' dlon= ',dlon + else + print*,' +++ nhalf<=0 so rdeg,ri,cosfac,dtr,dtk,dlon unused' + endif + print *,' +++ ibmaxlonpts= ',ibmaxlonpts,' dx= ',dx,' dy= ',dy + endif + +c Roughly fix geslon to the grid point just EASTward of geslon. + + ilonfix = int((geslon - rglonmin)/dx + 2.) + + ibeg = ilonfix - ibmaxlonpts + iend = ilonfix + ibmaxlonpts + + if ( verb .ge. 3 ) then + print *,' +++ (orig) ilonfix= ',ilonfix + print *,' +++ (orig) ibeg= ',ibeg,' iend= ',iend + print *,' +++ ' + endif + + if (ibeg > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 1 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, ibeg > imax' + print *,'!!! for a non-global grid' + print *,'!!! ibeg = ',ibeg,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + ! For a regional grid, just set iend to be imax + iend = imax + endif + endif + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + ! For a regional grid, just set ibeg to be 1 + ibeg = 1 + endif + endif + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + + if ( verb .ge. 3 ) then + print *,'!!! ERROR in get_ij_bounds, iend < 1' + print *,'!!! for a non-global grid' + print *,'!!! iend = ',iend,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine check_bounds (guesslon,guesslat,ist,ifh,trkrinfo + & ,icbret) +c +c ABSTRACT: This subroutine checks to make sure that the requested +c storm is in fact within the model's grid boundaries; +c this is only a concern for the regional models. +c + USE def_vitals; USE grid_bounds; USE set_max_parms + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + if (trkrinfo%gridtype == 'regional') then + if (guesslon > glonmax .or. guesslon < glonmin .or. + & guesslat > glatmax .or. guesslat < glatmin) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is outside of grid' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + goto 125 + else + icbret = 0 + endif + endif + + ! We have encountered problems with global grids where we + ! continue tracking almost the whole way to the pole. While + ! that's nice to do that, it creates problems for array + ! indices, especially in subroutine getradii. So we will cut + ! our losses and eliminate tracking of storms within + ! 5 degrees of the pole for global grids. + + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'global')then + if (guesslat > 85.0 .or. guesslat < -85.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is too close to the' + print *,'!!! N or S Pole for global tracking.' + print *,'!!! STOPPING TRACKING FOR THIS STORM DUE TO POLE' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + else + icbret = 0 + endif + endif + + 125 continue +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calcdist(rlonb,rlatb,rlonc,rlatc,xdist,degrees) +c +c ABSTRACT: This subroutine computes the distance between two +c lat/lon points by using spherical coordinates to +c calculate the great circle distance between the points. +c Figure out the angle (a) between pt.B and pt.C, +c N. Pole then figure out how much of a % of a great +c x circle distance that angle represents. +c / \ +c b/ \ cos(a) = (cos b)(cos c) + (sin b)(sin c)(cos A) +c / \ +c pt./<--A-->\c NOTE: The latitude arguments passed to the +c B / \ subr are the actual lat vals, but in +c \ the calculation we use 90-lat. +c a \ +c \pt. NOTE: You may get strange results if you: +c C (1) use positive values for SH lats AND +c you try computing distances across the +c equator, or (2) use lon values of 0 to +c -180 for WH lons AND you try computing +c distances across the 180E meridian. +c +c NOTE: In the diagram above, (a) is the angle between pt. B and +c pt. C (with pt. x as the vertex), and (A) is the difference in +c longitude (in degrees, absolute value) between pt. B and pt. C. +c +c !!! NOTE !!! -- THE PARAMETER ecircum IS DEFINED (AS OF THE +c ORIGINAL WRITING OF THIS SYSTEM) IN KM, NOT M, SO BE AWARE THAT +c THE DISTANCE RETURNED FROM THIS SUBROUTINE IS ALSO IN KM. +c + USE trig_vals + + real degrees +c + if (rlatb < 0.0 .or. rlatc < 0.0) then + pole = -90. + else + pole = 90. + endif +c + distlatb = (pole - rlatb) * dtr + distlatc = (pole - rlatc) * dtr + difflon = abs( (rlonb - rlonc)*dtr ) +c + cosanga = ( cos(distlatb) * cos(distlatc) + + & sin(distlatb) * sin(distlatc) * cos(difflon)) + +c This next check of cosanga is needed since I have had ACOS crash +c when calculating the distance between 2 identical points (should +c = 0), but the input for ACOS was just slightly over 1 +c (e.g., 1.00000000007), due to (I'm guessing) rounding errors. + + if (cosanga > 1.0) then + cosanga = 1.0 + endif +cPENG added bug fixed on 04/28/2016-------------------------- + if (cosanga < -1.0) then + cosanga = -1.0 + endif +cPENG added on 04/28/2016-------------------------- + + degrees = acos(cosanga) / dtr + circ_fract = degrees / 360. + xdist = circ_fract * ecircum +c +c NOTE: whether this subroutine returns the value of the distance +c in km or m depends on the scale of the parameter ecircum. +c At the original writing of this subroutine (7/97), ecircum +c was given in km. +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine subtract_cor (imax,jmax,dy,level) +c +c ABSTRACT: This subroutine subtracts out the coriolis parameter +c from the vorticity values. It is needed because at the original +c writing of this system, all of the forecast centers who included +c vorticity included only absolute vorticity. +c + USE tracked_parms; USE trig_vals; USE grid_bounds + + implicit none + + integer :: i,j,imax,jmax,level + real :: dy,coriolis,rlat +c + do j=1,jmax + rlat = glatmax - ((j-1) * dy) + coriolis = 2. * omega * sin(rlat*dtr) + do i=1,imax + zeta(i,j,level) = zeta(i,j,level) - coriolis + enddo + enddo +c + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_grib_file_name (ifh,gfilename,ifilename) + +c ABSTRACT: This subroutine uses various input regarding the model +c and forecast hour and generates the name of the input grib file +c for this particular forecast hour. Remember that the lead time +c is in minutes and that 5 spaces must be reserved for the lead +c time (e.g., f00360). File name should be something that looks +c like either, e.g., "gfdl.6thdeg.katrina12l.2005082818.f00720", +c or "gfdl.6thdeg.2005082818.f00720" (the part in there with the +c storm name & ID is optional). The grib index file name should +c be exactly the same as the grib data file itself, but with the +c character string ".ix" added onto the end of the name. +c +c NOTE: Array iftotalmins is brought in via module tracked_parms. +c +C INPUT: +c ifh integer array index for current lead time +c +c OUTPUT: +c gfilename GRIB file name +c ifilename GRIB index file name + + USE gfilename_info; USE tracked_parms; USE atcf + USE verbose_output + + implicit none + + character(*) gfilename,ifilename + character cfmin*5,cymdh*10 + integer ifh,nlen1,nlen2,nlen3,nlen4,nlen5 + +c Convert integer minutes to 5-position character, with +c leading zeroes, and convert 10-digit integer date into +c 10-position character. Then trim the various input variables +c and combine all into the file name. + + write (cfmin,'(i5.5)') iftotalmins(ifh) + write (cymdh,'(i10.10)') atcfymdh + + nlen1 = len_trim(gmodname) + gfilename = trim(gmodname(1:nlen1)) + + nlen2 = len_trim(rundescr) + + gfilename = trim(gfilename(1:nlen1))//'.'//trim(rundescr(1:nlen2)) + + nlen3 = len_trim(atcfdescr) + nlen4 = len_trim(gfilename) + +c If an extension to the name with the ATCF or storm name descriptor +c was included, then add it to the name now. Otherwise, just add +c the starting date and the lead time in minutes. + + if (nlen3 > 0) then + gfilename = trim(gfilename(1:nlen4))//'.' + & //trim(atcfdescr(1:nlen3))//'.'//cymdh//'.f'//cfmin + else + gfilename = trim(gfilename(1:nlen4))//'.'//cymdh//'.f'//cfmin + endif + +c Create the name for the grib index file, which is just the name of +c the grib file, with "ix" added to the end of it. + + nlen5 = len_trim(gfilename) + ifilename = trim(gfilename(1:nlen5))//'.ix' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,72) 'gfilename',gfilename + write (6,72) 'ifilename',ifilename + endif + + 72 format (1x,'In get_grib_file_name, file name for ',a9 + & ,' is ',a120) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) +c +c ABSTRACT: This subroutine reads the input GRIB file for the +c tracked parameters. It then calls subroutines to convert the +c data from a 1-d array into a 2-d array if the read was successful. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature (I jerry-rigged this by storing +c the data as being at the 401 mb level.) +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +cPENG----2018-06-07 ------------------------ +c 18. Ushear 200-850hPa (501hPa level) +c 19. 500hPa relative humidity +c +c INPUT: +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c inp of a derived type, contains user-input info +c lugb integer unit number of input grib file +c lugi integer unit number of input grib index file +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE verbose_output; USE params; USE grib_mod; USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + type (datecard) inp + type (gribfield) :: gfld,prevfld,holdgfld +c + integer, parameter :: jf=40000000 +c integer, parameter :: nreadparms=17 +cPENG----2018-06-07 ------------------------ + integer, parameter :: nreadparms=19 + + real, allocatable :: f(:) + real :: dmin,dmax,firstval,lastval + logical(1), allocatable :: lb(:) + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1) file_open + logical :: unpack=.true. + logical :: open_grb=.false. + character*1 :: lbrdflag + character*8 :: chparm(nreadparms) + CHARACTER(len=8) :: pabbrev + character (len=10) big_ben(3) + integer date_time(8) + integer,dimension(200) :: jids,jpdt,jgdt + integer :: listsec1(13), enable_timing + integer, intent(in) :: imax,jmax + integer igparm(nreadparms),iglev(nreadparms) + integer iglevtyp(nreadparms) + integer ig2_parm_cat(nreadparms),ig2_parm_num(nreadparms) +cPENG----2018-06-07 ------------------------ + integer ig2_parm_cat_cmcd(nreadparms) + & ,ig2_parm_num_cmcd(nreadparms) + +c integer ig2_lev_val(nreadparms),ig2_lev_typ(nreadparms) +cPENG----2018-06-07 ------------------------ + integer ig2_lev_10(nreadparms) + integer ig2_lev_11(nreadparms),ig2_lev_12(nreadparms) + +cPENG 04/18/2018 for CMC Det. and CMC ensemble data +cPENG----2018-06-07 ------------------------ +c integer ig2_lev_11_cmc(nreadparms),ig2_lev_val_cmc(nreadparms) +c integer ig2_lev_11_cmcd(nreadparms),ig2_lev_val_cmcd(nreadparms) + integer ig2_lev_11_cmc(nreadparms),ig2_lev_12_cmc(nreadparms) + integer ig2_lev_11_cmcd(nreadparms),ig2_lev_12_cmcd(nreadparms) + + integer cpsig2_parm_cat(nlevs_cps),cpsig2_parm_num(nlevs_cps) +c integer cpsig2_lev_typ(nlevs_cps),cpsig2_lev_val(nlevs_cps) +cPENG-04/18/2018 for CMC Det. and CMC ensemble data + integer cpsig2_lev_10(nlevs_cps) + integer cpsig2_lev_11(nlevs_cps),cpsig2_lev_12(nlevs_cps) + + integer ec_igparm(nreadparms),ec_iglev(nreadparms) + integer ec_iglevtyp(nreadparms) + integer cpsgparm(nlevs_cps),cpsglev(nlevs_cps) + integer cpsglevtyp(nlevs_cps) + integer ec_cpsgparm(nlevs_cps) + integer jpds(200),jgds(200),kpds(200),kgds(200) + integer igvret,ifa,ila,ip,ifh,i,j,k,kj,iret,kf,lugb,lugi + integer jskp,jdisc,np + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer pdt_4p0_vert_level,pdt_4p0_vtime + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 +c + lbrdflag = 'n' + enable_timing=trkrinfo%enable_timing +c The following data statements contain the parameters that will be +c used to search the grib files. The first 9 parameters will all be +c used to locate the storm position. The last 4 parameters (500 mb +c u- and v-components and 10 m u- and v- components) will not be +c used for tracking, but only for helping to estimate the next first +c guess position (500 mb winds) and for estimating the max near- +c surface wind speeds in the vicinity of the storm (10 m winds). +c +c ** NOTE: iglevtyp(12 & 13) and iglev(12 & 13) are initialized to +c 0 just to satisfy the IBM xlf compiler, which barks about +c there being too few initial values in the list when I +c only had 11 values there -- even though the real +c initialization for these variables is done just about +c 10 lines below. +c +c ** NOTE: The new ECMWF hi-res data uses the ECMWF GRIB parameter +c ID table, which has different values than the NCEP +c table. Therefore, we needed to add the variables and +c data values for ec_igparm, ec_iglevtyp and ec_iglev. +c +c July 2007: Read statements added for GP height for cyclone +c phase space (CPS) algorithm. + +c data igparm /41,41,33,34,33,34,7,7,1,33,34,33,34,11,7,7,81/ +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7,81/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 +c & ,100,100,100,1/ +c data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500,401 +c & ,500,200,0/ +cPENG----2018-06-07 ------------------------ + data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7,81 + & ,33,52/ + data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 + & ,100,100,100,1,100,100/ + data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500,401 + & ,500,200,0,501,500/ + + data cpsgparm /13*7/ + data ec_cpsgparm /13*156/ + data cpsglevtyp /13*100/ + data cpsglev /900,850,800,750,700,650,600,550,500,450,400 + & ,350,300/ + +c data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 +c & ,131,132,130,156,156,999/ +c data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 +c & ,100,100,100,999/ +c data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 +c & ,401,500,200,999/ +c +c data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' +c & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' +c & ,'vgrid','temp','gphgt','gphgt','lmask'/ +cPENG----2018-06-07 ------------------------ + data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 + & ,131,132,130,156,156,999,131,157/ + data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 + & ,100,100,100,999,100,100/ + data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 + & ,401,500,200,999,501,500/ + + data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' + & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' + & ,'vgrid','temp','gphgt','gphgt','lmask' + & ,'ushe','rhum'/ + +c data ig2_parm_cat /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2/ +c data ig2_parm_num /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8/ +c data ig2_lev_typ /100,100,100,100,100,100,100,100,101,103,103 +c & ,100,100,100,100,100,-9999/ +c data ig2_lev_val /850,700,850,850,700,700,850,700,0,10,10,500,500 +c & ,401,500,200,-9999/ +cPENG----2018-06-07 ------------------------ + data ig2_parm_cat /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2,2,1/ + data ig2_parm_num /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8,2,1/ + data ig2_parm_cat_cmcd /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2,2,1/ + data ig2_parm_num_cmcd /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8,2,1/ + + data ig2_lev_10 /100,100,100,100,100,100,100,100,101,103,103 + & ,100,100,100,100,100,-9999,100,100/ + +cPENG 04/18/2018 for CMC Det. and CMC ensemble data +c data ig2_lev_11_cmc /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 +c & ,-4,-4,0/ +c data ig2_lev_val_cmc/85,7,85,85,7,7,85,7,0,10,10,5,5,40100 +c & ,5,2,-9999/ +c data ig2_lev_11_cmcd /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 +c & ,-4,-4,0/ +c data ig2_lev_val_cmcd /85,7,85,85,7,7,85,7,0,10,10,5,5,40100 +c & ,5,2,-9999/ +cPENG----2018-06-07 ------------------------ +cPENG---for gfs/gefs NEVGEM/FENS ensemble---------- + data ig2_lev_11 /0,0,0,0,0,0,0,0,0,0,0 + & ,0,0,0,0,0,0,0,0/ + data ig2_lev_12 /85000,70000,85000,85000,70000,70000,85000,70000 + & ,0,10,10,50000,50000 + & ,40100,50000,20000,-9999,50100,50000/ +cPENG---for CMC ensemble--------------------- + data ig2_lev_11_cmc /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0,0,-4/ + data ig2_lev_12_cmc/85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999,50100,5/ +cPENG---for CMC deterministic--------- + data ig2_lev_11_cmcd /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0,0,0/ + data ig2_lev_12_cmcd /85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999,50100,50000/ + + + data cpsig2_parm_cat /13*3/ + data cpsig2_parm_num /13*5/ +c data cpsig2_lev_typ /13*100/ +c data cpsig2_lev_val /900,850,800,750,700,650,600,550,500,450,400 +c & ,350,300/ +cPENG---------------------------------- + data cpsig2_lev_10 /13*100/ + data cpsig2_lev_11 /13*0/ + data cpsig2_lev_12 /90000,85000,80000,75000,70000,65000 + & ,60000,55000,50000,45000,40000 + & ,35000,30000/ + +c Model numbers used: (1) AVN, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) Early Eta, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble, +c (13) SREF Ensemble, +c (14) NCEP Ensemble (from ensstat mean fields), +c (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) NCEP Ensemble RELOCATION +c (21) UKMET hi-res (from NHC) +c (23) FNMOC Ensemble +c (24) HWRF Basin-scale + + if (trkrinfo%gribver == 2) then + +c For GRIB2, we will check to see if the MSLP being searched for +c is the standard MSLP (MSLP parm ID = 1) or if it is the +c so-called "Eta" or "Membrane" MSLP reduction that is included +c in the output for some models (like GFS and GDAS). Note that +c for 10m winds, with GRIB2, so far with all of the GRIB2 model +c data we've seen to this point, they all have the same IDs for +c 10m winds for all models, so no need to break out by model +c like we do for GRIB v1 in the else portion of this if statement. + + ig2_parm_num(9) = trkrinfo%g2_mslp_parm_id ! 1 = standard MSLP + ! reduction, 192 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB2 read, MSLP ID = ig2_parm_num(9) = ' + & ,ig2_parm_num(9) + + endif + + else + +c For GRIB1, do the same check as done just above in the IF part +c of this IF statement, but note that we need to also check to +c see what the GRIB1 parm IDs are for the sfc wind level type +c and value. Most models list the level type as 105 (which means +c height above the ground) and then a level value of 10. But +c ECMWF and UKMET use a level type of 1 (which means ground or +c water surface) and a level value of 0. + + igparm(9) = trkrinfo%g1_mslp_parm_id ! 102 = standard MSLP + ! reduction, 130 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglev(10) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + iglev(11) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + + ec_iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglev(10) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + ec_iglev(11) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB1 read, MSLP ID = igparm(9) = ' + & ,igparm(9) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev type = ' + & ,iglevtyp(10) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev value = ' + & ,iglev(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev type = ' + & ,ec_iglevtyp(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev value = ' + & ,ec_iglev(10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata. A return' + print *,'code (iret) not equal to zero indicates that ' + print *,'subroutine getgb was unable to find the requested ' + print *,'parameter. This could be simply because the parm is ' + print *,'not included in the grib file (this is likely for ' + print *,'ECMWF data, as they limit what they send us), or it ' + print *,'could indicate a problem with the grib index file.' + endif + + + if (allocated(f)) deallocate(f) + if (allocated(lb)) deallocate(lb) + allocate (f(imax*jmax),stat=ifa) + allocate (lb(imax*jmax),stat=ila) + if (ifa /= 0 .or. ila /= 0) then + print *,' ' + print *,'!!! ERROR in getdata allocating f or lb array.' + print *,'!!! ifa = ',ifa,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + if (trkrinfo%gribver == 2) then + + ! Reading from a GRIB v2 file.... + + grib2_standard_parm_read_loop: do ip = 1,nreadparms + + if (ip == 17) then + if (trkrinfo%use_land_mask == 'y' .or. + & trkrinfo%use_land_mask == 'Y') then + continue + else + if (verb .ge. 3) then + print *,' ' + print *,'The use_land_mask flag has not been set to ' + print *,'y or Y, so we will not try to read it in... ' + print *,' ' + cycle grib2_standard_parm_read_loop + endif + endif + endif + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + if (ip == 17) then + jdisc=1 ! hydrological products. At this point, used only + ! for the land-sea mask. + else + jdisc=0 ! meteorological products + endif + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for input parameter by production template 4.0. This +c tave program is used primarily for temperature, but still we +c will leave that as a variable and not-hard wire it in case we +c choose to average something else in the future. + + ! We are looking for Temperature or GP Height here. This + ! block of code, or even the smaller subset block of code that + ! contains the JPDT(1) and JPDT(2) assignments, can of course + ! be modified if this program is to be used for interpolating + ! other variables.... + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = ig2_parm_cat(ip) + JPDT(2) = ig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + +c JPDT(10) = ig2_lev_typ(ip) +cPENG----2018-06-07 ------------------------ + JPDT(10) = ig2_lev_10(ip) + +cPENG 04/18/2018 CMC Det. and CMC ensemble data +c if (inp%model == 16 ) then +c JPDT(11) = ig2_lev_11_cmc(ip) +c JPDT(12) = ig2_lev_val_cmc(ip) +c elseif (inp%model == 15 ) then +c JPDT(11) = ig2_lev_11_cmcd(ip) +c JPDT(12) = ig2_lev_val_cmcd(ip) +c else +c JPDT(11) = 0 +c if (JPDT(10) == 100) then ! isobaric surface +c JPDT(12) = ig2_lev_val(ip) * 100 ! GRIB2 levels are in Pa +c else +c JPDT(12) = ig2_lev_val(ip) ! This is going to be either mslp +c & ! or 10m winds. +c endif +c endif +cPENG----2018-06-07 ------------------------ + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 8 ) then + JPDT(11) = ig2_lev_11(ip) + JPDT(12) = ig2_lev_12(ip) + else if(inp%model == 16) then + JPDT(11) = ig2_lev_11_cmc(ip) + JPDT(12) = ig2_lev_12_cmc(ip) + else if(inp%model == 15) then + JPDT(11) = ig2_lev_11_cmcd(ip) + JPDT(12) = ig2_lev_12_cmcd(ip) + endif + + if ( verb_g2 .ge. 1 ) then + print *,'before getgb2 call, value of unpack = ',unpack + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is CLOSED' + endif + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is CLOSED' + endif + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,531) date_time(5),date_time(6),date_time(7) + 531 format (1x,'TIMING: before getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,532) date_time(5),date_time(6),date_time(7) + 532 format (1x,'TIMING: after getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call, value of unpacked = ' + & ,gfld%unpacked + print *,'after getgb2 call, gfld%ngrdpts = ',gfld%ngrdpts + print *,'after getgb2 call, gfld%ibmap = ',gfld%ibmap + endif + + if ( iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb2 found parm: ',chparm(ip) +c print *,'+++ at level = ',ig2_lev_val(ip) +cPENG---2018-06-07------------------------------------------- + print *,'+++ at level = ',ig2_lev_12(ip) + + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + select case (chparm(ip)) + case ('absv') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpdt(12) == 20000 .or. jpdt(12) == 2) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) +cPENG----2018-06-07 ------------------------ + case ('ushe') + call conv1d2d_real (imax,jmax,f,ushear + & ,need_to_flip_lats) + case ('rhum') + call conv1d2d_real (imax,jmax,f,rhumid + & ,need_to_flip_lats) + + + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb2 could not find parm: ' + & ,chparm(ip) +c print *,'!!! at level = ',ig2_lev_val(ip) +cPENG---2018-06-07------------------------------------------- + print *,'+++ at level = ',ig2_lev_12(ip) + + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + +c J.Peng---07/29/2019 to free the memory in reading GRIB2 data + call gf_free (gfld) + + enddo grib2_standard_parm_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c This is the GRIB2 reading section. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + grib2_cps_parm_lev_loop: do ip = 1,nlevs_cps + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + + jpds = -1 + jgds = -1 + j=0 + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = cpsig2_parm_cat(ip) + JPDT(2) = cpsig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + +c JPDT(10) = cpsig2_lev_typ(ip) + JPDT(10) = cpsig2_lev_10(ip) +cPENG-------------------------------------------- + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + JPDT(11) = cpsig2_lev_11(ip) + JPDT(12) = cpsig2_lev_12(ip) + endif + +c if (JPDT(10) == 100) then ! isobaric surface +c JPDT(12) = cpsig2_lev_val(ip) * 100 ! GRIB2 levels +c ! are in Pa +c else +c if (verb .ge. 3) then +c print *,' ' +c print *,'ERROR in getdata: JPDT(10) array value' +c print *,'should only be 100 in this CPS section' +c print *,'for GRIB2 data.' +c endif +c endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,731) date_time(5),date_time(6),date_time(7) + 731 format (1x,'TIMING: before getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn + & ,jgdt,unpack,krec,gfld,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,732) date_time(5),date_time(6),date_time(7) + 732 format (1x,'TIMING: after getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 (PHASE) in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call(PHASE),' + & ,' value of unpacked = ',gfld%unpacked + print *,'after getgb2 (PHASE) call, gfld%ngrdpts = ' + & ,gfld%ngrdpts + print *,'after getgb2 (PHASE) call, gfld%ibmap = ' + & ,gfld%ibmap + endif + + if (verb .ge. 3) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + +c Determine packing information from GRIB2 file. +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) + & then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ' + & ,gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ' + & ,gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned + ! from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do +c this once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) + & then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.' + & ,gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ' + & ,gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ' + & ,gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.' + & ,gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline + & ,gfld%ipdtmpl(1),gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,231) + 231 format (' rec# param level byy bmm bdd ' + & ,'bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin + & ,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif +c J.Peng---10/07/2019 to free the memory in reading GRIB2 data + call gf_free (gfld) + + enddo grib2_cps_parm_lev_loop + + endif + + endif + + else + + !---------------------------------- + ! Reading from a GRIB v1 file.... + !---------------------------------- + + grib1_read_loop: do ip = 1,nreadparms + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then ! ECMWF hi-res data uses ECMWF table + print *,' ' + print *,'WARNING: From the namelist, inp%model is set to a' + print *,' value of 4, which is for ECMWF, so in routine' + print *,' getdata_grib, the input jpds(5,6,7) parms are' + print *,' going to have values that are specific for' + print *,' ECMWF GRIB1 data.' + print *,' ' + jpds(5) = ec_igparm(ip) + jpds(6) = ec_iglevtyp(ip) + jpds(7) = ec_iglev(ip) + else ! All other models use NCEP-standard GRIB table + jpds(5) = igparm(ip) + jpds(6) = iglevtyp(ip) + jpds(7) = iglev(ip) + endif + + print *,' ' + print *,' --- Before getgb, jpds(5)= ',jpds(5) + print *,' --- , jpds(6)= ',jpds(6) + print *,' --- , jpds(7)= ',jpds(7) + + if (jpds(5) == 999) then + cycle + endif + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,831) date_time(5),date_time(6),date_time(7) + 831 format (1x,'TIMING: before getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + + if (enable_timing /= 0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,832) date_time(5),date_time(6),date_time(7) + 832 format (1x,'TIMING: after getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb call, j= ',j,' k= ',k + & ,' iftotalmins= ' + & ,iftotalmins(ifh),' parm # (ip) = ',ip,' iret= ',iret + else + print *,'After getgb call, j= ',j,' k= ',k,' ifhours= ' + & ,ifhours(ifh),' parm # (ip) = ',ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb found parm: ',chparm(ip) + print *,'+++ at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,29) + else + write (6,31) + endif + 29 format (' rec# parm# levt lev byy bmm bdd bhh fmin' + & ,' npts minval maxval') + 31 format (' rec# parm# levt lev byy bmm bdd bhh fhr ' + & ,' npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + + select case (chparm(ip)) + case ('absv') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpds(7) == 200) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb could not find parm: ',chparm(ip) + print *,'!!! at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib1_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + cps_grib1_lev_loop: do ip = 1,nlevs_cps + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then + ! Use different grib parm id for ECMWF GP height + jpds(5) = ec_cpsgparm(ip) + else + jpds(5) = cpsgparm(ip) + endif + jpds(6) = cpsglevtyp(ip) + jpds(7) = cpsglev(ip) + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,841) date_time(5),date_time(6),date_time(7) + 841 format (1x,'TIMING: before getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,842) date_time(5),date_time(6),date_time(7) + 842 format (1x,'TIMING: after getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,39) + else + write (6,41) + endif + 39 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fmin npts minval maxval') + 41 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fhr npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo cps_grib1_lev_loop + + endif + + endif + + endif +c + deallocate (f) + deallocate (lb) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) +c +c ABSTRACT: This subroutine reads the input NetCDF file for the +c tracked parameters for one lead time. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +c +c If the user has requested to check the cyclone phase space for +c this run (phaseflag set to 'y' and phasescheme set to 'cps'), +c then we need to have gp height data for 900-300 mb at every 50 +c mb. Some of those levels for gp height data were already read +c in with the read of the initial 17 parameters, but we will be +c sure to read in the others, if requested. +c +c INPUT: +c ncfile_id integer ID associated with the NetCDF file +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file +c itself in subroutine read_netcdf_fhours. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE netcdf_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo +c integer, parameter :: nreadparms=17,nreadparms_cps=13 +cPENG---2018-06-07-------------------------------- + integer, parameter :: nreadparms=19,nreadparms_cps=13 + + real, allocatable :: f(:) + real :: dmin,dmax,xmissing_value,xfill_value + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + character*1 :: lbrdflag,match_check + character*30 :: chparm(nreadparms) + character*30 :: chparm_cps(nreadparms_cps) + integer, intent(in) :: ncfile_id,imax,jmax + integer :: igvret,ifa,ip,ifh,i,j,k,m,n,ncfile_tmax,nf_get_att_real + integer :: nf_get_att_double,nf_inq_attlen,imvlen,ifvlen + integer :: usertime,ncix,missing_val_length,nf_status + integer :: nf_inq_varid,varid +c + lbrdflag = 'n' + +cnc data cpsgparm /13*7/ +cnc data cpsglevtyp /13*100/ +cnc data cpsglev /900,850,800,750,700,650,600,550,500,450,400 +cnc & ,350,300/ + +c data chparm /'vort850','vort700' +c & ,'u850','v850','u700','v700' +c & ,'h850','h700','slp','u_ref','v_ref' +c & ,'u500','v500','tm'/ + +c Load the names of the NetCDF variables for the standard +c variables into the chparm array... + + chparm(1) = netcdfinfo%rv850name + chparm(2) = netcdfinfo%rv700name + chparm(3) = netcdfinfo%u850name + chparm(4) = netcdfinfo%v850name + chparm(5) = netcdfinfo%u700name + chparm(6) = netcdfinfo%v700name + chparm(7) = netcdfinfo%z850name + chparm(8) = netcdfinfo%z700name + chparm(9) = netcdfinfo%mslpname + chparm(10) = netcdfinfo%usfcname + chparm(11) = netcdfinfo%vsfcname + chparm(12) = netcdfinfo%u500name + chparm(13) = netcdfinfo%v500name + chparm(14) = netcdfinfo%tmean_300_500_name + chparm(15) = netcdfinfo%z500name + chparm(16) = netcdfinfo%z200name + chparm(17) = netcdfinfo%lmaskname + + + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata_netcdf.' + endif + + if (allocated(f)) deallocate(f) + allocate (f(imax*jmax),stat=ifa) + if (ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getdata_netcdf allocating f data array.' + print *,'!!! ifa = ',ifa + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + !--------------------------------------------------------------- + ! First go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match up + ! the lead times that were read in with the lead times that + ! we read in directly from the NetCDF file. Get the index from + ! the NetCDF file for that lead time and use that in the call to + ! the read routine (get_var3_tlev_double). + !--------------------------------------------------------------- + + usertime = iftotalmins(ifh) + + match_check = 'n' + + find_index_loop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + ncix = m + if (verb .ge. 1) then + print *,'+++ Time match in getdata_netcdf for usertime= ' + & ,usertime,' netcdf file index= ncix= ',ncix + endif + match_check = 'y' + exit find_index_loop + endif + + enddo find_index_loop + + if (match_check == 'n') then + print *,' ' + print *,'!!! ERROR in getdata_netcdf: ' + print *,' For a NetCDF file, the user has ' + print *,' requested to process a lead time, and that lead' + print *,' time does not exist in the NetCDF list of time' + print *,' values. ' + print *,' ifh= ',ifh + print *,' usertime= iftotalmins(ifh)= ',iftotalmins(ifh) + print *,' STOPPING....' + stop 99 + endif + + !--------------------------------------------------------------- + ! Now go through the read loop for the list of parameters + !--------------------------------------------------------------- + + netcdf_standard_parm_read_loop: do ip = 1,nreadparms + + if (chparm(ip) == 'X' .or. chparm(ip) == 'x') then + if (verb .ge. 3) then + print *,' ' + print *,'!!! NetCDF read NOT requested for parm # ',ip + endif + cycle netcdf_standard_parm_read_loop + else + if (verb .ge. 3) then + print *,' ' + print *,'+++ NetCDF read requested for parm # ',ip + & ,' ... parm= ',chparm(ip) + endif + endif + + ! Note that I am sending a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which we + ! want), depending on the model & grid, we may need to flip the + ! grid in the north-south direction. I already have a routine + ! for converting data from a 1-d to a 2-d array, and it has + ! the functionality for flipping a grid, so I programmed it as + ! getting a 1-d array from the netcdf read routine and send that + ! 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm(ip),imax,jmax,ncix + & ,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + + ! Need to get the value of the "missing_value" attribute for + ! this variable from the list of attributes in the NetCDF + ! file. Only do this for the first lead time, since the + ! value of the "missing_value" obviously will not change + ! with lead time. + +c nf_status = nf_inq_attlen (ncfile_id,varid,"missing_value" +c & ,imvlen) +c nf_status = nf_inq_attlen (ncfile_id,varid,"_FillValue" +c & ,ifvlen) + + ! These next two nf function calls retrieve the value of the + ! "missing_value" attribute from the list of attributes for + ! the given variable being read in. This is needed in order + ! to know if a non-valid point is being accessed, as for a + ! regional grid, like the nested fvGFS. In GRIB1/GRIB2 files, + ! such regions would be bitmapped out, but in a NetCDF file, + ! no such bitmap exists, so we have to check for missing + ! values. In case it's a moving grid, we need to do this + ! for every lead time, since the "map of missing values" + ! will shift with lead time. Once we have those missing + ! values, we can loop through them and fill the valid_pt + ! logical array so that, in the end, we will have the same + ! logical bitmap for masking out missing data that we have + ! with GRIB1/GRIB2 data. + + nf_status = nf_inq_varid (ncfile_id,chparm(ip),varid) + + print *,'nf_status from nf_inq_varid call = ',nf_status + + nf_status = nf_get_att_real (ncfile_id,varid,"missing_value" + & ,xmissing_value) + + print *,'nf_status from nf_get_att_real call = ',nf_status + +c nf_status = nf_get_att_real (ncfile_id,varid,"_FillValue" +c & ,xfill_value) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"missing_value",len=imvlen) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"_FillValue",len=ifvlen) +c +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + if (verb .ge. 3) then + write (6,31) + 31 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,33) ifhours(ifh),ifclockmins(ifh),ip,chparm(ip) + & ,dmin,dmax + 33 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,35) chparm(ip),xmissing_value + 35 format (' --- ',a30,' missing value = ',g12.4) + endif + + ! This call to conv1d2d_logic_netcdf creates + ! a logical bitmap, so that in case we have + ! regional (non-global) data and an irregular grid (e.g., + ! the FV3 nested grid), we can mask out grid points that + ! have missing values as their data values. There is not + ! actually a native logical bitmap in NetCDF, so we will + ! create one by examining the real data values and masking + ! out grid points that have missing values. + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic_netcdf (imax,jmax,f,valid_pt + & ,xmissing_value,need_to_flip_lats) + lbrdflag = 'y' + endif + + if (ip == 1) then ! 850 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else if (ip == 2) then ! 700 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + else if (ip == 3) then ! 850 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (ip == 4) then ! 850 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (ip == 5) then ! 700 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (ip == 6) then ! 700 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (ip == 7) then ! 850 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (ip == 8) then ! 700 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (ip == 9) then ! MSLP + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + else if (ip == 10) then ! Near-sfc (10m) u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + else if (ip == 11) then ! Near-sfc (10m) v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + else if (ip == 12) then ! 500 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else if (ip == 13) then ! 500 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else if (ip == 14) then ! 300-500 mb mean Temp + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + else if (ip == 15) then ! 500 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (ip == 16) then ! 200 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + else if (ip == 17) then ! Land-sea mask + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + else + + print *,'!!! NOTE: Parm not recognized. ' + print *,'!!! ip is > 17.... ip= ',ip + print *,'!!! Forecast time level = ',ifh + + endif + + endif + + enddo netcdf_standard_parm_read_loop + +c *--------------------------------------------------------------* +c If we are attempting to determine the cyclone structure using +c Hart's cyclone phase space, then read in data now that will +c allow us to do that. If we are instead just using the +c mid-level (300-500 mb) mean temperature to do that with a +c simple warm-core check, then that mean temperature field was +c already read in above in the read loop for the standard +c variables. The variables needed here for CPS are pretty +c straightforward: gp height every 50 mb from 300 to 900 mb. +c keep in mind that we have already read in a few of these +c gp height records for selected levels above. +c *--------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + chparm_cps(1) = netcdfinfo%z900name + chparm_cps(2) = netcdfinfo%z850name + chparm_cps(3) = netcdfinfo%z800name + chparm_cps(4) = netcdfinfo%z750name + chparm_cps(5) = netcdfinfo%z700name + chparm_cps(6) = netcdfinfo%z650name + chparm_cps(7) = netcdfinfo%z600name + chparm_cps(8) = netcdfinfo%z550name + chparm_cps(9) = netcdfinfo%z500name + chparm_cps(10) = netcdfinfo%z450name + chparm_cps(11) = netcdfinfo%z400name + chparm_cps(12) = netcdfinfo%z350name + chparm_cps(13) = netcdfinfo%z300name + + ! Read in GP Height levels for cyclone phase space... + + if (verb .ge. 3) then + print *,' ' + print *,'--- Reads for CPS parms follow...' + print *,' ' + endif + + netcdf_cps_parm_read_loop: do ip = 1,nreadparms_cps + + if (chparm_cps(ip) == 'X' .or. chparm_cps(ip) == 'x') then + if (verb .ge. 3) then + print *,'!!! ERROR: NetCDF read NOT requested for' + print *,'!!! CPS parm # ',ip + print *,'!!! You must have an error in your namelist.' + print *,'!!! You have requested to do cyclone phase' + print *,'!!! checking, so you need to include the ' + print *,'!!! NetCDF names for ALL requested gp height' + print *,'!!! variables from 900 to 300 mb, every 50 ' + print *,'!!! mb,in the namelist.' + print *,'!!! phaseflag is being set to NO (n), and ' + print *,'!!! phase-checking will NOT take place.' + print *,'!!! If you want to run again and just do ' + print *,'!!! phase-checking with a simple warm-core' + print *,'!!! check, then in the namelist set phaseflag' + print *,'!!! to y and set phasescheme to vtt.' + phaseflag = 'n' + endif + exit netcdf_cps_parm_read_loop + else + if (verb .ge. 3) then + print *,'+++ NetCDF read requested for cps parm # ',ip + & ,' ... parm= ',chparm_cps(ip) + endif + endif + + ! As above, we send a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which + ! we want), depending on the model & grid, we may need to + ! flip the grid in the north-south direction. I already + ! have a routine for converting data from a 1-d to a 2-d + ! array, and it has the functionality for flipping a grid, + ! so I programmed it as getting a 1-d array from the netcdf + ! read routine and send that 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm_cps(ip),imax + & ,jmax,ncix,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm_cps(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + nf_status = nf_inq_varid (ncfile_id,chparm_cps(ip),varid) + nf_status = nf_get_att_real (ncfile_id,varid + & ,"missing_value",xmissing_value) + + if (verb .ge. 3) then + write (6,231) + 231 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,233) ifhours(ifh),ifclockmins(ifh),ip + & ,chparm_cps(ip),dmin,dmax + 233 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,235) chparm_cps(ip),xmissing_value + 235 format (' --- ',a30,' missing value = ',g12.4) + endif + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo netcdf_cps_parm_read_loop + + endif + + endif + + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_ncdim1 (ncid,var1_name,nmax) +c +c ABSTRACT: This routine queries a netcdf file to get the +c value of a requested file dimension (e.g., imax, jmax) +c + implicit none + + include "netcdf.inc" + + integer, intent(in) :: ncid + character*(*), intent(in) :: var1_name + integer, intent(out) :: nmax + integer :: status, var1id + + status = nf_inq_dimid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + status = nf_inq_dimlen (ncid,var1id,nmax) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_ncdim1 +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_var1_double (ncid,var1_name,nmax,var1) +c +c ABSTRACT: This routine reads a netcdf file in order to return +c a 1-dimensional array of data. + + implicit none + + include "netcdf.inc" + + integer, intent(in):: ncid + character*(*), intent(in):: var1_name + integer, intent(in):: nmax + real, intent(out):: var1(nmax) + + integer :: status, var1id + + status = nf_inq_varid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) +! write(*,*) 'Got var1id', var1id + + status = nf_get_var_real (ncid,var1id,var1) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var1_double +c +c--------------------------------------------------------- +c +c--------------------------------------------------------- + subroutine get_var3_tlev_double (ncid,var3_name,imax,jmax,ncix + & ,var3,igvret) +c +c ABSTRACT: This routine reads a netcdf file and returns a +c 2-dimensional synoptic variable at a particular lead time. +c The lead time is specified by the ltix array, which is +c included in module tracked_parms and defined in subroutine +c read_fhours. +c +c PARAMETERS +c +c INPUT: +c ncid integer that contains the NetCDF file ID +c var3_name character name of NetCDF input file +c imax integer x-dimension of input data +c jmax integer y-dimension of input data +c ncix integer index of time level for where this time level +c actually is inside the NetCDF data. Do NOT confuse this +c with the index of where this forecast hour is in the +c user's list of input forecast hours, as they may be +c different. For example, the user may request times that +c are every 6 hours, but the NetCDF file might have times +c that are every hour, so the indices for those two arrays +c will be different. Be sure to use the one (ncix) that +c indicates where the data actually starts in the +c NetCDF file. +c +c OUTPUT: +c var3 real array with real values returned from NetCDF read +c igvret integer return code from this routine + + USE tracked_parms; USE verbose_output; USE netcdf_parms + + implicit none + + include "netcdf.inc" +c + integer, intent(in) :: ncid,ncix + character*(*), intent(in) :: var3_name + integer, intent(in) :: imax,jmax + real, intent(out) :: var3(imax,jmax) + integer :: istart(3),ilength(3) + integer :: status,var3id,igvret + + if (verb .ge. 3) then + print *,' ' + print *,'In get_var3_tlev_double, ncix= ',ncix + print *,' nctotalmins(ncix)= ',nctotalmins(ncix) + endif + + istart(1) = 1 + istart(2) = 1 + istart(3) = ncix + + ilength(1) = imax + ilength(2) = jmax + ilength(3) = 1 + + igvret = 0 + + status = nf_inq_varid (ncid,var3_name,var3id) + + if (status /= NF_NOERR) then + print *,' ' + print *,'NOTE: Could not find variable ',var3_name,' at time' + & ,' index ncix= ',ncix + & ,' nctotalmins(ncix)= ',nctotalmins(ncix) + + igvret = 92 + return + endif + + status = nf_get_vara_real (ncid,var3id,istart,ilength,var3) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var3_tlev_double +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine handle_netcdf_err (status) +c +c ABSTRACT: This subroutine is an error handling routine for NetCDF- +c related functions. + + implicit none + + include "netcdf.inc" + + integer status +c + if (status /= nf_noerr) then + print *,' ' + print *,'Tracker NetCDF error: ' + print *, nf_strerror(status) + stop 'Stopped' + endif + + end subroutine handle_netcdf_err +c +c------------------------------------------------------------------- +c +c------------------------------------------------------------------- + subroutine bitmapchk (n,ld,d,dmin,dmax) +c +c This subroutine checks the bitmap for non-existent data values. +c Since the data from the regional models have been interpolated +c from either a polar stereographic or lambert conformal grid +c onto a lat/lon grid, there will be some gridpoints around the +c edges of this lat/lon grid that have no data; these grid +c points have been bitmapped out by Mark Iredell's interpolater. +c To provide another means of checking for invalid data points +c later in the program, set these bitmapped data values to a +c value of -999.0. The min and max of this array are also +c returned if a user wants to check for reasonable values. +c + logical(1) ld + dimension ld(n),d(n) +c + dmin=1.E15 + dmax=-1.E15 +c + do i=1,n + if (ld(i)) then + dmin=min(dmin,d(i)) + dmax=max(dmax,d(i)) + else + d(i) = -999.0 + endif + enddo +c + return + end +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic (imax,jmax,lb1d,lb2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of logical data (lb1d) into a 2-dimensional output +c array (dimension imax,jmax) of logical data (lb2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c lb1d 1-d array containing logical bitmap values +c iscanflag This is kgds(11), an integer value in the GDS, +c which holds the scanning mode for the data values +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + logical(1) lb1d(imax*jmax),lb2d(imax,jmax) + logical(1) :: need_to_flip_lats + integer :: ilat,ilatix,ilon,imax,jmax +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + lb2d(ilon,ilatix) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + lb2d(ilon,ilat) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic_netcdf (imax,jmax,dat1d,lb2d + & ,xmissing_val,need_to_flip_lats) +c +c ABSTRACT: The purpose of this routine is to create a 2-d logical +c bitmap to be used for masking out regions with missing data, +c such as for a regional grid with irregular boundaries (such as +c we've seen for the regional / nested FV3). This bitmap will +c have the same functionality as a GRIB1/GRIB2 bitmap. The trick +c is that NetCDF does not have a logical bitmap within its +c definition, so we need to make one. We do this by reading in +c the "missing_value" attribute for any variable, then here we +c scan through all the data values retrieved from the NetCDF read, +c and then for all grid points with missing values we set the +c valid_pt flag to .false. +c +c Note the use of the need_to_flip_lats flag. This is in order to +c handle grids that are flipped. Most grids -- NCEP, UKMET, ECMWF +c -- have point (1,1) as the uppermost left point on the grid, and +c the data goes from north to south. Some grids -- GFDL and the +c new NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the need_to_flip_lats flag was set to TRUE in getgridinfo, meaning +c that we have northward scanning data, we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d array containing floating point data values +c xmissing_val real value of missing value for the given variable +c that was read in for the calling routine +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + USE verbose_output + + implicit none + + logical(1) lb2d(imax,jmax) + logical(1) need_to_flip_lats + integer ilat,ilatix,ilon,imax,jmax,tct,fct,mct + real dat1d(imax*jmax) + real xmissing_val +c + tct = 0 + fct = 0 + mct = 0 + + if (verb >= 3) then + print *,' ' + print *,'TOP of conv1d2d_logic_netcdf, xmissing_val= ' + & ,xmissing_val + print *,' ' + endif +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then + lb2d(ilon,ilatix) = .false. +c print *,'LBSF FLIP: ilon= ',ilon,' ilatix= ',ilatix +c fct = fct + 1 + else + lb2d(ilon,ilatix) = .true. +c tct = tct + 1 + endif + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then +c print *,'LBSF no-flip: ilon= ',ilon,' ilat= ',ilat + lb2d(ilon,ilat) = .false. +c fct = fct + 1 + else + lb2d(ilon,ilat) = .true. +c tct = tct + 1 + endif + enddo + enddo + + endif + +c print *,' ' +c print *,' LB STATS: tct= ',tct,' fct= ',fct,' mct= ',mct +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine conv1d2d_real (imax,jmax,dat1d,dat2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of real data (dat1d) into a 2-dimensional output +c array (dimension imax,jmax) of real data (dat2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d real array of data +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c dat2d 2-d real array of data +c + logical(1) :: need_to_flip_lats + real dat1d(imax*jmax),dat2d(imax,jmax) +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + dat2d(ilon,ilatix) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + dat2d(ilon,ilat) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_nlists (inp,trkrinfo,netcdfinfo) +c +c ABSTRACT: This subroutine simply reads in the namelists that are +c created in the shell script. Namelist datein contains the +c starting date information, plus the model identifier. Namelist +c stswitch contains the flags for processing for each storm. +c + USE inparms; USE set_max_parms; USE atcf; USE trkrparms; USE phase + USE structure; USE gfilename_info + USE verbose_output; USE waitfor_parms; USE netcdf_parms + USE tracking_parm_prefs + + implicit none + + integer ifh + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo +c + namelist/datein/inp + namelist/atcfinfo/atcfnum,atcfname,atcfymdh,atcffreq + namelist/trackerinfo/trkrinfo + namelist/phaseinfo/phaseflag,phasescheme,wcore_depth + namelist/structinfo/structflag,ikeflag + namelist/fnameinfo/gmodname,rundescr,atcfdescr + namelist/verbose/verb,verb_g2 + namelist/waitinfo/use_waitfor,wait_min_age,wait_min_size + & ,wait_max_wait,wait_sleeptime + & ,use_per_fcst_command,per_fcst_command + namelist/netcdflist/netcdfinfo + namelist/parmpreflist/user_wants_to_track_zeta850 + & ,user_wants_to_track_zeta700,user_wants_to_track_wcirc850 + & ,user_wants_to_track_wcirc700,user_wants_to_track_gph850 + & ,user_wants_to_track_gph700,user_wants_to_track_mslp + & ,user_wants_to_track_wcircsfc,user_wants_to_track_zetasfc + & ,user_wants_to_track_thick500850 + & ,user_wants_to_track_thick200500 + & ,user_wants_to_track_thick200850 + +c Set namelist default values: + use_per_fcst_command='t' + per_fcst_command=' ' + atcffreq=600 + trkrinfo%enable_timing=1 + trkrinfo%want_oci=.false. + trkrinfo%gribver=1 ! Set to GRIB1 as default, can be set to + ! something else in the namelist input. + + read (5,NML=datein,END=801) + 801 continue + read (5,NML=atcfinfo,END=807) + 807 continue + print *,'just before trackerinfo read namelist' + read (5,NML=trackerinfo,END=809) + 809 continue + print *,'just after trackerinfo read namelist' + read (5,NML=phaseinfo,END=811) + 811 continue + read (5,NML=structinfo,END=815) + 815 continue + read (5,NML=fnameinfo,END=817) + 817 continue + read (5,NML=waitinfo,END=821) + 821 continue + read (5,NML=netcdflist,END=823) + 823 continue + read (5,NML=parmpreflist,END=825) + 825 continue + read (5,NML=verbose,END=819,ERR=833) + 819 continue + goto 837 + 833 continue + verb = 1 + 837 continue + + print *,'in read_nlists, verb= ',verb + + if ( verb .ge. 0 ) then + print *,' ' + print *,'After datein namelist in trak.f, namelist ' + & ,'parms follow:' + print *,'Forecast initial year = byy = ',inp%byy + print *,'Forecast initial month = bmm = ',inp%bmm + print *,'Forecast initial day = bdd = ',inp%bdd + print *,'Forecast initial hour = bhh = ',inp%bhh + print *,'Forecast model identifier = model= ',inp%model + print *,'Forecast model type = modtyp= ',inp%modtyp + print *,'Forecast model data lead time units= lt_units= ' + & ,inp%lt_units + print *,'Forecast model data sequencing setup= file_seq= ' + & ,inp%file_seq + print *,'Forecast model nest type = ',inp%nesttyp +c + print *,' ' + print *,'Values read in from atcfinfo namelist: ' + write (6,89) atcfnum,atcfname + write (6,90) atcfymdh + write (6,92) atcffreq + 89 format ('ATCF ID = ',i2,' ATCF Name = ',a4) + 90 format ('ATCF date (initial date on output atcf records) = ' + & ,i10) + 92 format ('ATCF output frequency (in hours*100) = atcffreq = ',i6) +c + print *,' ' + print *,'Values read in from trackerinfo namelist follow: ' + write (6,101) ' western boundary = westbd = ',trkrinfo%westbd + write (6,101) ' eastern boundary = eastbd = ',trkrinfo%eastbd + write (6,101) ' northern boundary = northbd = ',trkrinfo%northbd + write (6,101) ' southern boundary = southbd = ',trkrinfo%southbd + write (6,102) ' tracker type = ',trkrinfo%type + write (6,103) ' mslp threshold = mslpthresh = ' + & ,trkrinfo%mslpthresh + write (6,120) ' Flag for using backup mslp gradient check= ' + & ,'use_backup_mslp_grad_check = ' + & ,trkrinfo%use_backup_mslp_grad_check + write (6,103) ' v850 threshold = v850thresh = ' + & ,trkrinfo%v850thresh + write (6,122) ' Flag for using backup 850 mb Vt check= ' + & ,'use_backup_850_vt_check = ' + & ,trkrinfo%use_backup_850_vt_check + write (6,123) ' Max allowable distance between the ' + & ,'tracker-found fixes for mslp and 850 zeta = ' + & ,trkrinfo%max_mslp_850 + write (6,104) ' model grid type = ',trkrinfo%gridtype + write (6,101) ' Contour interval to be used = ',trkrinfo%contint + write (6,106) ' Flag for whether or not roci will be computed' + & ,' and written out for tracker-type case = ' + & ,trkrinfo%want_oci + write (6,105) ' Flag for whether or not vitals will be written ' + & ,'out = ',trkrinfo%out_vit + write (6,109) ' Flag for whether or not a land mask will be ' + & ,'used for tcgen candidate low filtering = ' + & ,trkrinfo%use_land_mask + write (6,110) ' Flag for input data type (grib or netcdf) = ' + & ,trkrinfo%inp_data_type + write (6,107) ' Flag for which GRIB version (1 or 2) the input' + & ,' data will be in = ',trkrinfo%gribver + write (6,108) ' Flag for input GRIB2 JPDTN (0 or 1) = ' + & ,trkrinfo%g2_jpdtn + write (6,112) ' Flag for input GRIB2 MSLP ID (1 or 192) = ' + & ,trkrinfo%g2_mslp_parm_id + write (6,114) ' Flag for input GRIB1 MSLP ID (102 or 130) = ' + & ,trkrinfo%g1_mslp_parm_id + write (6,116) ' Flag for input GRIB1 sfcwind level type ' + & ,'(PDS Octet 10... should be 1 or 105) = ' + & ,trkrinfo%g1_sfcwind_lev_typ + write (6,118) ' Flag for input GRIB1 sfcwind level value ' + & ,'(PDS Octets 11 & 12... usually 0 or 10) = ' + & ,trkrinfo%g1_sfcwind_lev_val + + 101 format (a31,f7.2) + 102 format (a16,a7) + 103 format (a31,f7.4) + 104 format (a19,a8) + 106 format (a46,a41,L1) + 105 format (a48,a6,a1) + 109 format (a45,a41,a1) + 110 format (a45,a6) + 107 format (a47,a19,i1) + 108 format (a39,i2) + 112 format (a43,i4) + 114 format (a45,i4) + 116 format (a41,a39,i4) + 118 format (a42,a43,i4) + 120 format (a44,a29,a1) + 122 format (a40,a26,a1) + 123 format (a36,a44,f7.1) + + print *,' ' + print *,' ' + print *,'Values read in from netcdflist namelist: ' + print *,' ' + write (6,300) netcdfinfo%num_netcdf_vars ! Total *possible* + ! number of input NetCDF variables, + ! including those that are included + ! in the input file and those that + ! are not. + write (6,370) netcdfinfo%netcdf_filename ! full path filename + write (6,301) + write (6,302) netcdfinfo%rv850name ! 850 mb rel vort + write (6,304) netcdfinfo%rv700name ! 700 mb rel vort + write (6,306) netcdfinfo%u850name ! 850 mb u-comp + write (6,308) netcdfinfo%v850name ! 850 mb v-comp + write (6,310) netcdfinfo%u700name ! 700 mb u-comp + write (6,312) netcdfinfo%v700name ! 700 mb v-comp + write (6,314) netcdfinfo%z850name ! 850 mb gp height + write (6,316) netcdfinfo%z700name ! 700 mb gp height + write (6,318) netcdfinfo%mslpname ! mslp + write (6,320) netcdfinfo%usfcname ! near-sfc u-comp + write (6,322) netcdfinfo%vsfcname ! near-sfc v-comp + write (6,324) netcdfinfo%u500name ! 500 mb u-comp + write (6,326) netcdfinfo%v500name ! 500 mb v-comp + write (6,328) netcdfinfo%tmean_300_500_name !Mean T @ 300-500 mb + write (6,330) netcdfinfo%z500name ! 500 mb gp height + write (6,332) netcdfinfo%z200name ! 200 mb gp height + write (6,334) netcdfinfo%lmaskname ! Land mask + write (6,336) netcdfinfo%z900name ! 900 mb gp height + write (6,338) netcdfinfo%z800name ! 800 mb gp height + write (6,340) netcdfinfo%z750name ! 750 mb gp height + write (6,342) netcdfinfo%z650name ! 650 mb gp height + write (6,344) netcdfinfo%z600name ! 600 mb gp height + write (6,346) netcdfinfo%z550name ! 550 mb gp height + write (6,348) netcdfinfo%z450name ! 450 mb gp height + write (6,350) netcdfinfo%z400name ! 400 mb gp height + write (6,352) netcdfinfo%z350name ! 350 mb gp height + write (6,354) netcdfinfo%z300name ! 300 mb gp height + write (6,355) netcdfinfo%time_name ! Name of time variable + ! (usually it is "time") + write (6,356) netcdfinfo%lon_name ! longitudes + write (6,358) netcdfinfo%lat_name ! latitudes + write (6,359) netcdfinfo%time_units ! This will be either "days" + ! or "hours". If it's "hours", + ! then all the time data values + ! are for hours since the initial + ! time. Same thing for "days", + ! however if it is "days", then + ! know that a value of 0.25 will + ! be the same as a 6-hour lead + ! time. + + 300 format ('Total *possible* number of input NetCDF variables,' + & ,/,' including those that are included in the input' + & ,/,' NetCDF file and those that are not = ',i4) + 370 format ('Input NetCDF filename = ',a180) + 301 format (' ',/ + & ,'List of NetCDF variables follows. A value of X ',/ + & ,'indicates the variable is not included in the ',/ + & ,'input file and no attempt will be made to read in ',/ + & ,'that variable: ',/,' ') + 302 format ('NetCDF variable name for 850 mb vort = ',a30) + 304 format ('NetCDF variable name for 700 mb vort = ',a30) + 306 format ('NetCDF variable name for 850 mb u-comp = ',a30) + 308 format ('NetCDF variable name for 850 mb v-comp = ',a30) + 310 format ('NetCDF variable name for 700 mb u-comp = ',a30) + 312 format ('NetCDF variable name for 700 mb v-comp = ',a30) + 314 format ('NetCDF variable name for 850 mb gp height = ',a30) + 316 format ('NetCDF variable name for 700 mb gp height = ',a30) + 318 format ('NetCDF variable name for MSLP = ',a30) + 320 format ('NetCDF variable name for near-sfc u-comp = ',a30) + 322 format ('NetCDF variable name for near-sfc v-comp = ',a30) + 324 format ('NetCDF variable name for 500 mb u-comp = ',a30) + 326 format ('NetCDF variable name for 500 mb v-comp = ',a30) + 328 format ('NetCDF variable name for 300-500 mb Mean T = ',a30) + 330 format ('NetCDF variable name for 500 mb gp height = ',a30) + 332 format ('NetCDF variable name for 200 mb gp height = ',a30) + 334 format ('NetCDF variable name for land-sea mask = ',a30) + 336 format ('NetCDF variable name for 900 mb gp height = ',a30) + 338 format ('NetCDF variable name for 800 mb gp height = ',a30) + 340 format ('NetCDF variable name for 750 mb gp height = ',a30) + 342 format ('NetCDF variable name for 650 mb gp height = ',a30) + 344 format ('NetCDF variable name for 600 mb gp height = ',a30) + 346 format ('NetCDF variable name for 550 mb gp height = ',a30) + 348 format ('NetCDF variable name for 450 mb gp height = ',a30) + 350 format ('NetCDF variable name for 400 mb gp height = ',a30) + 352 format ('NetCDF variable name for 350 mb gp height = ',a30) + 354 format ('NetCDF variable name for 300 mb gp height = ',a30) + 355 format ('NetCDF variable name for time = ',a30) + 356 format ('NetCDF variable name for longitudes = ',a30) + 358 format ('NetCDF variable name for latitudes = ',a30) + 359 format ('NetCDF time value (hours|days) = ',a30) + + print *,' ' + print *,' ' + print *,'Values read in from parmpreflist namelist: ' + print *,' ' + write (6,402) user_wants_to_track_zeta850 + write (6,404) user_wants_to_track_zeta700 + write (6,406) user_wants_to_track_wcirc850 + write (6,408) user_wants_to_track_wcirc700 + write (6,410) user_wants_to_track_gph850 + write (6,412) user_wants_to_track_gph700 + write (6,414) user_wants_to_track_mslp + write (6,416) user_wants_to_track_wcircsfc + write (6,418) user_wants_to_track_zetasfc + write (6,420) user_wants_to_track_thick500850 + write (6,422) user_wants_to_track_thick200500 + write (6,424) user_wants_to_track_thick200850 + + 402 format ('user_wants_to_track_zeta850= ',a2) + 404 format ('user_wants_to_track_zeta700= ',a2) + 406 format ('user_wants_to_track_wcirc850= ',a2) + 408 format ('user_wants_to_track_wcirc700= ',a2) + 410 format ('user_wants_to_track_gph850= ',a2) + 412 format ('user_wants_to_track_gph700= ',a2) + 414 format ('user_wants_to_track_mslp= ',a2) + 416 format ('user_wants_to_track_wcircsfc= ',a2) + 418 format ('user_wants_to_track_zetasfc= ',a2) + 420 format ('user_wants_to_track_thick500850= ',a2) + 422 format ('user_wants_to_track_thick200500= ',a2) + 424 format ('user_wants_to_track_thick200850= ',a2) + + print *,' ' + print *,'Values read in from phaseinfo namelist: ' + write (6,211) phaseflag,phasescheme + write (6,212) wcore_depth + 211 format ('Storm phase flag = ',a1,' Phase scheme = ',a4) + 212 format ('Storm phase, warm core depth (wcore_depth) = ',f7.2) + + print *,' ' + print *,'Values read in from structinfo namelist: ' + write (6,93) structflag + write (6,95) ikeflag + 93 format ('Structure flag = ',a1) + 95 format ('IKE flag = ',a1) + + print *,' ' + print *,'Values read in for grib file name from fnameinfo' + & ,' namelist: ' + write (6,131) gmodname + write (6,133) rundescr + write (6,135) atcfdescr + 131 format ('Model name description = gmodname = ',a4) + 133 format ('Forecast run description = rundescr = ',a40) + 135 format ('Optional ATCF / Storm name description = atcfdescr = ' + & ,a40) + + print *,' ' + print *,'Value read in for verbose output for most output:' + write (6,141) verb + 141 format ('Value read in for verbose flag = verb = ',i2) + + print *,' ' + print *,'Value read in for verbose output for grib2 output:' + write (6,142) verb_g2 + 142 format ('Value read in for GRIB2 verbose flag = verb_g2 = ',i2) + + print *,' ' + print *,'Values read in from waitinfo namelist:' + write (6,151) use_waitfor + write (6,152) wait_min_age + write (6,153) wait_min_size + write (6,154) wait_max_wait + write (6,155) wait_sleeptime + if(len_trim(per_fcst_command)>0) then + write (6,156) trim(per_fcst_command) + else +c No command specified, so disable the feature + use_per_fcst_command='n' + endif + 151 format ('Flag for input file waiting = use_waitfor = ',a1) + 152 format ('min age (time in seconds since last mod) = ' + & ,'wait_min_age = ',i8) + 153 format ('min file size in bytes = wait_min_size = ',i12) + 154 format ('max number of seconds to wait for each file = ' + & ,'wait_max_wait = ',i6) + 155 format ('number of seconds to sleep between checks = ' + & ,'wait_sleeptime = ',i6) + 156 format ('command to run after every forecast time = "',A,'"') +c + if (use_waitfor == 'y') then + if (inp%file_seq == 'multi') then + continue + else + print *,' ' + print *,'!!! ERROR: The use_waitfor flag is set to "y".' + print *,' This requires that the inp%file_seq flag be' + print *,' set to "multi", but you have specified ' + print *,' something else. ' + print *,' inp%file_seq = ',inp%file_seq + print *,' STOPPING....' + print *,' ' + STOP 95 + endif + endif +c + endif + return + end +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_fhours (ifhmax) +c +c ABSTRACT: This subroutine reads in a text file that contains the +c forecast times that will be read in. The format of the file is +c in "MMMMM", i.e., minutes, for example, for a forecast going out +c to 120h, the file would look like this: +c +c For reference, here +c are the times that +c match up with the +c minutes on the left: +c +c 1 0 0:00 +c 2 240 4:00 +c 3 270 4:30 +c 4 300 5:00 +c 5 330 5:30 +c 6 360 6:00 +c 7 600 10:00 +c 8 630 10:30 +c 9 660 11:00 +c 10 690 11:30 +c 11 720 12:00 +c 12 960 16:00 +c 13 990 16:30 +c . . . +c . . . +c . . . +c 87 7200 120:00 +c +c Note that we are now allowing for sub-hourly time intervals. +c + USE tracked_parms + USE verbose_output + + implicit none +c + integer, parameter :: iunit_fh=15 + integer itmphrs(750),itmpmins(750),input_mins(750),itmpltix(750) + integer ifhmax,inphr,inpmin,ict,i,ifa,ifma,icma,ira,inpltix,ila + real xminfract + + itmphrs = -99 + itmpmins = -99 + + if (allocated(ifhours)) deallocate (ifhours) + if (allocated(iftotalmins)) deallocate (iftotalmins) + if (allocated(ifclockmins)) deallocate (ifclockmins) + if (allocated(fhreal)) deallocate (fhreal) + if (allocated(ltix)) deallocate (ltix) + + ict = 0 + do while (.true.) + + if ( verb .ge. 3 ) then + print *,'Top of while loop in read_fhours' + endif + + read (iunit_fh,85,end=130) inpltix,inpmin + write (6,85) inpltix,inpmin + + if (inpmin >= 0 .and. inpmin < 150000) then + ict = ict + 1 + itmpltix(ict) = inpltix + itmphrs(ict) = inpmin / 60 + itmpmins(ict) = mod(inpmin,60) + input_mins(ict) = inpmin + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Input minutes not between 0 and 150000' + print *,'!!! inpmin= ',inpmin + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + if ( verb .ge. 3 ) then + print *,'readloop, ict= ',ict,' inpmin= ',inpmin + endif + + enddo + + 130 continue + + ifhmax = ict + + 85 format (i4,1x,i5) + + if ( verb .ge. 3 ) then + print *,' ' + endif + + allocate (ifhours(ifhmax),stat=ifa) + allocate (iftotalmins(ifhmax),stat=ifma) + allocate (ifclockmins(ifhmax),stat=icma) + allocate (fhreal(ifhmax),stat=ira) + allocate (ltix(ifhmax),stat=ila) + if (ifa /= 0 .or. ifma /= 0 .or. icma /= 0 .or. ira /= 0 .or. + & ila /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_fhours allocating either ifhours,' + print *,'!!! iftotalmins, ifclockmins or fhreal.' + print *,'!!! ifa = ',ifa,' ifma= ',ifma,' ira= ',ira + print *,'!!! icma= ',icma,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + do i = 1,ifhmax + + ltix(i) = itmpltix(i) + xminfract = float(itmpmins(i)) / 60. + fhreal(i) = float(itmphrs(i)) + xminfract + ifhours(i) = itmphrs(i) + ifclockmins(i) = itmpmins(i) + iftotalmins(i) = input_mins(i) + + if (i > 1) then + if (fhreal(i) > fhreal(i-1)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: In read_fhours, the time read in ' + print *,'!!! is not greater than the previous time.' + print *,'!!! i= ',i + print *,'!!! fhreal(i)= ',fhreal(i) + print *,'!!! fhreal(i-1)= ',fhreal(i-1) + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + endif + + if ( verb .ge. 3 ) then + write (6,87) i,ltix(i),iftotalmins(i),fhreal(i),ifhours(i) + & ,ifclockmins(i) + endif + + enddo + + 87 format (1x,'i= ',i3,' input lead time index= ',i4,' minutes= ' + & ,i5,' real_lead_time= ',f6.2,' clock_lead_time= ',i3,':' + & ,i2) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_tcv_card1 (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + logical(1) :: vit_file_exists + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + + ! Check to see if the TC Vitals file exists. If so, then open it + ! using the unit specified in lucard. + + inquire (file="tcvit_rsmc_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for existing, RSMC-numbered' + & ,' storms exists and will be opened with ' + & ,' unit= lucard= ',lucard + endif + + open (unit=lucard,file="tcvit_rsmc_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_rsmc_storms.txt has ' + print *,' been opened with unit= lucard= ',lucard + endif + + else + + if (trkrinfo%type == 'tracker') then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card. The fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. This TC Vitals file is needed for' + print *,'!!! a tracker case. Check to see that the ' + print *,'!!! TC Vitals file exists in this directory and' + print *,'!!! is named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING....' + print *,'!!! ' + iret=99 + return + endif + + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! NOTE: In read_tcv_card, the fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. While this TC Vitals file is ' + print *,'!!! needed for tracker cases, you are running' + print *,'!!! either a midlat or tcgen case here, and so ' + print *,'!!! that file is not needed... although you can ' + print *,'!!! run with using tc vitals for those genesis' + print *,'!!! cases if you want to. You may want to check' + print *,'!!! and make sure this is what you intend. If ' + print *,'!!! you do want to use it, the TC Vitals file ' + print *,'!!! should be in this directory and it should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! ' + endif + + endif + + endif + + ii=1 + + if (vit_file_exists) then + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + 801 continue + endif + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + + maxstorm = numtcv + + if (maxstorm > 0) then + continue + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING...' + print *,'!!! ' + iret=99 + return + endif + endif + + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! Stopping....' + print *,' ' + endif + + iret = 99 + return + + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card opening rsmc TC vitals' + print *,'!!! file named tcvit_rsmc_storms.txt. A file with' + print *,'!!! that name needs to be in your working directory.' + print *,'!!! It should contain the input TC vitals for ' + print *,'!!! already-existing storms that have rsmc-issued' + print *,'!!! storm IDs.' + endif + + iret = 97 + return + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine read_gen_vitals1(lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + logical(1) :: vit_file_exists + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + + ! Check to see if the genesis TC Vitals file exists. If so, then + ! open it using the unit specified in lgvcard. + + inquire (file="tcvit_genesis_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for genesis' + & ,' storms exists and will be opened with ' + & ,' unit= lgvcard= ',lgvcard + endif + + open (unit=lgvcard,file="tcvit_genesis_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_genesis_storms.txt has ' + print *,' been opened with unit= lgvcard= ',lgvcard + endif + + endif + + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + + ii = numtcv + 1 + + if (vit_file_exists) then + + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1 + & ,1x,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + endif + + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals opening genesis vitals' + print *,'!!! file named tcvit_genesis_storms.txt' + endif + + iret = 97 + return + + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just read the +c grib file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE tracked_parms; USE inparms + USE verbose_output; USE params; USE grib_mod + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + + type(gribfield) :: gfld,prevfld,holdgfld + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1), allocatable :: lb(:) + logical :: unpack=.true. + logical :: open_grb=.false. + CHARACTER(len=8) :: pabbrev + integer,dimension(200) :: jids,jpdt,jgdt + integer, parameter :: jf=40000000 + integer :: listsec1(13) + integer pdt_4p0_vert_level,pdt_4p0_vtime + real xhold,xlondiff,xlatdiff,temp,firstval,lastval + real, allocatable :: f(:) + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer jpds(200),jgds(200),igetpds(200),igetgds(200) + integer, intent(in) :: ifh + integer, intent(out) :: imax,jmax + integer iia,ija,ila,midi,midj,i,j,iix,jix,ifa,iret + integer iscanflag,iggret,kf,k,lugb,lugi,jskp,jdisc + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 + + iggret = 0 + + allocate (lb(jf),stat=ila); allocate (f(jf),stat=ifa) + if (ila /= 0 .or. ifa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating either lb or f' + print *,'!!! ila = ',ila,' ifa= ',ifa + endif + iggret = 97 + return + endif + + if (trkrinfo%gribver == 2) then + + ! Search for a record from a GRIB2 file + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 ! meteorological products + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for Temperature or GP Height by production template.... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + ! Request a record on a lat/lon grid. + + jgdtn = 0 + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpdt(8) = 0 + jpdt(9) = iftotalmins(ifh) + else + jpdt(8) = 1 + jpdt(9) = ifhours(ifh) + endif + + if (verb >= 3) then + print *,'before getgb2 call, lugb= ',lugb,' lugi= ',lugi + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + if ( iret.ne.0) then + print *,' ' + print *,' ERROR: getgb2 error in getgridinfo = ',iret + print *,' FATAL ERROR: cannot proceed without info ' + print *,' from getgridinfo. STOPPING....' + stop 95 + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if ( verb_g2 .ge. 1 ) then + print *,' ' + print *,' -- BEGIN getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'PDT num= gfld%ipdtnum= ',gfld%ipdtnum + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + imax = gfld%igdtmpl(8) + jmax = gfld%igdtmpl(9) + dx = float(gfld%igdtmpl(17))/1.e6 + dy = float(gfld%igdtmpl(17))/1.e6 + kf = gfld%ngrdpts + + holdgfld = gfld + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + + endif + + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' -- END getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' ' + print *,' ' + + endif + + need_to_flip_lons = .false. + + iscanflag = gfld%igdtmpl(19) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(gfld%igdtmpl(12))/1.e6 + glatmax = float(gfld%igdtmpl(15))/1.e6 + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(gfld%igdtmpl(15))/1.e6 + glatmax = float(gfld%igdtmpl(12))/1.e6 + need_to_flip_lats = .false. + endif + + glonmin = float(gfld%igdtmpl(13))/1.e6 + glonmax = float(gfld%igdtmpl(16))/1.e6 + + if (verb .ge. 3) then + print *,'In getgridinfo: glatmin= ',glatmin + print *,' glatmax= ',glatmax + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif +c J.Peng---10/07/2019 to free the memory in reading GRIB2 data + call gf_free (gfld) + + else + + !------------------------------------------ + ! Search for a record from a GRIB1 file + !------------------------------------------ + + jpds = -1 + jgds = -1 + + jgds(1) = 0 ! Request a record that's on a lat/lon grid + + if ( verb .ge. 3 ) then + print *,'before getgb in getgridinfo, ifh= ',ifh + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* Forecast hour: ',i4,':',i2.2) + print *,' ifhours(ifh)= ',ifhours(ifh) + print *,' iftotalmins(ifh)= ',iftotalmins(ifh) + endif + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + j=0 + +c jpds(14) = 0 ! test +c + write(*,980) jpds(1),jpds(2) + write(*,981) jpds(3),jpds(4) + write(*,982) jpds(5),jpds(6) + write(*,983) jpds(7),jpds(8) + write(*,984) jpds(9),jpds(10) + write(*,985) jpds(11),jpds(12) + write(*,986) jpds(13),jpds(14) + write(*,987) jpds(15),jpds(16) + write(*,988) jpds(17),jpds(18) + write(*,989) jpds(19),jpds(20) + write(*,990) jpds(21),jpds(22) + write(*,991) jpds(23),jpds(24) + write(*,992) jpds(25) + write(*,880) jgds(1),jgds(2) + write(*,881) jgds(3),jgds(4) + write(*,882) jgds(5),jgds(6) + write(*,883) jgds(7),jgds(8) + write(*,884) jgds(9),jgds(10) + write(*,885) jgds(11),jgds(12) + write(*,886) jgds(13),jgds(14) + write(*,887) jgds(15),jgds(16) + write(*,888) jgds(17),jgds(18) + write(*,889) jgds(19),jgds(20) + write(*,890) jgds(21),jgds(22) + + 980 format(' jpds(1) = ',i7,' jpds(2) = ',i7) + 981 format(' jpds(3) = ',i7,' jpds(4) = ',i7) + 982 format(' jpds(5) = ',i7,' jpds(6) = ',i7) + 983 format(' jpds(7) = ',i7,' jpds(8) = ',i7) + 984 format(' jpds(9) = ',i7,' jpds(10) = ',i7) + 985 format(' jpds(11) = ',i7,' jpds(12) = ',i7) + 986 format(' jpds(13) = ',i7,' jpds(14) = ',i7) + 987 format(' jpds(15) = ',i7,' jpds(16) = ',i7) + 988 format(' jpds(17) = ',i7,' jpds(18) = ',i7) + 989 format(' jpds(19) = ',i7,' jpds(20) = ',i7) + 990 format(' jpds(21) = ',i7,' jpds(22) = ',i7) + 991 format(' jpds(23) = ',i7,' jpds(24) = ',i7) + 992 format(' jpds(25) = ',i7) + 880 format(' jgds(1) = ',i7,' jgds(2) = ',i7) + 881 format(' jgds(3) = ',i7,' jgds(4) = ',i7) + 882 format(' jgds(5) = ',i7,' jgds(6) = ',i7) + 883 format(' jgds(7) = ',i7,' jgds(8) = ',i7) + 884 format(' jgds(9) = ',i7,' jgds(10) = ',i7) + 885 format(' jgds(11) = ',i7,' jgds(12) = ',i7) + 886 format(' jgds(13) = ',i7,' jgds(14) = ',i7) + 887 format(' jgds(15) = ',i7,' jgds(16) = ',i7) + 888 format(' jgds(17) = ',i7,' jgds(18) = ',i7) + 889 format(' jgds(19) = ',i7,' jgds(20) = ',i7) + 890 format(' jgds(20) = ',i7,' jgds(22) = ',i7) + + print *,'lugb= ',lugb,' lugi= ',lugi + print *,'before ggi getgb jpds(14) = ',jpds(14) + print *,'before ggi getgb jgds(1) = ',jgds(1) + + call getgb(lugb,lugi,jf,j,jpds,jgds, + & kf,k,igetpds,igetgds,lb,f,iret) + + if (iret.ne.0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo calling getgb' + print *,'!!! Return code from getgb = iret = ',iret + endif + + iggret = iret + else + iggret=0 + imax = igetgds(2) + jmax = igetgds(3) + dx = float(igetgds(9))/1000. + dy = float(igetgds(10))/1000. + endif + +c write(*,780) igetpds(1),igetpds(2) +c write(*,781) igetpds(3),igetpds(4) +c write(*,782) igetpds(5),igetpds(6) +c write(*,783) igetpds(7),igetpds(8) +c write(*,784) igetpds(9),igetpds(10) +c write(*,785) igetpds(11),igetpds(12) +c write(*,786) igetpds(13),igetpds(14) +c write(*,787) igetpds(15),igetpds(16) +c write(*,788) igetpds(17),igetpds(18) +c write(*,789) igetpds(19),igetpds(20) +c write(*,790) igetpds(21),igetpds(22) +c write(*,791) igetpds(23),igetpds(24) +c write(*,792) igetpds(25) +c write(*,680) igetgds(1),igetgds(2) +c write(*,681) igetgds(3),igetgds(4) +c write(*,682) igetgds(5),igetgds(6) +c write(*,683) igetgds(7),igetgds(8) +c write(*,684) igetgds(9),igetgds(10) +c write(*,685) igetgds(11),igetgds(12) +c write(*,686) igetgds(13),igetgds(14) +c write(*,687) igetgds(15),igetgds(16) +c write(*,688) igetgds(17),igetgds(18) +c write(*,689) igetgds(19),igetgds(20) +c write(*,690) igetgds(21),igetgds(22) +c +c 780 format(' kpds(1) = ',i7,' kpds(2) = ',i7) +c 781 format(' kpds(3) = ',i7,' kpds(4) = ',i7) +c 782 format(' kpds(5) = ',i7,' kpds(6) = ',i7) +c 783 format(' kpds(7) = ',i7,' kpds(8) = ',i7) +c 784 format(' kpds(9) = ',i7,' kpds(10) = ',i7) +c 785 format(' kpds(11) = ',i7,' kpds(12) = ',i7) +c 786 format(' kpds(13) = ',i7,' kpds(14) = ',i7) +c 787 format(' kpds(15) = ',i7,' kpds(16) = ',i7) +c 788 format(' kpds(17) = ',i7,' kpds(18) = ',i7) +c 789 format(' kpds(19) = ',i7,' kpds(20) = ',i7) +c 790 format(' kpds(21) = ',i7,' kpds(22) = ',i7) +c 791 format(' kpds(23) = ',i7,' kpds(24) = ',i7) +c 792 format(' kpds(25) = ',i7) +c 680 format(' kgds(1) = ',i7,' kgds(2) = ',i7) +c 681 format(' kgds(3) = ',i7,' kgds(4) = ',i7) +c 682 format(' kgds(5) = ',i7,' kgds(6) = ',i7) +c 683 format(' kgds(7) = ',i7,' kgds(8) = ',i7) +c 684 format(' kgds(9) = ',i7,' kgds(10) = ',i7) +c 685 format(' kgds(11) = ',i7,' kgds(12) = ',i7) +c 686 format(' kgds(13) = ',i7,' kgds(14) = ',i7) +c 687 format(' kgds(15) = ',i7,' kgds(16) = ',i7) +c 688 format(' kgds(17) = ',i7,' kgds(18) = ',i7) +c 689 format(' kgds(19) = ',i7,' kgds(20) = ',i7) +c 690 format(' kgds(20) = ',i7,' kgds(22) = ',i7) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,' dx= ',dx,' dy= ',dy + endif + + +c ---------------------------------------------------------------- +c Get boundaries of the data grid. NOTE: gds(4) is referred to in +c GRIB documenatation as the "Latitude of origin", which might +c imply "minimum Latitude". However, for the grids that we'll be +c using in this program, the "Latitude of origin" will be listed +c under gds(4) as the northernmost point (eg., in MRF, +c gds(4) = 90), so for this program, use gds(4) as your max lat, +c and gds(7) as your min lat. However, in case NCEP, UKMET or +c ECMWF change their convention and begin flipping their grids, a +c check is made to make sure that the max lat is not less than the +c min lat. +c +c BUGFIX (August, 2001): It is possible to have an input grid +c which goes from south to north (such as NAVGEM). In this case, +c we flip the data in subroutine conv1d2d_real. However, the max +c and min latitudes listed in the GRIB GDS will be confused, so we +c need to check the value of the GRIB scanning mode flag here. + + need_to_flip_lons = .false. + + iscanflag = igetgds(11) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(igetgds(4))/1000. + glatmax = float(igetgds(7))/1000. + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(igetgds(7))/1000. + glatmax = float(igetgds(4))/1000. + need_to_flip_lats = .false. + endif + + glonmin = float(igetgds(5))/1000. + glonmax = float(igetgds(8))/1000. + + endif + +c After this point in this subroutine, nothing is GRIB1 / GRIB2 +c specific, so it does not need to be within the if/then +c statement above that differentiated between GRIB / GRIB2. + +c17Jul2014 if (glonmin < 0.0) glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax < 0.0) glonmax = 360. - abs(glonmax) + + if (glonmin >= 0.0 .and. glonmax >= 0.0) then + if (glonmin > glonmax) then + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Badly notated longitude boundaries in ' + print *,' GRIB PDS, because the min longitude ' + print *,' (glonmin) is greater than the max ' + print *,' longitude (glonmax) where both longitudes' + print *,' are greater than 0.' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + print *,' !!! STOPPING....' + stop 98 + endif + endif + elseif (glonmin < 0.0 .and. glonmax >= 0.0) then + ! An example of this is the MPAS data, which starts and ends + ! at the dateline and is specified as glonmin=-179.875, + ! glonmax=179.875. Convert to be positive and go from + ! 180.125 to 539.875. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0, glonmax > 0, so glonmin' + print *,' will be converted to be > 0 and 360 will' + print *,' be added to glonmax.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. + abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin < 0.0 .and. glonmax < 0.0) then + ! Examples of this are GFDL and HWRF. In this case, make + ! both glonmin and glonmax positive. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0 and glonmax < 0, so both' + print *,' will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin >= 0.0 .and. glonmax < 0.0) then + ! An example of this is the GFS data, which goes from + ! glonmin=0.0 to glonmax=-0.5. Convert it here to go + ! from glonmin=0.0 to glonmax=359.5 + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is >= 0 and glonmax < 0, so' + print *,' glonmax will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + endif + +c17Jul2014 if (glonmin < 0.0) then +c17Jul2014 glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax <= 0.0) then +c17Jul2014 glonmax = 360. - abs(glonmax) +c17Jul2014 else +c17Jul2014 glonmax = 360 + abs(glonmax) +c17Jul2014 endif +c17Jul2014 endif + + if (glatmax < glatmin) then + temp = glatmax + glatmax = glatmin + glatmin = temp + endif + + if (glonmin > 200.0 .and. glonmin <= 360.) then + if (glonmax < 50.) then + ! Likely GM-wrapping in current record + glonmax = glonmax + 360. + endif + endif +c + if ( verb .ge. 3 ) then + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + print *,' ' + print *,'NOTE: For regional grids, valid data points might' + print *,'NOT extend all the way to the gds-defined grid ' + print *,'boundary, due to the fact that data have been ' + print *,'interpolated from a NPS or Lamb-Conf grid onto a ' + print *,'lat/lon grid. This program checks the logical ' + print *,'bitmap for valid data points, but just keep this in' + print *,'mind if trying to debug errors that occur near the' + print *,'grid boundaries for regional models.' + endif + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glat)) deallocate(glat) + if (allocated(glon)) deallocate(glon) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + endif + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + + iggret = 96 + return + endif + + do j=1,jmax + glat(j) = glatmax - (j-1)*dy + enddo + do i=1,imax + glon(i) = glonmin + (i-1)*dx + enddo + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + +c -------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have +c forgotten to change the input grid bounds from a global grid +c run). Modify the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just query the +c netcdf file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE inparms + USE verbose_output; USE netcdf_parms + + implicit none +c + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (datecard) inp + + logical(1) :: need_to_flip_lats,need_to_flip_lons + real xhold,xlondiff,xlatdiff + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer iscanflag,iggret + integer, intent(in) :: ncfile_id + integer, intent(out) :: imax,jmax + integer :: iia,ija,midi,midj,i,j,iix,jix +c + + iggret = 0 + + call get_ncdim1(ncfile_id,netcdfinfo%lon_name,imax) + call get_ncdim1(ncfile_id,netcdfinfo%lat_name,jmax) + + if (allocated(tmplon)) deallocate (tmplon) + if (allocated(tmplat)) deallocate (tmplat) + allocate (tmplon(imax),stat=iia) + allocate (tmplat(jmax),stat=ija) + if (iia /= 0 .or. ija /= 0) then + print *,' ' + print *,'!!! ERROR in sub getgridinfo_netcdf allocating arrays.' + print *,'!!! iia = ',iia,' ija= ',ija + iggret = 94 + return + endif + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + endif + + call get_var1_double (ncfile_id,netcdfinfo%lon_name,imax,tmplon) + call get_var1_double (ncfile_id,netcdfinfo%lat_name,jmax,tmplat) + +c Compute the dx and dy by picking values out of the middle of +c the lat and lon arrays.... + + midi = imax/2 + midj = jmax/2 + + dx = abs(tmplon(midi) - tmplon(midi-1)) + dy = abs(tmplat(midj) - tmplat(midj-1)) + + if (verb .ge. 1) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,'dx= ',dx,' dy= ',dy + print *,' ' + write (6,112) midi,dx + write (6,113) midj,dy + + 112 format(1x,' DX: midi= ',i4,' dx= ',f8.4) + 113 format(1x,' DY: midj= ',i4,' dy= ',f8.4) + endif + + +c ------------------------------------------------------------------ +c Get boundaries of the data grid. Note that it is possible to have +c an input grid which goes from south to north (in fact, it appears +c that many NetCDF files are constructed this way). Keep in mind, +c however, that the tracker has been written such that point (1,1) +c should be the upper-leftmost point on the grid, while point +c (imax,jmax) should be the lower-rightmost point. If we check and +c find that we're dealing with data that instead starts from the +c south and increases northward, we flip the data in subroutine +c conv1d2d_real. Similarly here, we make sure to test so that when +c we are done in this routine, glatmax refers to the northernmost +c latitude and glatmin the southernmost latitude. + + if (tmplon(imax) > tmplon(1)) then + glonmin = tmplon(1) + glonmax = tmplon(imax) + else + glonmin = tmplon(imax) + glonmax = tmplon(1) + endif + + if (tmplat(1) > tmplon(jmax)) then + glatmax = tmplat(1) + glatmin = tmplat(jmax) + else + glatmax = tmplat(jmax) + glatmin = tmplat(1) + endif + + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glon)) deallocate (glon) + if (allocated(glat)) deallocate (glat) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + iggret = 96 + return + endif + + ! If the lat or lon grids are flipped (i.e., the lats increase + ! from south to north, or the lons increase westward), then we + ! will need to flip both the data arrays as well as the arrays + ! that are holding the values of the lats and lons.... + + need_to_flip_lats = .false. + need_to_flip_lons = .false. + + if (tmplat(1) > tmplon(jmax)) then + do j=1,jmax + glat(j) = tmplat(j) + enddo + else + do j=1,jmax + jix = jmax - j + 1 + glat(jix) = tmplat(j) + enddo + need_to_flip_lats = .true. + endif + + if (tmplon(imax) > tmplon(1)) then + do i=1,imax + glon(i) = tmplon(i) + enddo + else + do i=1,imax + iix = imax - i + 1 + glon(iix) = tmplon(i) + enddo + need_to_flip_lons = .true. + endif + +c do i = 1,imax +c print *,'i= ',i,' glon(i)= ',glon(i) +c enddo +c do j = 1,jmax +c print *,'j= ',j,' glat(j)= ',glat(j) +c enddo + +c --------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have forgotten +c to change the input grid bounds from a global grid run). Modify +c the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) +c +c ABSTRACT: The purpose of this subroutine is to read the "time" +c dimension and "time data" from the NetCDF file so that we know +c how many time levels there are and what those time levels are. +c One reason for doing this is that some models, like the GFDL +c FV3, do not output hour 0 data, so we need to check this first +c before running through the tracking processing for the various +c hours. We will take the list of hours read in here directly from +c the NetCDF file and compare that against the *requested* list of +c forecast hours that the user has entered. The user might not be +c aware that there is no hour 0 data for a given model. We compare +c these two lists of forecast hours and then write a message if +c there is a lead time that is not in the NetCDF file. +c +c INPUT: +c ncfile character name of NetCDF file +c ncfile_id integer id associated with NetCDF file after open +c ifhmax integer max number of lead times that the user has +c requested on the input lead times data file. This +c value was set in subroutine read_fhours. +c netcdfinfo variable of user-defined type netcdfstuff (from +c module netcdf_parms). +c +c OUTPUT: +c ncfile_tmax integer max number of lead times that are in the +c NetCDF file, as read in from this subroutine +c ncfile_has_hour0 character flag (y|n) that tells whether or not +c the input NetCDF data file actually has an hour0 +c record in it or not. +c + USE netcdf_parms; USE tracked_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo + + character :: ncfile*180,ncfile_has_hour0*1,match_check*1 + integer, intent(in) :: ncfile_id + integer, intent(out) :: ncfile_tmax + integer :: infta,k,m,n,ifhmax,irnhret,usertime +c + + irnhret = 0 + ncfile_has_hour0 = 'n' + + !----------------------------------------------------------- + ! First read the NetCDF file to get the number of time levels, + ! which will be returned in "ncfile_tmax".... + !----------------------------------------------------------- + + print *,' ' + print *,'in read_netcdf_hours...' + print *,'ncfile_id= ',ncfile_id + print *,'netcdfinfo%time_name= ',netcdfinfo%time_name + print *,'ncfile_tmax= ',ncfile_tmax + + call get_ncdim1(ncfile_id,netcdfinfo%time_name,ncfile_tmax) + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + print *,'Num netcdf time levs= ncfile_tmax= ',ncfile_tmax + endif + + if (allocated(netcdf_file_time_values)) then + deallocate (netcdf_file_time_values) + endif + + allocate (netcdf_file_time_values(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating' + print *,'!!! netcdf_file_time_values array. infta = ',infta + irnhret = 94 + return + endif + + + !----------------------------------------------------------- + ! Now read in the actual time values that are stored in the + ! NetCDF file.... + !----------------------------------------------------------- + + call get_var1_double (ncfile_id,netcdfinfo%time_name,ncfile_tmax + & ,netcdf_file_time_values) + + if (verb .ge. 1) then + do k = 1,ncfile_tmax + print *,'k= ',k,' netcdf_file_time_values(k)= ' + & ,netcdf_file_time_values(k) + enddo + endif + + !------------------------------------------------------------ + ! Now convert the NetCDF time values into minutes in order to + ! be able to compare with the user-requested list of lead + ! times. Remember that the NetCDF lead times will be listed + ! either as hours or as fractions of days. + !------------------------------------------------------------ + + if (allocated(nctotalmins)) then + deallocate (nctotalmins) + endif + + allocate (nctotalmins(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating ' + print *,'!!! nctotalmins array. infta = ',infta + irnhret = 94 + return + endif + + do k = 1,ncfile_tmax + + if (netcdfinfo%time_units == 'hours') then + nctotalmins(k) = int(netcdf_file_time_values(k)) * 60 + elseif (netcdfinfo%time_units == 'days') then + nctotalmins(k) = int(netcdf_file_time_values(k) * 60. * 24.) + else + print *,' ' + print *,'!!! ERROR: In read_netcdf_hours, the value of' + print *,' netcdfinfo%time_units is neither hours nor days.' + print *,' netcdfinfo%time_units= ',netcdfinfo%time_units + print *,' STOPPING....' + print *,' ' + stop 99 + endif + + if (verb .ge. 1) then + write (6,71) k,netcdf_file_time_values(k),nctotalmins(k) + endif + + enddo + + 71 format (1x,i5,' netcdf_file_time_values(k)= ',f8.4 + & ,' nctotalmins(k)= ',i10) + + !------------------------------------------------------------ + ! Now go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match + ! the two lists up. The big one to watch out for is whether + ! or not the NetCDF file actually has an hour 0 lead time. + !------------------------------------------------------------ + + userloop: do n = 1,ifhmax + + usertime = iftotalmins(n) + + match_check = 'n' + + netcdfloop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + if (verb .ge. 1) then + print *,'+++ Time match for usertime= ',usertime + endif + match_check = 'y' + endif + + enddo netcdfloop + + if (match_check == 'n') then + + if (usertime == 0) then + print *,' ' + print *,'Warning: For a NetCDF file, the user has requested' + print *,'to read in an hour 0 file, however a scan of the' + print *,'time data values in the NetCDF file indicates' + print *,'that there is no hour 0 data in this file. ' + print *,'We will substitute either missing values or ' + print *,'the values from the TC Vitals data in the ' + print *,'hour 0 record and then start searching at the ' + print *,'next lead time.' + ncfile_has_hour0 = 'n' + else + print *,' ' + print *,'!!! ERROR: For a NetCDF file, the user has' + print *,' requested to process a particular lead time that' + print *,' does not exist in the NetCDF list of time ' + print *,' values.' + print *,' n= ',n + print *,' usertime= iftotalmins(n)= ',iftotalmins(n) + print *,' STOPPING....' + stop 99 + endif + + elseif (match_check == 'y') then + + if (usertime == 0) then + if (verb .ge. 1) then + print *,' ' + print *,'+++ For the input NetCDF file, an hour0 data ' + print *,' record exists in the data file.' + endif + ncfile_has_hour0 = 'y' + endif + + endif + + enddo userloop +c + return + end +c +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_valid_point (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) +c +c ABSTRACT: This subroutine checks to see if the input lat/lon +c point is associated with four surrounding (i,j) locations that +c have valid data. The writing of this routine was prompted by the +c HFIP project in February, 2009. Some of their high resolution +c data for their inner nests contained grids that had been rotated +c from native map projections to regular lat/lon grids, but that +c rotation left "empty" spots on the lat/lon grid where there is +c no data. Then when searching in find_maxmin, we were running +c barnes iterations from these lat/lon locations where there was +c no data, which would give artificially low values at those +c lat/lon locations (because the barnes scheme would only include +c points that were relatively far away where there was valid data). +c So in this routine, we call subroutine fix_latlon_to_ij in order +c to get the nearest (i,j) coordinates, and then we check all of +c these points to make sure that valid data exist. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing in i-direction +c dy grid spacing in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c rlatt,rlont input lat/lon about which we will check the +c surrounding (i,j) locations for valid data. +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c icvpret return code from this routine. A value of 0 means that +c all is okay and the input point is surrounded by valid +c data. + + USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,ifix,jfix + integer ifilret,icvpret + character(*) cmaxmin + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real rlont,rlatt,xdum,gridpoint_maxmin + real dx,dy,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +c + call fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt + & ,xdum,ifix,jfix,gridpoint_maxmin,'checker' + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) + + if (ifilret /= 0) then + icvpret = 99 + return + endif + + if (valid_pt(ifix,jfix)) then + icvpret = 0 + else + icvpret = 99 + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.025) then + grfact = 20 + else if (grdspc > 0.025 .and. grdspc <= 0.05) then + grfact = 12 + else if (grdspc > 0.05 .and. grdspc <= 0.10) then + grfact = 6 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif + + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (1*grfact) + iend = ipfix + (2*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (2*grfact) + iend = ipfix + (1*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (1*grfact) + iend = ipfix + (1*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (2*grfact) + jend = jpfix + (1*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (1*grfact) + jend = jpfix + (2*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (1*grfact) + jend = jpfix + (1*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +c print *,' End of fix_latlon_to_ij, gridpoint_maxmin = ' +c & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine rvcal (imax,jmax,dlon,dlat,z,vp) +c +c ABSTRACT: This routine calculates the relative vorticity (zeta) +c from u,v on an evenly-spaced lat/lon grid. Centered finite +c differences are used on the interior points and one-sided +c differences are used on the boundaries. +c +c NOTE: There are 3 critical arrays in this subroutine, the first +c being zeta and the 2nd and 3rd being u and v. There is a +c critical difference in the array indexing for the levels. For +c zeta, the array is dimensioned with levels from 1 to 3, with +c 1 = 850, 2 = 700, 3 = sfc. However, there is an extra level +c for the winds, such that the level dimension goes 1 = 850, +c 2 = 700, 3 = 500, 4 = sfc. So we need to adjust for that in +c this routine. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE trig_vals; USE grid_bounds + USE verbose_output + + implicit none + + dimension cosfac(jmax),tanfac(jmax) + real tmpzeta(imax,jmax) + real xlondiff,xlatdiff,dlon,dlat,dfix + real dlat_edge,dlat_inter,dlon_edge,dlon_inter + real rlat(jmax),cosfac,tanfac + integer z,iscanflag,nlat,nlon,i,j,imax,jmax,w + integer ii,jj + logical(1) vp(imax,jmax) + +c -------------------------- + +c Figure out what level of data we have and what the array +c indices should be. + + if (z == 1) then + ! z = 1 for 850 mb zeta, w = 1 for 850 mb winds + w = 1 + else if (z == 2) then + ! z = 2 for 700 mb zeta, w = 2 for 700 mb winds + w = 2 + else if (z == 3) then + ! z = 3 for sfc zeta, w = 4 for sfc (10m) winds + w = 4 + endif + +c Calculate grid increments for interior and edge points. + +c IMPORTANT: If dtk is defined in module trig_vals in km, then +c we need to multiply by 1000 here to get meters. If it's defined +c as meters, just let it be. Since the wind values are given in +c meters, that's why we need the dlon values to be in meters. + + if (dtk < 750.) then ! chances are, dtk was defined as km + dfix = 1000.0 + else ! dtk was already defined as meters + dfix = 1.0 + endif + + dlon_edge = dtk * dfix * dlon ! Di dist over 1 grid pt + dlat_edge = dtk * dfix * dlat ! Dj dist over 1 grid pt + dlon_inter = dtk * dfix * 2.0 * dlon ! Di dist over 2 grid pts + dlat_inter = dtk * dfix * 2.0 * dlat ! Dj dist over 2 grid pts + + +c Calculate required trig functions. These are functions of +c latitude. Remember that the grid must go from north to south. +c This north-to-south requirement has +c already been checked in subroutine getgridinfo. If necessary, +c any flipping of the latitudes was done there, and flipping of +c the data, again if necessary, was done in subroutine getdata. + + do j=2,jmax-1 + rlat(j) = glatmax - ((j-1) * dlat) + cosfac(j) = cos(dtr*rlat(j)) + tanfac(j) = (tan(dtr*rlat(j)))/erad + enddo + +c Set trig factors at end points to closest interior point +c to avoid a singularity if the domain includes the poles, +c which it will for the global grids (MRF, GDAS, GFS, UKMET,NCE) + + cosfac(1) = cosfac(2) + tanfac(1) = tanfac(2) + cosfac(jmax) = cosfac(jmax-1) + tanfac(jmax) = tanfac(jmax-1) + +c NOTE: These next bits of vorticity calculation code assume that +c the input grid is oriented so that point (1,1) is the upper +c left-most (NW) and point (imax,jmax) is the lower right- +c most point. Any other grids will probably crash the +c program due to array out of bounds errors. +c NOTE: Before each calculation is done, the logical array is +c checked to make sure that all the data points in this +c calculation have valid data (ie., that the points are not +c outside a regional model's boundaries). +c +c !!! IMPORTANT NOTE: While testing this, I uncovered a bug, which was +c that I had the "j+1" and "j-1" reversed. Just from a physical +c understanding, the du/dy term at a point is calculated by taking +c the u value north of the point minus the u value south of the +c point. Intuitively, this is u(j+1) - u(j-1). However, we have +c designed this program to have the northernmost point as +c the beginning of the grid (i.e., for the global grids, j=1 at 90N, +c and j increases southward). Thus, if you would do u(j+1) - +c u(j-1), you would actually be taking the u value south of the +c point minus the u value north of the point, EXACTLY THE OPPOSITE +c OF WHAT YOU WANT. Therefore, the vorticity calculations have +c been changed so that we now have u(j-1) - u(j+1). +c +c UPDATE FEB 2009: With limited domain grids that have missing +c data on them (such as you would have for a grid that has been +c converted from a non-lat/lon grid to a lat/lon grid), we were +c running into problems below with the setting of zeta values to +c a missing value of -999. In place of this, the easiest thing to +c do is to simply assign a value of the background coriolis value +c to that point. No, this is not correct, but it is the easiest +c workaround for this right now. Setting it to zero would be too +c far off. Setting it to the coriolis component has a net effect +c of not having much impact on the barnes scheme result. +c +c --------------- +c Interior points +c --------------- + + if ( verb .ge. 3 ) then + print *,'Just before inter rvcalc, dlon_inter = ',dlon_inter + & ,' dlat_inter = ',dlat_inter + endif + + do j=2,jmax-1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1) .and. vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo + enddo +c +c ----------------------------- +c Bottom (Southernmost) points +c ----------------------------- +c + j=jmax + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------- +c Top (Northernmost) points +c -------------------------- +c + j=1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c ------------------------------- +c Left edge (Westernmost) points +c ------------------------------- +c + i=1 + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------------- +c Right edge (Easternmost) points +c -------------------------------- +c + i=imax + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c --------- +c SW corner +c --------- + i=1 + j=jmax + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i+1,j,w)-v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NW corner +c --------- + i=1 + j=1 + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NE corner +c --------- + i=imax + j=1 + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c SE corner +c --------- + i=imax + j=jmax + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i,j,w)-v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + do ii=1,imax + do jj=1,jmax + tmpzeta(ii,jj) = zeta(ii,jj,z) * 1.e5 + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine thickness_calc (imax,jmax,vp) +c +c ABSTRACT: This routine calculates the thicknesses for three +c different layers: 200-500, 500-850 and 200-850 mb. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE verbose_output + + implicit none + + integer i,j,layer,upper,lower,imax,jmax + logical(1) vp(imax,jmax) + +c -------------------------- + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 +c +c The array indices for the levels for the 4 different GP height +c arrays (as assigned in subroutine getdata) are as follows: +c 1: 850 mb +c 2: 700 mb +c 3: 500 mb +c 4: 200 mb + + + do layer = 1,3 + + select case (layer) + case (1); upper=3; lower=1; + case (2); upper=4; lower=3; + case (3); upper=4; lower=1; + end select + + do j = 1,jmax + do i = 1,imax + + if (vp(i,j)) then + thick(i,j,layer) = hgt(i,j,upper) - hgt(i,j,lower) + else + thick(i,j,layer) = -999.0 + endif + + enddo + enddo + + enddo +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine first_ges_center (imax,jmax,dx,dy,cparm,fxy + & ,cmaxmin,trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) +c +c ABSTRACT: This subroutine scans an array and picks out areas of +c max or min, then loads those center positions into the first- +c guess lat & lon arrays to be used by subroutine tracker for +c locating the very specific low center positions. +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c dx Grid spacing in i-direction for the input grid +c dy Grid spacing in j-direction for the input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c finf Logical. Field of influence. Dimension same as fxy +c cmaxmin Char string to indicate if search is for a max or a min +c trkrinfo Derived type that holds/describes various tracker parms, +c including the contour interval to be used +c ifh Index for the forecast hour +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c contour_info Type cint_stuff from module contours. Contains +c contour information +c +c OUTPUT: +c maxmini Integer array containing i-indeces of max/min locations +c maxminj Integer array containing j-indeces of max/min locations +c ifgcret return code from this subroutine +c +c OTHER: +c storm Contains the tcvitals for the storms (module def_vitals) + + USE trkrparms; USE grid_bounds; USE set_max_parms; USE def_vitals + USE contours; USE tracked_parms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,n,isstart,ifamret,ibeg,jbeg,iend,jend + integer ifh,maxstorm,imax,jmax,itemp,ifgcret + integer stormct,oldstormct,mm + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + character(*) cparm,cmaxmin + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real dmax,dmin,dx,dy,dbuffer,tmp + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of first_ges_center *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for new lows at hour ',i4,':',i2.2) + print *,'*-------------------------------------------------*' + endif + + +c First check the user-supplied grid boundaries to see if we will +c scan the entire array or just a portion of it. + + if (trkrinfo%northbd < -998.0 .or. trkrinfo%southbd < -998.0 .or. + & trkrinfo%westbd < -998.0 .or. trkrinfo%eastbd < -998.0) then + ! User did not specify a subgrid, so scan the whole domain + ibeg = 1 + iend = imax + jbeg = 1 + jend = jmax + else + +c if (trkrinfo%westbd > 360.0 .or. trkrinfo%eastbd < 0.0 .or. +c & trkrinfo%westbd < 0.0 .or. + + if (trkrinfo%westbd > 360.0 .or. + & trkrinfo%northbd > 90.0 .or. trkrinfo%northbd <-90.0 .or. + & trkrinfo%southbd > 90.0 .or. trkrinfo%southbd <-90.0 .or. + & trkrinfo%westbd >= trkrinfo%eastbd .or. + & trkrinfo%southbd >= trkrinfo%northbd) then + + if (trkrinfo%westbd > trkrinfo%eastbd) then + + if (trkrinfo%westbd < 360.0 .and. + & trkrinfo%eastbd >= 0.0)then + + ! In this special case, the user has specified that the + ! western boundary be to the west of the Greenwich + ! meridian and the eastern boundary be to the east of it. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE: The user supplied grid lon boundaries' + print *,'++ span across the Greenwich meridian.' + print *,'++ ' + print *,'++ Western boundary: ',trkrinfo%westbd + print *,'++ Eastern boundary: ',trkrinfo%eastbd + print *,'++ Northern boundary: ',trkrinfo%northbd + print *,'++ Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ! Calculate the beginning and ending i and j points for + ! this case of spanning the Greenwich meridian. The + ! beginning and ending j points are, obviously, the same + ! as for the regular case below in the else. The + ! i-beginning point will also be the same as for the + ! regular case. However, the i-ending point will be + ! modified for the meridian wrap; it will be > imax. + + jbeg = int(((glatmax + dy - trkrinfo%northbd) + & / dy) + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) + & / dy) + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) + & / dx) + 0.5) +c iend = int(((trkrinfo%eastbd - glonmin + dx) +c & / dx) + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) + & / dx) + 0.5) + imax + + goto 377 + + endif + endif + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. There is a' + print *,'!!! problem with the user-supplied grid ' + print *,'!!! boundaries. Please check them and ' + print *,'!!! resubmit the program.' + print *,'!!!' + print *,'!!! Western boundary: ',trkrinfo%westbd + print *,'!!! Eastern boundary: ',trkrinfo%eastbd + print *,'!!! Northern boundary: ',trkrinfo%northbd + print *,'!!! Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ifgcret = 91 + return + + 377 continue + + else + ! Calculate the beginning and ending i and j points.... + jbeg = int(((glatmax + dy - trkrinfo%northbd) / dy) + & + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) / dy) + & + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) / dx) + & + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) / dx) + & + 0.5) + endif + endif + +c Scan the requested portion of the grid and pick out the max and +c min data values, figure out what the max and min contour levels +c will be, and fill an array with the values of the various +c intermediate, incremental contour levels. + + if (trkrinfo%contint <= 0) then + + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. For a midlat' + print *,'!!! or tcgen run of the tracker, the contour' + print *,'!!! interval supplied by the user is not ' + print *,'!!! greater than 0.' + print *,'!!! ' + print *,'!!! User-supplied contint = ',trkrinfo%contint + print *,' ' + endif + + ifgcret = 91 + return + endif + + dmin = 9.99e20 + dmax = -9.99e20 + + do j = jbeg,jend + do i = ibeg,iend + if (i > imax) then + itemp = i - imax ! If wrapping past GM + else + itemp = i + endif + if (valid_pt(itemp,j)) then + if (fxy(itemp,j) < dmin) dmin = fxy(itemp,j) + if (fxy(itemp,j) > dmax) dmax = fxy(itemp,j) + endif + enddo + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*--------------------------------------------*' + print *,'In first_ges_center, dmin= ',dmin,' dmax= ',dmax + endif + + +c We want to allow for storms moving out of the sub-region, +c in which case we might hit slightly lower or higher +c contours than were found in the sub-region, so allow for +c an extra buffer and modify dmin and dmax.... + + dbuffer = (dmax - dmin) / 2.0 + dmax = dmax + dbuffer + dmin = dmin - dbuffer + + if ( verb .ge. 3 ) then + print *,'after adjustment, dmin= ',dmin,' dmax= ',dmax + endif + +c Next 2 lines changed for compiler compatibility on +c other platforms.... +c contour_info%xmaxcont = dmax - amod(dmax,trkrinfo%contint) +c contour_info%xmincont = dmin - amod(dmin,trkrinfo%contint) + + tmp = trkrinfo%contint + contour_info%xmaxcont = dmax - mod(dmax,tmp) + contour_info%xmincont = dmin - mod(dmin,tmp) + + if ( verb .ge. 3 ) then + print *,'A1 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A1 contour_info%xmincont= ',contour_info%xmincont + endif + + if (contour_info%xmincont > contour_info%xmaxcont) then + contour_info%xmincont = contour_info%xmaxcont + endif + +c if (dmin > contour_info%xmincont) then +c contour_info%xmincont=contour_info%xmincont + trkrinfo%contint +c endif +c if (dmax < contour_info%xmaxcont) then +c contour_info%xmaxcont=contour_info%xmaxcont - trkrinfo%contint +c endif + + if ( verb .ge. 3 ) then + print *,'A2 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A2 contour_info%xmincont= ',contour_info%xmincont + print *,'maxconts= ',maxconts + endif + +c NOTE: In the loop below, the contour_info%contvals array is now +c (5/2003) no longer used in subsequent subroutines. But we still +c need to figure out the value of the contvals as we iterate the +c loop so we can know when we've surpassed dmax and can stop +c incrementing contour_info%numcont, which we do need in subsequent +c subroutines. + + contour_info%numcont = 0 + do n = 1,maxconts + contour_info%numcont = contour_info%numcont + 1 + contour_info%contvals(n) = contour_info%xmincont + + & float(n-1)*trkrinfo%contint +c print *,'n= ',n,' contour_info%contvals(n)= ' +c & ,contour_info%contvals(n) + if (contour_info%contvals(n) >= dmax) exit + enddo + + oldstormct = stormct + call find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) + + if (stormct > 0) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' ' + print *,'!!! ************************************************' + print *,'!!! ' + print *,'!!! NOTE: In first_ges_center, the value of stormct' + print *,'!!! returned from find_all_maxmins is not greater' + print *,'!!! than 0. This means there are no new centers' + print *,'!!! to track, which is not likely. Perhaps you are' + print *,'!!! searching over too small of an area??' + print *,'!!! ' + print *,'!!! ************************************************' + print *,' ' + endif + + endif + + print *,'ifh= ',ifh,' oldstormct= ',oldstormct + print *, ' stormct= ',stormct + + do mm = 1,300 + print *,'mm= ',mm,' maxmini(mm)= ',maxmini(mm) + & ,' maxminj(mm)= ',maxminj(mm) + enddo + + if (stormct > oldstormct .and. stormct > 0) then + isstart = oldstormct + 1 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,*) 'New search: ' + write (6,*) 'Possible new max/min locations at ifh= ',ifh + write (6,*) '--------------------------------------------' + endif + + do n = isstart,stormct + if (trkrinfo%type == 'midlat') then + storm(n)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(n)%tcv_center = 'TCG ' + endif + slonfg(n,ifh) = glonmin + (maxmini(n)-1)*dx + slatfg(n,ifh) = glatmax - (maxminj(n)-1)*dy + storm(n)%tcv_stspd = -99 + storm(n)%tcv_stdir = -99 + write (storm(n)%tcv_storm_id,'(i4.4)') n + write (storm(n)%tcv_storm_name,'(i4.4)') n + stormswitch(n) = 1 + if (cparm == 'mslp') then + + if ( verb .ge. 3 ) then + write (6,71) maxmini(n),maxminj(n),slonfg(n,ifh) + & ,360.-slonfg(n,ifh),slatfg(n,ifh) + & ,slp(maxmini(n),maxminj(n))/100.0 + endif + + endif + enddo + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' New search: ' + print *,'!!! NOTE: No new storms found in find_all_maxmins' + print *,'!!! at ifh = ',ifh,' stormct= ',stormct + print *,'!!! oldstormct= ',oldstormct + print *,' ' + endif + + endif + + 71 format (1x,'i= ',i4,' j= ',i4,' lon: ',f7.2,'E (',f6.2,'W)' + & ,2x,' lat: ',f6.2,' mslp: ',f6.1,' mb') +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) +c +c ABSTRACT: This subroutine will search an area delineated by +c input i and j indeces in order to find all local maxes or mins +c in that area. The (i,j) locations of the maxes/mins are returned +c in the maxmini and maxminj arrays. The input 3-character string +c cmaxmin will tell the subroutine to look for a "max" or a "min". +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c ibeg i-index for upper left location of grid to search +c iend i-index for lower right location of grid to search +c jbeg j-index for upper left location of grid to search +c jend j-index for lower right location of grid to search +c fxy Real array of data values +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c contour_info Type cint_stuff from module contours containing the +c the following 4 variables: +c 1. xmincont Real value for min contour level in the fxy data array +c 2. xmaxcont Real value for max contour level in the fxy data array +c 3. contvals Real array holding values of cont levels at this time +c 4. numcont Number of contour intervals found at this time +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c trkrinfo derived type containing various user-input tracker parms +c cmaxmin String that declares if "min" or "max" is being searched +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c +c OUTPUT: +c maxmini integer array containing i-indeces of the max/min points +c maxminj integer array containing j-indeces of the max/min points +c ifamret return code from this subroutine + + USE trkrparms; USE set_max_parms; USE contours + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + integer stormct,i,j,ibeg,iend,jbeg,jend,ix,jx,ixp1,ixm1 + integer ip,jp,maxstorm,jxp1,jxm1,ifamret,isret,iaret,iclmret + integer isoiret,icccret,igicwret,imax,jmax + character ccflag*1,get_last_isobar_flag*1,point_is_over_water*1 + character(*) cmaxmin + logical(1) still_finding_valid_maxmins,rough_gradient_check_okay + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real xavg,stdv,search_cutoff,dmin,dmax,sphere_cutoff + real plastbar,rlastbar,fract_land,dx,dy + +c----- + still_finding_valid_maxmins = .true. + + +c print *,'ctm beg of find_all_maxmins, maxstorm= ',maxstorm + + +c First, we want to get the mean and standard deviation of the input +c field to be searched. We can use the standard deviation info as +c part of our guideline for when to stop searching for maxes & mins. +c We will set the search cut-off threshold at 1/2 standard deviation +c above the mean for min searches. So, for the example of mslp, if +c the mean pressure over the whole domain is 1010 mb and the +c standard deviation is 12 mb, then when we are searching, if the +c lowest available (i.e., hasn't been found in a previous iteration +c of this loop) pressure is 1016, then it's time to stop searching. + + call avgcalc (fxy,imax*jmax,valid_pt,xavg,iaret) + call stdevcalc (fxy,imax*jmax,valid_pt,xavg,stdv,isret) + if (iaret /= 0 .or. isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_all_maxmins, the calls to avgcalc' + print *,'!!! and/or stdevcalc returned an error.' + print *,'!!! iaret= ',iaret,' isret= ',iaret + print *,' ' + endif + + ifamret = 98 + return + endif + + if (cmaxmin == 'min') then + search_cutoff = xavg + stdv*0.5 + else + search_cutoff = xavg - stdv*0.5 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In find_all_maxmins, search_cutoff= ',search_cutoff + print *,' ' + endif + +c Now begin to search the domain. We do a simple gridpoint scan, +c and once we find the max/min value, we pass the (i,j) coordinates +c at that point to a routine to check for a closed contour. Then +c we mask out those points in the contour (or, if there is not a +c closed contour, just the 8 points immediately surrounding the low +c center) and we do another iteration of search_loop to look for +c more lows. We mask out points we've found so that on subsequent +c iterations of search_loop, we don't find the same old center +c again and again and again..... + + search_loop: do while (still_finding_valid_maxmins) + + dmin = 9.99e20 + dmax = -9.99e20 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + ip = i + jp = j + + if (ip > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: In find_all_maxmins, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. The search' + print *,'!!! will not extend to the user-requested' + print *,'!!! grid boundary.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',ip + print *,' ' + endif + + exit iloop + + endif + endif + + if (valid_pt(ip,jp) .and..not. masked_out(ip,jp)) then + if (cmaxmin == 'min') then + if (fxy(ip,jp) < dmin) then + dmin = fxy(ip,jp) + ix = ip + jx = jp + endif + else + if (fxy(ip,jp) > dmax) then + dmax = fxy(ip,jp) + ix = ip + jx = jp + endif + endif + endif + + enddo iloop + enddo jloop + + if (cmaxmin == 'min') then + if (dmin < search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + else + if (dmax > search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + endif + +c As a rough first check, see if the neighboring points on all +c 4 sides have a gradient sloping down into the found min point, +c or at least that there is a flat field not having a gradient +c sloping away from the center point. + + call get_ijplus1_check_wrap (imax,jmax,ix,jx,ixp1,jxp1,ixm1 + & ,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In find_all_maxmins, the center we found' + print *,'!!! is too close to the grid boundary and will' + print *,'!!! NOT be checked for a closed contour.' + print *,'!!! ix= ',ix,' jx= ',jx,' fxy= ',fxy(ix,jx) + print *,'!!! ' + print *,' ' + endif + + masked_out(ix,jx) = .true. + cycle search_loop + endif + + if (cmaxmin == 'min') then + if (fxy(ix,jx) <= fxy(ixp1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxm1) .and. + & fxy(ix,jx) <= fxy(ixm1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + else + if (fxy(ix,jx) >= fxy(ixp1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxm1) .and. + & fxy(ix,jx) >= fxy(ixm1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + endif + + if (rough_gradient_check_okay) then + + if ( verb .ge. 3 ) then + print *,'Found a possible max/min at ix= ',ix,' jx= ',jx + endif + + +c From this rough check, we appear to have a gradient sloping +c in towards the center point. Now call the subroutine to +c check whether or not there is in fact a closed contour +c surrounding this local maximum or minimum. + + get_last_isobar_flag = 'n' + ccflag = 'n' + call check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,ccflag,cmaxmin,trkrinfo + & ,1,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if (ccflag == 'y') then + if (stormct < maxstorm) then + stormct = stormct + 1 + + if ( verb .ge. 3 ) then + print *,'AAA stormct= ',stormct,' ix= ',ix,' jx= ',jx + endif + + ! For a tcgen case, we will add in one additional check, + ! and that is to ensure the point is (mostly) over water. + ! Only do this check if the user has requested it (some + ! of the global models do not have a land-sea mask + ! included in the grib data files). + + point_is_over_water = 'u' + + if (trkrinfo%use_land_mask == 'y') then + call check_land_mask (imax,jmax,ix,jx,fract_land + & ,valid_pt,dx,dy,point_is_over_water,iclmret) + if (iclmret /= 0) then + print *,' ' + print *,'!!! ERROR from check_land_mask for ix= ',ix + & ,' jx= ',jx + print *,'!!! STOPPING PROGRAM' + stop 95 + endif + endif + + if (point_is_over_water /= 'n') then + maxmini(stormct) = ix + maxminj(stormct) = jx + endif + + else + + if ( verb .ge. 3 ) then + print *,'---max stormct reached, stormct= ', stormct + endif + + endif + else + + if ( verb .ge. 3 ) then + print *,'!!! contour check negative, ccflag= ',ccflag + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*-----------------------------------------------*' + print *,' ' + endif + + endif + +c Regardless of whether or not the found point turns out to have +c a closed contour, we don't want to find this local minimum or +c its 8 surrounding points again in a search on a subsequent +c iteration of this loop. + + masked_out(ix,jx) = .true. + masked_out(ix,jxp1) = .true. + masked_out(ixp1,jxp1) = .true. + masked_out(ixp1,jx) = .true. + masked_out(ixp1,jxm1) = .true. + masked_out(ix,jxm1) = .true. + masked_out(ixm1,jxm1) = .true. + masked_out(ixm1,jx) = .true. + masked_out(ixm1,jxp1) = .true. + + enddo search_loop + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine mask_based_on_wind_circ (imax,jmax,dx,dy,level + & ,valid_pt,masked_outc,trkrinfo + & ,ctlon,ctlat,cmodel_type,imbowret) +c +c ABSTRACT: This subroutine masks out grid points for a storm that +c is currently being tracked. It is called after a fix has been +c made at the current forecast hour. It is only used as a backup, +c that is, if the mslp data were not there and/or a fix position +c for mslp could not be made, then that means that the mask would +c not be able to get updated using the routine in subroutine +c check_closed_contour. But we still do need to update that mask, +c so we will instead do it based on wind circulation. We will go +c out radially from the center, starting at 40 km, then every +c 40 km from there on out. When the mean cyclonic Vt drops below +c 3 m/s, stop searching, and then mask out all grid points within +c that last-searched radius. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_outc Logical. T = data point is already accounted for, +c under the influence of another nearby max or min +c center; F = data point is available to be scanned by +c this subroutine for max or min centers. +c ctlon Fix longitude for the input parameter to this routine +c ctlon Fix latitude for the input parameter to this routine +c cmodel_type character, 'global' or 'regional' + + USE set_max_parms; USE trkrparms; USE grid_bounds + USE verbose_output; USE level_parms + + implicit none + + type (trackstuff) trkrinfo + + character(*) cmodel_type + integer, parameter :: numazim=24 + integer imax,jmax,level,imbowret,nlev,iazim,i,j + integer ibiret1,ibiret2,azimuth_ct,igvtret + integer jnfix,jsfix,iefix,iwfix + real vr(numazim),vt(numazim) + real dx,dy,ctlon,ctlat,rdist,bear,targlat,targlon + real xintrp_u,xintrp_v,grid_buffer,xmax_rdist_reached + real vt_mean,vt_azim_sum,xbear,dist,degrees + logical(1) valid_pt(imax,jmax),masked_outc(imax,jmax) + logical(1) searching_valid_pts + + imbowret = 0 + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + searching_valid_pts = .true. + + rdist = 40.0 ! units in km + xmax_rdist_reached = rdist ! units in km + + radial_loop: do while (searching_valid_pts) + + azimuth_ct = 0 + vt_azim_sum = 0.0 + vt = -999.0 + vr = -999.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (ctlat,ctlon,rdist,bear,targlat,targlon) + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + xmax_rdist_reached = rdist + exit radial_loop + endif + + ! These calls to bilin_int_uneven pass a variable, level, + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (ctlon,ctlat,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim) + & ,vt(iazim),igvtret) + azimuth_ct = azimuth_ct + 1 + vt_azim_sum = vt_azim_sum + vt(iazim) + else + xmax_rdist_reached = rdist + exit radial_loop + endif + + enddo azimloop + + if (azimuth_ct > 0) then + ! Compute azimuthally-averaged Vt at this distance + vt_mean = vt_azim_sum / float(azimuth_ct) + else + vt_mean = -999.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: rdist= ',rdist,' azimuth_ct= ',azimuth_ct + & ,' vt_azim_sum= ',vt_azim_sum,' vt_mean= ',vt_mean + endif + + if (ctlat >= 0.0) then + if (vt_mean >= 3.0) then + ! For a NH storm, if the cyclonic mean Vt >= 3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + else + if (vt_mean <= -3.0 .and. vt_mean > -998.0) then + ! For a SH storm, if the cyclonic mean Vt <= -3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + enddo radial_loop + + if ( verb .ge. 3 ) then + print *,'mbow: After radial_loop, rdist= ',rdist + & ,' xmax_rdist_reached= ',xmax_rdist_reached + endif + +c ----------------------------------------------------------------- +c At this point, we are done searching radially outwards away from +c the storm center. The max radial distance we reached is called +c xmax_rdist_reached. By getting to this spot in the subroutine, +c that means that we bumped out of radial_loop above because the +c rdist being used in that loop got to a radius at which the mean +c cyclonic Vt no longer was strong enough to continue the search +c outward, so we need to reduce it by 40 km here (back to the value +c for the last successful search). At a minimum, we will mask to a +c radius of 80 km. + + if (xmax_rdist_reached > 80.0) then + xmax_rdist_reached = xmax_rdist_reached - 40.0 + else + xmax_rdist_reached = 80.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: After adjustment of xmax_rdist_reached, rdist= ' + & ,rdist,' xmax_rdist_reached= ',xmax_rdist_reached + endif + + bearloop: do i = 1,4 + + ! Now find the values of the longitude for the farthest west + ! and east points and find the values of the latitude for the + ! farthest north and south points. The i and j indices + ! associated with these lons and lats will be used to define + ! the bounds of the grid over which we scan to find points + ! that will update the mask. + + select case (i) + case (1); xbear = 0.0; + case (2); xbear = 90.0; + case (3); xbear = 180.0; + case (4); xbear = 270.0; + end select + + call distbear (ctlat,ctlon,xmax_rdist_reached,xbear + & ,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,'mbow: distbear for i= ',i,' targlon= ',targlon + & ,' targlat= ',targlat + endif + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon > glonmax for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmax= ',glonmax + imbowret = 95 + return + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon < glonmin for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmin= ',glonmin + imbowret = 95 + return + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'either targlat > glatmx or targlat < glatmin, so' + print *,'we cannot update the mask.' + print *,'targlat= ',targlat,' glatmin= ',glatmin + print *,' glatmin= ',glatmin + imbowret = 95 + return + cycle bearloop + endif + + ! Get the i & j starting and ending points for our loop where + ! we will update the mask.... + + if (i == 1) then + + ! Get j for northern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jnfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jnfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 2) then + + ! Get i for eastern longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iefix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + elseif (i == 3) then + + ! Get i for southern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jsfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jsfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 4) then + + ! Get i for western longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iwfix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + endif + + enddo bearloop + + if ( verb .ge. 3 ) then + print *,'mbow: iwfix= ',iwfix,' iefix= ',iefix + & ,' jnfix= ',jnfix,' jsfix= ',jsfix + endif + + do i = iwfix,iefix + do j = jnfix,jsfix + + call calcdist (glon(i),glat(j),ctlon,ctlat,dist,degrees) + + if (dist < xmax_rdist_reached) then + masked_outc(i,j) = .true. + endif + + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,closed_contour,cmaxmin,trkrinfo + & ,num_requested_contours,contour_info + & ,get_last_isobar_flag,plastbar,rlastbar,icccret) +c +c ABSTRACT: This subroutine checks a field of data around an input +c (ix,jx) data point to see if a closed contour exists around +c that data point. It can check for a closed contour on a max or a +c min field, depending on the value of the input variable 'cmaxmin'. +c The algorithm works by examining rings of the 8 data points +c surrounding a data point that is in the contour interval. For +c example, in the diagram below, the X represents the location of +c the local minimum value which was passed into this routine with +c the coordinates (ix,jx), let's say it's 985 mb. And let's assume +c that the data values at points A-I are all in the 4 mb contour +c interval of 985-989 mb, and that all the surrounding points have +c data values >= 989. To test for a closed contour, we first check +c the ring of 8 points immediately around point X to see what their +c data values are. If a data value is found that is below the +c lower limit of this contour interval (985 mb) or lower than the +c local minimum value at the X point that we initially targeted +c (985 mb), then we do NOT have a closed contour, and we exit this +c subroutine. But in our example, that's not the case, and we have +c 5 points (B,D,E,F,G) that are in the interval. So in our next +c iteration of the loop, we set up 5 rings, each one set up around +c the points found in the first iteration (B,D,E,F,G), and we check +c the 8 points around each of those points. A logical array is +c used so that as soon as a point is found, it is flagged as being +c found. In this way, when we look at the ring around point D, for +c example, we won't pick point X again and set up another ring +c around it in the next ring iteration and end up in an infinite +c loop, going back and forth between point X and point D. While +c checking the 8 points in a ring, if a found data value is above +c our contour interval (i.e., >= 989 mb), we just ignore the +c point; we only mark points that are in our contour interval, +c and again, if we find a point below our contour interval, we +c exit the subroutine with a flag indicating a closed contour was +c NOT found. So in this method, we keep spreading out from the +c initial local minimum and creating and checking new rings until +c we either: (a) Hit the edge of the regional grid, in which case +c we consider a closed contour NOT found, (b) Run into a data +c point that has been marked as being under the influence of +c another nearby low, in which case we consider a closed contour +c NOT found, (c) Run into a point which is below (above) our +c contour interval for a min (max) check, in which case we +c consider a closed contour NOT found, or (d) we run out of +c points to keep searching, we have no rings left to create and +c check because all of the surrounding points are above (below) +c our contour interval for a min (max) check, and by default we +c consider this a closed contour and return to the calling +c subroutine a flag indicating such. +c +c + + + + + + + + + + +c + + + + + + + + + + +c + + A B + + + + + + +c + + C D X E + + + + +c + + + + F G + + + + +c + + + + + H I + + + +c + + + + + + + + + + +c + + + + + + + + + + +c +c UPDATE: This subroutine was updated to keep searching for +c multiple closed contours until it can't find anymore. The +c input parameter num_requested_contours dictates how many +c contours to search for. In the case of just trying to roughly +c locate new centers and establish that there is a closed +c circulation, num_requested_contours will = 1, and we will exit +c after finding that 1 contour. But for a check after making a +c full center fix, we set num_requested_contours = 999 so that +c we can keep searching for all closed contours around the low. +c In this 999 case, you will eventually get to a point where +c there is no closed contour. In that case, in the standard +c output you will see a message telling you that you hit a point +c that is not in the contour and that there is no closed contour, +c but you will also notice that the ccflag = y, meaning there is +c a closed contour (because you have found at least 1 closed +c contour along the way). The reason to keep searching for more +c closed contours is that we can then return the value of the +c outermost closed isobar. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c cmaxmin character string ('max' or 'min') that tells this +c routine what we're looking for. +c trkrinfo derived type that holds/describes various tracker parms +c contour_info Type cint_stuff from module contours. Contains +c contour information +c num_requested_contours For the simple first_ges_center check, +c this will be 1 (we just want to know if there's at +c least 1 closed contour). For the verifying check after +c we've found a center, this will be 9999 (i.e., just keep +c searching for more contours) +c get_last_isobar_flag character ('y' or 'n') to indicate whether +c or not to report on the value of the last closed isobar +c and the radius of the last closed isobar. +c +c OUTPUT: +c closed_contour character; A returned value of 'y' indicates that +c this routine was able to find a closed contour. +c plastbar Contains the value of the last closed isobar (unrounded) +c rlastbar Contains the mean radius of the last closed isobar +c +c LOCAL: +c num_pts_in_all_contours Counter for the number of pts inside of +c the contour we're looking at +c next_ring_ct Counter for the number of points that have been +c tagged to be used as center points for the next +c iteration of multiple_ring_loop. +c next_contour_ct Counter for the number of points that have been +c tagged to be used as center points in the first iteration +c through single_contour_scan_loop as we begin to scan +c points in the *next* contour interval. This counter gets +c incremented when, for example, we are searching points +c around a current center point and we find one that is not +c in our current interval, but rather is in the next +c interval. We want to remember this point and store the +c location, so we increment this counter and store the +c location in next_contour_i and next_contour_j arrays. +c beyond_contour_ct Counter for the number of points that have been +c tagged to be used as center points for some subsequent +c iteration of successive_contours_loop. This is +c different from next_contour_ct, which is used to hold +c the locations of points that are definitely in the +c *next* contour interval. Here, we have points that we +c just store in a pool of potential points to be searched +c in future iterations. These points can come about in +c cases where there is a very intense, very compact low +c with a tight pressure gradient, such that multiple +c contour intervals could be spanned in between 2 adjacent +c gridpoints (this is especially the case if the contour +c interval you have chosen is small). You need to be +c careful with how you handle this array. Once you find +c that you have searchable points in next_contour_i or +c next_contour_j, do not just simply empty out this +c beyond_contour count and its i and j arrays. The +c reason being that some of these "beyond" points may end +c up being used and searched in subsequent iterations, but +c not if we just delete them now. + + + USE set_max_parms; USE trkrparms; USE contours; USE grid_bounds + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,ir,iria,irja,irx,jrx,ix,jx,imax,jmax + integer nb,ibx,jby,nct,iflip + integer mr,ringct,ixp1,ixm1,jxp1,jxm1,nring,iter + integer icenx,jcenx,icccret,next_ring_ct,igicwret + integer num_pts_in_all_contours,next_contour_ct + integer beyond_contour_ct + integer num_pts_in_one_contour + integer num_requested_contours,num_found_contours + integer nm,im,jm,inall,insingle,isc_count,rlast_distct + character found_a_point_in_our_contour*1,closed_contour*1 + character found_a_point_below_contour*1 + character found_a_point_above_contour*1,get_last_isobar_flag*1 + character(*) cmaxmin + logical(1) still_scanning + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + logical(1) point_is_already_in_our_contour(imax,jmax) + logical(1) point_is_already_in_next_contour(imax,jmax) + logical(1) point_is_already_in_beyond_pool(imax,jmax) + integer isni,isnj,inci,incj,ibci,ibcj,ihmi,ihmj,itmi,itmj + integer, allocatable :: search_next_i(:) + integer, allocatable :: search_next_j(:) + integer, allocatable :: next_contour_i(:) + integer, allocatable :: next_contour_j(:) + integer, allocatable :: beyond_contour_i(:) + integer, allocatable :: beyond_contour_j(:) + integer, allocatable :: hold_mask_i_loc(:) + integer, allocatable :: hold_mask_j_loc(:) + integer, allocatable :: temp_mask_i_loc(:) + integer, allocatable :: temp_mask_j_loc(:) + integer, allocatable :: ringposi(:),ringposj(:) + real,allocatable :: ringpos(:,:) + real fxy(imax,jmax),contvals(maxconts) + real contlo,conthi,xcentval,contlo_next,conthi_next + real dist,degrees,rlast_distsum,plastbar,rlastbar +c + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + allocate (search_next_i(imax*jmax),stat=isni) + allocate (search_next_j(imax*jmax),stat=isnj) + allocate (next_contour_i(imax*jmax),stat=inci) + allocate (next_contour_j(imax*jmax),stat=incj) + allocate (beyond_contour_i((imax*jmax)/2),stat=ibci) + allocate (beyond_contour_j((imax*jmax)/2),stat=ibcj) + allocate (hold_mask_i_loc(imax*jmax),stat=ihmi) + allocate (hold_mask_j_loc(imax*jmax),stat=ihmj) + allocate (temp_mask_i_loc(imax*jmax),stat=itmi) + allocate (temp_mask_j_loc(imax*jmax),stat=itmj) + if (isni /= 0 .or. isnj /= 0 .or. inci /= 0 .or. incj /= 0 .or. + & ibci /= 0 .or. ibcj /= 0 .or. ihmi /= 0 .or. ihmj /= 0 .or. + & itmi /= 0 .or. itmj /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various search, hold and temp arrays.' + print *,'!!! isni = ',isni,' isnj= ',isnj + print *,'!!! inci = ',inci,' incj= ',incj + print *,'!!! ibci = ',ibci,' ibcj= ',ibcj + print *,'!!! ihmi = ',ihmi,' ihmj= ',ihmj + print *,'!!! itmi = ',itmi,' itmj= ',itmj + print *,' ' + endif + + STOP 98 + endif + + closed_contour = 'n' + xcentval = fxy(ix,jx) + num_found_contours = 0 + next_contour_ct = 0 + beyond_contour_ct = 0 + num_pts_in_all_contours = 0 + hold_mask_i_loc = 0 + hold_mask_j_loc = 0 + beyond_contour_i = 0 + beyond_contour_j = 0 + point_is_already_in_our_contour = .false. + point_is_already_in_beyond_pool = .false. + icccret = 0 + isc_count = 0 + plastbar = -999.0 + rlastbar = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* Top of check_closed_contour, ix= ',ix,' jx= ',jx + print *,'*-----------------------------------------------*' + print *,' ' + print *,'fxy(ix,jx)= ',fxy(ix,jx),' xcentval= ',xcentval + endif + +c First, set up the contour intervals that will be used. In +c the original version of this code, we used preset +c standard intervals (984,988,992,996,1000,1004....). But upon +c further review, it was decided that this was too arbitrary. +c So instead, we consider the found min (max) value to be the +c bottom (top) of the list of contour intervals. In this way, +c we can clearly specify and screen storms based on the "depth" +c of the pressure field as compared to the surroundings. + + i = 1 + do while (i <= maxconts) + if (cmaxmin == 'min') then + contvals(i) = xcentval + float(i-1)*trkrinfo%contint + i = i + 1 + else + iflip = maxconts - i + 1 + contvals(iflip) = xcentval - float(i-1)*trkrinfo%contint + i = i + 1 + endif + enddo + +c This successive_contours loop is the master loop.... + + successive_contours_loop: do while (num_found_contours < + & num_requested_contours) + +c Find the contour interval in which the center value resides. +c Note that the lower bound is included for a min check, while +c the upper bound is included for a max check. Note also that +c this subroutine can be used to find the last closed contour, +c and part of that functionality shows up in the next while +c statement where we reference "num_found_contours" in the +c array indeces for the contour values. Basically, the way we +c do this is, for example, if our central value is 990.4 mb and +c our contour interval is 4 mb, then in the first run through +c successive_contours_loop we see if we have a closed contour in +c the interval 990.4-994.4. If yes, then the next time through +c this loop, we see if we have a closed contour in the interval +c 994.4-998.4. If yes, then the next loop check is for 998.4- +c 1002.4, and so on.... We stop searching if we find a value +c that is either below the xcentval input into this subroutine +c or below the lower value of the current contour interval (this +c would mean a change in the gradient and would indicate that, +c in the case of mslp, we are heading down towards another, +c different low). + + isc_count = isc_count + 1 + + point_is_already_in_next_contour = .false. + + i = 1 + do while (i < maxconts) + if (cmaxmin == 'min') then + if (contvals(i) <= xcentval .and. xcentval < contvals(i+1)) + & then + + if ( verb .ge. 3 ) then + print *,'At A, num_found_contours= ',num_found_contours + endif + + contlo = contvals(i+num_found_contours) + conthi = contvals(i+1+num_found_contours) + + if ( verb .ge. 3 ) then + print *,'At A, contlo= ',contlo,' conthi= ',conthi + endif + exit + + endif + else + if (contvals(i) < xcentval .and. xcentval <= contvals(i+1)) + & then + contlo = contvals(i-num_found_contours) + conthi = contvals(i-num_found_contours+1) + exit + endif + endif + i = i + 1 + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'num_found_contours= ',num_found_contours + print *,'contlo= ',contlo,' conthi= ',conthi + print *,'xcentval= ',xcentval + endif + + +c This single_contour_scan_loop is the main loop for searching +c for one individual contour. If it is determined that a contour +c exists, control is returned to the successive_contours_loop, +c and if more contours were requested to be found, then the +c search continues onward & outward.... + + temp_mask_i_loc = 0 + temp_mask_j_loc = 0 + + iter = 1 + num_pts_in_one_contour = 0 + still_scanning = .true. + + rlast_distsum = 0.0 + rlast_distct = 0 + + single_contour_scan_loop: do while (still_scanning) + +c print *,' ' +c print *,' top of single contour scan loop' +c print *,'+++ iter= ',iter +c print *,' N1: next_contour_ct= ',next_contour_ct + + if (iter == 1 .and. num_found_contours == 0) then + ! For the first iteration, we have only the first ring, + ! which is centered on the input minimum/maximum point. + ringct = 1 + search_next_i(1) = ix + search_next_j(1) = jx + +c point_is_already_in_our_contour(ix,jx) = .true. +c num_pts_in_one_contour = num_pts_in_one_contour + 1 +c temp_mask_i_loc(num_pts_in_one_contour) = ix +c temp_mask_j_loc(num_pts_in_one_contour) = jx + + else if (iter == 1 .and. num_found_contours > 0) then + ! This is the first iteration in a *new* contour. + ! That is, we have already found 1 or more previous + ! contours while in previous iterations of + ! successive_contours_loop and we are now beginning + ! to look for the next contour. + +c print *,' N2: next_contour_ct= ',next_contour_ct + + if (next_contour_ct == 0) then + ! This would be for the special case in which, for + ! example, you've got a very intense, compact storm + ! that "skips" a contour. That is, suppose the + ! min pressure of a storm is 982 mb, and we are + ! utilizing a 4-mb contour interval, but all + ! surrounding data points are, say, 987 mb or + ! higher. Then, next_contour_ct would be 0 since no + ! data points were found in the next contour interval + ! of 982-986 mb, but we can continue searching since the + ! gradient is still sloping the correct way. The code in + ! this if statement handles this special case. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ALERT: next_contour_ct = 0 ' + endif + + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + +c print *,'b4 ZZ, ringct= ',ringct +c print *,'at ZZ, bcc= ',beyond_contour_ct +c & ,'contlo_next= ',contlo_next +c & ,'conthi_next= ',conthi_next + + bey_con_min_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_min_loop + endif + +c print *,'-- ZZ, ibx= ',ibx,' jby= ',jby +c & ,' fxy(ibx,jby)= ',fxy(ibx,jby) + + if (fxy(ibx,jby) >= contlo_next .and. + & fxy(ibx,jby) < conthi_next) then + +c print *,'>> ZZ HIT!!, ibx= ',ibx,' jby= ',jby +c +c print *,' +++ BEYOND in NEXT: i= ',ibx,' j= ',jby +c & ,' fxy= ',fxy(ibx,jby) + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + +c print *,'.. ZZ, next_contour_ct= ',next_contour_ct + + enddo bey_con_min_loop + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + +c print *,'At A, beyond_contour_ct= ',beyond_contour_ct +c print *,' contlo_next = ',contlo_next +c print *,' conthi_next = ',conthi_next + + bey_con_max_loop: do nb = 1,beyond_contour_ct + +c print *,'in bey_con_max_loop, nb= ',nb + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_max_loop + endif + +c print *,'ibx= ',ibx,' jby= ',jby,' data= ' +c & ,fxy(ibx,jby) + + if (fxy(ibx,jby) > contlo_next .and. + & fxy(ibx,jby) <= conthi_next) then + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + +c print *,' ++ HIT! ibx= ',ibx,' jby= ',jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + enddo bey_con_max_loop + endif + + if (next_contour_ct > 0) then + ringct = next_contour_ct + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! XXX next_contour_ct not > 0 !!!' + print *,'next_contour_ct= ',next_contour_ct + print *,'beyond_contour_ct= ',beyond_contour_ct + print *,'ringct= ',ringct + print *,'next_ring_ct= ',next_ring_ct + print *,'cycling to top of successive_contours_loop..' + print *,' ' + endif + + ! The number of rings that we have available to search + ! in the next contour interval is 0, so cycle all the + ! way back to the top of the outer loop, which is + ! successive_contours_loop, so that we can increase the + ! contour bounds and search inside those new bounds. + ! Again, this is for the case in which we have an + ! intense, compact storm and we are using a small + ! contour interval, such that we are essentially + ! "skipping" over one of these intervals in one of the + ! loop iterations. We need to bump up the + ! num_found_contours by one in order to increase the + ! array index in the contvals array at the top of the + ! successive_contours_loop. It is kosher to do this + ! since the reason we are cycling back to the top of + ! that loop is that we are skipping over a contour + ! interval. + + num_found_contours = num_found_contours + 1 + cycle successive_contours_loop + + endif + + else + + ringct = next_contour_ct + + endif + + do nring = 1,ringct + search_next_i(nring) = next_contour_i(nring) + search_next_j(nring) = next_contour_j(nring) +c print *,'at A, nring= ',nring,' next_contour_i(nring)= ' +c & ,next_contour_i(nring),' next_contour_j(nring)= ' +c & ,next_contour_j(nring) + enddo + + next_contour_ct = 0 + + else + ringct = next_ring_ct + endif + + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + allocate (ringposi(ringct),stat=iria) + allocate (ringposj(ringct),stat=irja) + if (iria /= 0 .or. irja /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various ring arrays. iria = ',iria + print *,'!!! irja = ',irja + print *,' ' + endif + + STOP 98 + endif + +ctm +c print *,' ' +c print *,'ringct= ',ringct + + do nring = 1,ringct + ringposi(nring) = search_next_i(nring) + ringposj(nring) = search_next_j(nring) +ctm +c print *,'nring= ',nring,' ringposi= ',ringposi(nring) +c & ,' ringposj= ',ringposj(nring) + enddo + + next_ring_ct = 0 + + ! This next loop reviews the points that have been + ! labelled for the "beyond_contour" pool. As we get further + ! into successive iterations of successive_contours_loop, + ! some of these previously "beyond" points are now within + ! the contour interval range that we are checking, so we + ! need to go through the list of "beyond" points and remove + ! any that are no longer in that "beyond" category.... + + check_beyond_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! This point may have been removed already in a + ! previous iteration of successive_contours_loop. + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle check_beyond_loop + endif + + ! Check to see if any of the points being searched in the + ! upcoming multiple_ring_loop are points that had previously + ! been saved as "beyond_contour" points. If so, remove + ! their status as "beyond_contour" points by setting the + ! logical flag to false. + + do nring = 1,ringct + + if (ibx == ringposi(nring) .and. jby == ringposj(nring)) + & then +c print *,' ' +c print *,'!!! beyond remove: ibx= ',ibx,' jby= ',jby + point_is_already_in_beyond_pool(ibx,jby) = .false. + endif + + enddo + + enddo check_beyond_loop + + +c In each iteration of single_contour_scan_loop, we can have a +c different number of rings to analyze. In the first +c iteration, we only have 1 ring, the initial ring around the +c local max/min that was input to this subroutine. Subsequent +c iterations will have a variable number of rings, depending on +c how many new data points within our contour interval were +c found in the previous iteration. + + multiple_ring_loop: do mr = 1,ringct + + icenx = ringposi(mr) + jcenx = ringposj(mr) + +ctm +c print *,' --- iter= ',iter,' mr= ',mr,' icenx= ',icenx +c & ,' jcenx= ',jcenx,' imax= ',imax,' jmax= ',jmax + + call get_ijplus1_check_wrap (imax,jmax,icenx,jcenx,ixp1,jxp1 + & ,ixm1,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NO CLOSED CONTOUR: The call to ' + print *,'!!! get_ijplus1_check_wrap indicates the' + print *,'!!! max/min contour extends past the edge of' + print *,'!!! our regional grid. ' + print *,' ' + print *,' ' + endif + + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c For each individual ring, we check all 8 points surrounding +c the center point. The points are numbered for each ring as +c shown in the diagram to the right of the "select case" +c statement just below. REMEMBER: The j in our grids +c increases from north to south, so that for a global grid, +c j=1 is at 90N and j=jmax is at 90S. + + individual_ring_loop: do ir = 1,9 + + select case (ir) + case (1); irx=ixm1; jrx=jcenx;! 2 3 4 + case (2); irx=ixm1; jrx=jxm1; ! + case (3); irx=icenx;jrx=jxm1; ! + case (4); irx=ixp1; jrx=jxm1; ! 1 (icenx,jcenx) 5 + case (5); irx=ixp1; jrx=jcenx;! + case (6); irx=ixp1; jrx=jxp1; ! + case (7); irx=icenx;jrx=jxp1; ! 8 7 6 + case (8); irx=ixm1; jrx=jxp1; ! + case (9); irx=icenx; jrx=jcenx; ! = center pt of ring + end select + +c Make sure the point we are looking at has valid data. +c This is an issue only on regional grids, where we have a +c buffer of bitmapped (null) data points surrounding the +c real grid. + +c print *,'ind ring loop: ir= ',ir,' irx= ',irx,' jrx= ',jrx + + if (.not. valid_pt(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a non-' + print *,'!!! valid point, meaning we are near the' + print *,'!!! bounds of the grid, or at least the ' + print *,'!!! bounds of the valid data for this ' + print *,'!!! grid. We will skip the' + print *,'!!! search for this center.' + print *,'!!! ' + print *,'!!! (i,j) of non-valid pt = (' + & ,irx,',',jrx,')' + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c Check to make sure that the point we are looking at is +c not considered under the influence of another nearby low. + + if (masked_out(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a point' + print *,'!!! that has been masked out, meaning it' + print *,'!!! belongs under the influence of ' + print *,'!!! another nearby low, so we will skip' + print *,'!!! the search for this center....' + print *,'!!! ' + print *,'!!! Min central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Masked-out value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of masked value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we have already hit this point on a previous ring +c check, then just ignore this point and cycle past it. + + if (point_is_already_in_our_contour(irx,jrx)) then +ctm +c print *,' ' +c print *,'Pt. AAA, already-in-contour.....' +c print *,'irx= ',irx,' jrx= ',jrx + cycle individual_ring_loop + endif + +c For a MIN check, check to see if the data point is below +c the contour interval or is below the local minimum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c For a MAX check, check to see if the data point is above +c the contour interval or is above the local maximum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c +c For example, for mslp, this would be as we're moving +c outward away from lower pressures to higher pressures, +c and then all of a sudden we come upon a lower pressure. +c This probably means we're heading toward another low +c pressure area, so mark the point and return to the +c calling routine. + + found_a_point_below_contour = 'n' + found_a_point_above_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) < xcentval .or. fxy(irx,jrx) < contlo) + & then + found_a_point_below_contour = 'y' + endif + else + if (fxy(irx,jrx) > xcentval .or. fxy(irx,jrx) > conthi) + & then + found_a_point_above_contour = 'y' + endif + endif + + if (found_a_point_below_contour == 'y' .or. + & found_a_point_above_contour == 'y') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a data' + print *,'!!! value that is less (greater) than the' + print *,'!!! current contour interval bound for a' + print *,'!!! min (max) and/or is less (greater) ' + print *,'!!! than the minimum (maximum) central ' + print *,'!!! value that we are centering the ' + print *,'!!! search on.' + print *,'!!! ' + print *,'!!! Central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Flagged value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of flagged value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we've made it this far, then we at least know that the +c gradient is still heading in the right direction. Do the +c check now to see if the value at this point is within our +c specific contour interval (there is the possibility that +c the value is beyond our interval, which will be checked +c for just below, and if that's the case, then that point +c will be processed in a subsequent iteration of this loop +c that encompasses that correct contour interval). + + found_a_point_in_our_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) >= contlo .and. fxy(irx,jrx) < conthi) + & then + found_a_point_in_our_contour = 'y' + endif + else + if (fxy(irx,jrx) > contlo .and. fxy(irx,jrx) <= conthi) + & then + found_a_point_in_our_contour = 'y' + endif + endif + + if (found_a_point_in_our_contour == 'y') then + ! We've found a data point in our interval, something + ! that is inside the closed contour, and it hasn't been + ! marked as being found in a previous iteration of this + ! loop, so mark it now and store the (i,j) location so + ! that we can scan a ring around this point in a + ! successive iteration of this loop for more potential + ! points within this interval... + + point_is_already_in_our_contour(irx,jrx) = .true. + + next_ring_ct = next_ring_ct + 1 + search_next_i(next_ring_ct) = irx + search_next_j(next_ring_ct) = jrx + +c print *,'at B, next_ring_ct= ',next_ring_ct +c & ,' search_next_i()= ',search_next_i(next_ring_ct) +c & ,' search_next_j()= ',search_next_j(next_ring_ct) + + num_pts_in_one_contour = num_pts_in_one_contour + 1 + temp_mask_i_loc(num_pts_in_one_contour) = irx + temp_mask_j_loc(num_pts_in_one_contour) = jrx + + if (get_last_isobar_flag == 'y') then + call calcdist (glon(ix),glat(jx) + & ,glon(irx),glat(jrx),dist,degrees) + rlast_distsum = rlast_distsum + dist + rlast_distct = rlast_distct + 1 + endif + +ctm +c print *,' ' +c print *,' PT IN! irx= ',irx,' jrx= ',jrx,' xval= ' +c & ,fxy(irx,jrx) +c print *,'next_ring_ct= ',next_ring_ct +c print *,'num_pts_in_one_contour= ' +c & ,num_pts_in_one_contour + endif + +c If we've made it this far AND the +c found_a_point_in_our_contour flag indicates that this +c point is not in our contour interval, then by default that +c means that this point is for a contour interval beyond +c what we're currently looking at. E.g., if we're looking +c at the contours around a 972 mb low and we're moving +c outward and currently checking the 984-988 mb contour +c interval, it means that we found, say, a gridpoint with +c 991 mb. So we want to mark that point for a future +c iteration of this loop that would be checking the +c 988-992 mb contour interval. + + if (found_a_point_in_our_contour /= 'y' .and. + & .not. point_is_already_in_next_contour(irx,jrx)) then + ! We've found a data point that is beyond our interval, + ! so this is not a concern for finding the bounds of + ! our current contour interval, but we want to mark + ! these points and remember them for the next iteration + ! of successive_scan_loop. (For example, suppose we + ! are currently searching for points in the 984-988 mb + ! range, and we find a point that is 990 -- mark it + ! here to be remembered when we scan for 988-992 mb). + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + if (fxy(irx,jrx) >= contlo_next .and. + & fxy(irx,jrx) < conthi_next) then + ! "NEXT_CONTOUR" Comment: + ! We've found a point that is in the very next + ! contour interval.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. + else if (fxy(irx,jrx) >= conthi_next) then + ! "BEYOND_CONTOUR" Comment: + ! This point is at least 1 contour interval beyond + ! the next contour interval. Dump the info into + ! these i and j arrays. This info will be used if + ! in the next iteration of single_contour_scan_loop, + ! next_contour_ct = 0. That would mean that we + ! have, e.g., an intensely deep low with a sharp + ! mslp gradient that essentially "skips" over a + ! contour interval. E.g., if using a 4 mb interval, + ! we go from 947 to 953 AND there are NO + ! intervening gridpoints in the 948-952 interval. + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) + endif + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + if (fxy(irx,jrx) > contlo_next .and. + & fxy(irx,jrx) <= conthi_next) then + ! See "NEXT_CONTOUR" comment just above.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. +c print *,'NEXT ncc= ',next_contour_ct +c & ,'next_contour_i()= ' +c & ,next_contour_i(next_contour_ct) +c & ,'next_contour_j()= ' +c & ,next_contour_j(next_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + else if (fxy(irx,jrx) <= contlo_next) then + ! See "BEYOND_CONTOUR" comment just above.... + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'BEYOND bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + endif + endif + endif + + enddo individual_ring_loop + + enddo multiple_ring_loop + + if (next_ring_ct > 0) then + iter = iter + 1 + else + icccret = 0 + still_scanning = .false. + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + num_found_contours = num_found_contours + 1 + closed_contour = 'y' + if (num_found_contours == 1) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Closed contour found ' + endif + + endif + endif + + enddo single_contour_scan_loop + + do insingle = 1,num_pts_in_one_contour + num_pts_in_all_contours = num_pts_in_all_contours + 1 + inall = num_pts_in_all_contours + hold_mask_i_loc(inall) = temp_mask_i_loc(insingle) + hold_mask_j_loc(inall) = temp_mask_j_loc(insingle) + enddo + + if (get_last_isobar_flag == 'y') then + if (cmaxmin == 'min') then + plastbar = conthi + else + plastbar = contlo + endif + if (rlast_distct > 0) then + rlastbar = rlast_distsum / float(rlast_distct) + rlastbar = rlastbar * 0.539638 ! convert km to nm + else + rlastbar = -999.0 + endif + endif + + enddo successive_contours_loop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'END SUM: num of iterations = ',isc_count + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_land_mask (imax,jmax,ix,jx,fract_land,valid_pt + & ,dx,dy,point_is_over_water,iclmret) +c +c ABSTRACT: This subroutine looks at the values for the land-sea +c mask surrounding an input (i,j) position to determine if less +c than 50% of the area surrounding the input (i,j) position within +c 75 km radius is land. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c +c OUTPUT: +c fract_land Fraction of points/area that is covered by land +c point_is_over_water y/n: A value of 'y' is returned if <50% +c of the points/area is covered by land +c iclmret Return code from this routine +c + USE grid_bounds; USE tracked_parms + USE trkrparms; USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + logical(1) valid_pt(imax,jmax) + character point_is_over_water*1 + integer, parameter :: numazim=8 + integer iazim,ibiret1,imax,jmax,ix,jx,iclmret,imct + real bear,targlat,targlon,xplon,yplat,rdist,xintrp_mask + real fract_land,dx,dy,xmask_sum +c + iclmret = 0 + +c First, calculate the longitude and latitude of the input ix and +c jx points. If the xplon value ends up being >360.0 (this can +c happen for basin-scale HWRF), don't worry about it. Just leave +c it be, as the trigonometry will work out the same for lons >360. + + xplon = glonmin + (ix-1)*dx + yplat = glatmax - (jx-1)*dy + + rdist = 75.0 ! (We will always look only 75 km radius out for + ! this particular land-sea mask application) + + imct = 0 + +c Now go around the storm via azimloop and get interpolated +c values of the land-sea mask at each azimuth at a radial +c distance of 75 km from the center point.... + + xmask_sum = 0.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 45.0 + + call distbear (yplat,xplon,rdist,bear,targlat,targlon) + + ! These calls to bilin_int_uneven pass a variable, level, + ! that is used for applications of interpolating wind + ! data. Here, we are instead interpolating the land-sea + ! mask data, so we don't care about the level, so just + ! pass a dummy value of 850, which never gets used. + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,850,'m',xintrp_mask,ibiret1) + + if (ibiret1 == 0) then + xmask_sum = xmask_sum + xintrp_mask + imct = imct + 1 + else + iclmret = 95 + return + endif + + enddo azimloop + +c Now get the mask value directly at the point that was input to +c this routine.... + + xmask_sum = xmask_sum + lsmask(ix,jx) + imct = imct + 1 + +c Now get the mean land fraction.... + + if (imct > 0) then + + fract_land = xmask_sum / float(imct) + if (fract_land < 0.50) then + point_is_over_water = 'y' + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Land check yes: Point is over water. ' + print *,' Land check value: fract_land= ',fract_land + endif + else + point_is_over_water = 'n' + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Land check NO: Point is over land. ' + print *,' Land check value: fract_land= ',fract_land + endif + endif + + else + + iclmret = 95 + return + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine get_ijplus1_check_wrap (imax,jmax,i,j,iplus1,jplus1 + & ,iminus1,jminus1,trkrinfo,igicwret) +c +c ABSTRACT: This subroutine takes an (i,j) position input and +c returns the four neighboring (i,j) points to the east, south, +c west and north. The routine checks for wrap around the GM, so +c that if, for example, you are on a global 360x181 grid and you +c are at point i=360, then i+1 = 361, so you need something to +c adjust that back to i = 1. Likewise, if you are at i=1 and +c looking for point i-1, it will adjust it to be point 360 +c instead of the meaningless point 0 (i=0). + + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer i,j,imax,jmax,iplus1,jplus1,iminus1,jminus1,igicwret + + igicwret = 0 + + jplus1 = j + 1 + jminus1 = j - 1 + iplus1 = i + 1 + iminus1 = i - 1 + + if (iplus1 > imax) then + if (trkrinfo%gridtype == 'global') then + iplus1 = iplus1 - imax ! If wrapping east of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is too close to the eastern bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested eastern ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',iplus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (iminus1 < 1) then + if (trkrinfo%gridtype == 'global') then + iminus1 = imax + iminus1 ! If wrapping west of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested western search boundary' + print *,'!!! is too close to the western bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested western ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested western i = ',iminus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (jplus1 > jmax .or. jminus1 < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The ' + print *,'!!! user-requested northern or southern search' + print *,'!!! boundary is too close to the bounds of the' + print *,'!!! grid. Cut back your requested northern or' + print *,'!!! southern boundary by a degree or 2 in the' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested northern j = ',jminus1 + print *,'!!! User-requested southern j = ',jplus1 + print *,'!!! jmax of grid = ',jmax + print *,' ' + endif + + igicwret = 91 + return + endif + + return + end + +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + SUBROUTINE qsort(x,ind,n) +c +c Code converted using TO_F90 by Alan Miller +c Date: 2002-12-18 Time: 11:55:47 + + IMPLICIT NONE + INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(12, 60) + + REAL (dp), INTENT(IN) :: x(n) + INTEGER, INTENT(OUT) :: ind(n) + INTEGER, INTENT(IN) :: n + +c *************************************************************************** +c +c ROBERT RENKA +c OAK RIDGE NATL. LAB. +c +c THIS SUBROUTINE USES AN ORDER N*LOG(N) QUICK SORT TO SORT A REAL (dp) +c ARRAY X INTO INCREASING ORDER. THE ALGORITHM IS AS FOLLOWS. IND IS +c INITIALIZED TO THE ORDERED SEQUENCE OF INDICES 1,...,N, AND ALL INTERCHANGES +c ARE APPLIED TO IND. X IS DIVIDED INTO TWO PORTIONS BY PICKING A CENTRAL +c ELEMENT T. THE FIRST AND LAST ELEMENTS ARE COMPARED WITH T, AND +c INTERCHANGES ARE APPLIED AS NECESSARY SO THAT THE THREE VALUES ARE IN +c ASCENDING ORDER. INTERCHANGES ARE THEN APPLIED SO THAT ALL ELEMENTS +c GREATER THAN T ARE IN THE UPPER PORTION OF THE ARRAY AND ALL ELEMENTS +c LESS THAN T ARE IN THE LOWER PORTION. THE UPPER AND LOWER INDICES OF ONE +c OF THE PORTIONS ARE SAVED IN LOCAL ARRAYS, AND THE PROCESS IS REPEATED +c ITERATIVELY ON THE OTHER PORTION. WHEN A PORTION IS COMPLETELY SORTED, +c THE PROCESS BEGINS AGAIN BY RETRIEVING THE INDICES BOUNDING ANOTHER +c UNSORTED PORTION. +c +c INPUT PARAMETERS - N - LENGTH OF THE ARRAY X. +c +c X - VECTOR OF LENGTH N TO BE SORTED. +c +c IND - VECTOR OF LENGTH >= N. +c +c N AND X ARE NOT ALTERED BY THIS ROUTINE. +c +c OUTPUT PARAMETER - IND - SEQUENCE OF INDICES 1,...,N PERMUTED IN THE SAME +c FASHION AS X WOULD BE. THUS, THE ORDERING ON +c X IS DEFINED BY Y(I) = X(IND(I)). +c +c ********************************************************************* + + ! NOTE -- IU AND IL MUST BE DIMENSIONED >= LOG(N) WHERE LOG HAS BASE 2. + + !********************************************************************* + + INTEGER :: iu(21), il(21) + INTEGER :: m, i, j, k, l, ij, it, itt, indx + REAL :: r + REAL (dp) :: t + + ! LOCAL PARAMETERS - + + ! IU,IL = TEMPORARY STORAGE FOR THE UPPER AND LOWER + ! INDICES OF PORTIONS OF THE ARRAY X + ! M = INDEX FOR IU AND IL + ! I,J = LOWER AND UPPER INDICES OF A PORTION OF X + ! K,L = INDICES IN THE RANGE I,...,J + ! IJ = RANDOMLY CHOSEN INDEX BETWEEN I AND J + ! IT,ITT = TEMPORARY STORAGE FOR INTERCHANGES IN IND + ! INDX = TEMPORARY INDEX FOR X + ! R = PSEUDO RANDOM NUMBER FOR GENERATING IJ + ! T = CENTRAL ELEMENT OF X + + IF (n <= 0) RETURN + + ! INITIALIZE IND, M, I, J, AND R + + DO i = 1, n + ind(i) = i + END DO + m = 1 + i = 1 + j = n + r = .375 + + ! TOP OF LOOP + + 20 IF (i >= j) GO TO 70 + IF (r <= .5898437) THEN + r = r + .0390625 + ELSE + r = r - .21875 + END IF + + ! INITIALIZE K + + 30 k = i + + ! SELECT A CENTRAL ELEMENT OF X AND SAVE IT IN T + + ij = i + r*(j-i) + it = ind(ij) + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) > t) THEN + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + END IF + + ! INITIALIZE L + + l = j + + ! IF THE LAST ELEMENT OF THE ARRAY IS LESS THAN T, + ! INTERCHANGE IT WITH T + indx = ind(j) + IF (x(indx) >= t) GO TO 50 + ind(ij) = indx + ind(j) = it + it = indx + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) <= t) GO TO 50 + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + GO TO 50 + + ! INTERCHANGE ELEMENTS K AND L + + 40 itt = ind(l) + ind(l) = ind(k) + ind(k) = itt + + ! FIND AN ELEMENT IN THE UPPER PART OF THE ARRAY WHICH IS + ! NOT LARGER THAN T + + 50 l = l - 1 + indx = ind(l) + IF (x(indx) > t) GO TO 50 + + ! FIND AN ELEMENT IN THE LOWER PART OF THE ARRAY WHCIH IS NOT SMALLER THAN T + + 60 k = k + 1 + indx = ind(k) + IF (x(indx) < t) GO TO 60 + + ! IF K <= L, INTERCHANGE ELEMENTS K AND L + + IF (k <= l) GO TO 40 + + ! SAVE THE UPPER AND LOWER SUBSCRIPTS OF THE PORTION OF THE + ! ARRAY YET TO BE SORTED + + IF (l-i > j-k) THEN + il(m) = i + iu(m) = l + i = k + m = m + 1 + GO TO 80 + END IF + + il(m) = k + iu(m) = j + j = l + m = m + 1 + GO TO 80 + + + ! BEGIN AGAIN ON ANOTHER UNSORTED PORTION OF THE ARRAY + + 70 m = m - 1 + IF (m == 0) RETURN + i = il(m) + j = iu(m) + + 80 IF (j-i >= 11) GO TO 30 + IF (i == 1) GO TO 20 + i = i - 1 + + ! SORT ELEMENTS I+1,...,J. NOTE THAT 1 <= I < J AND J-I < 11. + + 90 i = i + 1 + IF (i == j) GO TO 70 + indx = ind(i+1) + t = x(indx) + it = indx + indx = ind(i) + IF (x(indx) <= t) GO TO 90 + k = i + + 100 ind(k+1) = ind(k) + k = k - 1 + indx = ind(k) + IF (t < x(indx)) GO TO 100 + + ind(k+1) = it + GO TO 90 + END SUBROUTINE qsort + +c +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT11/FORT31 + subroutine open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + +C ABSTRACT: This subroutine must be called before any attempt is +C made to read from the input GRIB files. The GRIB and index files +C are opened with a call to baopenr. This call to baopenr was not +C needed in the cray version of this program (the files could be +C opened with a simple Cray assign statement), but the GRIB-reading +C utilities on the SP do require calls to this subroutine (it has +C something to do with the GRIB I/O being done in C on the SP, and +C the C I/O package needs an explicit open statement). +C +C INPUT: +c inp Contains user-input info on the date & data +C lugb The Fortran unit number for the GRIB data file +C lugi The Fortran unit number for the GRIB index file +c ifh integer index for lead time level +c gfilename If using individual files for each tau, gfilename will +c contain the grib data filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +c ifilename If using individual files for each tau, gfilename will +c contain the grib index filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +C lout The Fortran unit number for the output grib file +C +C OUTPUT: +C iret The return code from this subroutine + + USE inparms + USE verbose_output + + implicit none +c + type (datecard) inp + + logical(1) output_file_open + logical(1) file_open + character(*) gfilename,ifilename +c character(120) gopen_g_file,gopen_i_file +cPeng 05/28/2018 Bug Fixed for FV3-GFS Job Crashed. + character(255) gopen_g_file,gopen_i_file + character(2) lugb_c,lugi_c + character(6) enameb,enamei + integer igoret,iioret,iooret,lugb,lugi,lout,iret,nlen1,nlen2 + + iret=0 + + if (inp%file_seq == 'onebig') then + write(lugb_c,'(i2)')lugb + write(lugi_c,'(i2)')lugi + enameb='FORT'//adjustl(lugb_c) + enamei='FORT'//adjustl(lugi_c) + call get_environment_variable(enameb,gopen_g_file,status=igoret) + call get_environment_variable(enamei,gopen_i_file,status=iioret) +c if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then +cPENG---2018-06-07-------------------------------- + if (igoret /= 0 .or. iioret /= 0 ) then + gopen_g_file(1:5) = "fort." + gopen_i_file(1:5) = "fort." + write(gopen_g_file(6:7),'(I2)') lugb + write(gopen_i_file(6:7),'(I2)') lugi + endif + else + nlen1 = len_trim(gfilename) + gopen_g_file = trim(gfilename(1:nlen1)) + nlen2 = len_trim(ifilename) + gopen_i_file = trim(ifilename(1:nlen2)) + endif + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + +c print *,'gopen_g_file= ',gopen_g_file,'....' +c print *,'gopen_i_file= ',gopen_i_file,'....' + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end + +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT12 + subroutine read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + ii=1 + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + maxstorm = numtcv + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that you have the Fortran' + print *,'!!! unit assigned right in your script.' + endif + + iret = 99 + return + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT14 + subroutine read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + ii = numtcv + 1 + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1,1x + & ,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end + +cPENG----2018-06-07 ------------------------ + subroutine get_ushe_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanushe,igridushe,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,iushet) +c +cABSTRACT: This subroutine finds the minimum and mean U-shear values +c between 200 & 850 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cPENG----2018-06-07--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07------------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanushe,dx,dy,re,ri,parmlon,parmlat + real igridushe,imeanushe + integer n,ix1,ix2,npts,imax,jmax,iushet,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ushe_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + iushet = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_ushe_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + iushet = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max ushe values at 200 and 850 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_ushe_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_ushe_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeanushe = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (ushear(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid ushe data for this level, so + ! we first call barnes now to get the mean ushe + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then +c cvort_maxmin = 'max' +c else + cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'ushe' + & ,ushear(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanushe + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cPENG--------------------------- +c imeanushe = int (xmeanushe + 0.5) + imeanushe = xmeanushe + else + imeanushe = -999. + igridushe = -999. + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_ushe_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for ushe values will not be done.') + exit report_ushe_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanushe = -999. + igridushe = -999. + exit report_ushe_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeanushe,imeanushe + 621 format (1x,' xmeanushe= ',f9.3,' imeanushe= ',f9.3) + write (6,*) ' --- mean ushe raw = ',xmeanushe + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! ushe data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,ushear(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanushe,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cPENG--------------------------- +c igridushe = int (gridpoint_maxmin + 0.5) + igridushe = gridpoint_maxmin + else + igridushe = -999. + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridushe,ifilret + 623 format (1x, + & ' grid ushe= ',f9.3,' igrid ushe= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid ushe raw= ',gridpoint_maxmin + endif + + enddo report_ushe_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get U-shear for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +cPENG----2018-06-07-------------------------------------------------- +c----------------------------------------------------------------------- + subroutine get_rhum_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanrhum,igridrhum,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,irhumt) +c +c ABSTRACT: This subroutine finds the minimum and mean RH values +c at 500 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cPENG----2018-06-07--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07------------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanrhum,dx,dy,re,ri,parmlon,parmlat + real igridrhum,imeanrhum + integer n,ix1,ix2,npts,imax,jmax,irhumt,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_rhum_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + irhumt = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_rhum_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + irhumt = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max rhum values at 500 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_rhum_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_rhum_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeanrhum = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (rhumid(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid rhum data for this level, so + ! we first call barnes now to get the mean rhum + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then +c cvort_maxmin = 'max' +c else + cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'rhum' + & ,rhumid(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanrhum + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cPENG------------------------------------------------- +c imeanrhum = int (xmeanrhum + 0.5) + imeanrhum = xmeanrhum + else + imeanrhum = -99.0 + igridrhum = -99.0 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_rhum_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for rhum values will not be done.') + exit report_rhum_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanrhum = -99.0 + igridrhum = -99.0 + exit report_rhum_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeanrhum,imeanrhum + 621 format (1x,' xmeanrhum= ',f9.3,' imeanrhum= ',f9.3) + write (6,*) ' --- mean rhum raw = ',xmeanrhum + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! rhum data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,rhumid(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanrhum,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cPENG------------------------------------------------- +c igridrhum = int (gridpoint_maxmin + 0.5) + igridrhum = gridpoint_maxmin + else + igridrhum = -99.0 + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridrhum,ifilret + 623 format (1x, + & ' grid rhum= ',f9.3,' igrid rhum= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid rhum raw= ',gridpoint_maxmin + endif + + enddo report_rhum_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get RH for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end + +cPENG----2018-06-07------------------------------------------- +c This is for area mean caculations +c--------------------------------------------------------------------- + subroutine fix_latlon_mean_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +cPENG----2018-06-07--------------------------------------- + real dsum, dnum + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.10) then + grfact = 4 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif +cPENG----2012--04--18---10X10 average ------------------------- + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (4*grfact) + iend = ipfix + (5*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (5*grfact) + iend = ipfix + (4*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (4*grfact) + iend = ipfix + (4*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (5*grfact) + jend = jpfix + (4*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (4*grfact) + jend = jpfix + (5*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (4*grfact) + jend = jpfix + (4*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix +cPENG----2018-06-07--------------------------------------- + dsum=0.0 + dnum=0.0 + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif +cPENG----2018-06-07--------------------------------------- + dsum=dsum+fxy(i,j) + dnum=dnum+1 + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +cPENG----2018-06-07--------------------------------------- + if (dnum.gt.0.0) then + gridpoint_maxmin=dsum/dnum + else + gridpoint_maxmin=-9999.0 + endif + + print *,'istart=',istart,'iend=',iend + print *,'jstart=',jstart,'jend=',jend + print *,'End of fix_latlon_mean_ij, gridpoint_maxmin = ' + & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +cPENG----2018-06-07------10X10 area mean temperature----------------- +c----------------------------------------------------------------------- + subroutine get_temp_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeantemp,igridtemp,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,itempt) +c +c ABSTRACT: This subroutine finds the maximum and mean temperature +c between 300 & 500 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms +c USE trkrparms; USE level_parms; USE grid_bounds; USE inparms +cPENG----2018-06-07--------------------------- + USE trkrparms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + +c logical(1) readflag(14),valid_pt(imax,jmax),compflag +cPENG----2018-06-07------------------------------ + logical(1) readflag(19),valid_pt(imax,jmax),compflag + + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeantemp,dx,dy,re,ri,parmlon,parmlat + real igridtemp,imeantemp + integer n,ix1,ix2,npts,imax,jmax,itempt,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_temp_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + itempt = 99 + return + endif + endif + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_temp_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + itempt = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max temp values at 300 and 500 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_temp_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_temp_loop: do n=1,1 + + gridpoint_maxmin = -9999.0 + xmeantemp = -9999.0 + compflag = .true. + +c select case (n) +c case (1); ilev=501 ! For 200 and 850 mb shear +c case (2); ilev=700 ! For 700 mb +c end select + + if (tmean(ilonfix,jlatfix) > -9990.0) then + + ! ------------------------------------------- + ! We have valid temp data for this level, so + ! we first call barnes now to get the mean temp + ! surrounding our found center position. + ! ------------------------------------------- + +c if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' +c else +c cvort_maxmin = 'min' +c endif + call find_maxmin (imax,jmax,dx,dy,'temp' + & ,tmean(1,1),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeantemp + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds +cPENG------------------- +c imeantemp = int (xmeantemp + 0.5) + imeantemp = xmeantemp + else + imeantemp = -99.0 + igridtemp = -99.0 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_temp_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for temp values will not be done.') + exit report_temp_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeantemp = -99.0 + igridtemp = -99.0 + exit report_temp_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) xmeantemp,imeantemp + 621 format (1x,' xmeantemp= ',f9.3,' imeantemp= ',f9.3) + write (6,*) ' --- mean temp raw = ',xmeantemp + endif + + ! ----------------------------------------------- + ! Call fix_latlon_mean_ij to get the average + ! temp data value(gridpoint_maxmin). + ! ----------------------------------------------- + + call fix_latlon_mean_ij (imax,jmax,dx,dy + & ,tmean(1,1),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeantemp,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then +cPENG------------------- +c igridtemp = int (gridpoint_maxmin + 0.5) + igridtemp = gridpoint_maxmin + else + igridtemp = -99.0 + endif + + if ( verb .ge. 3 ) then + write (6,623) gridpoint_maxmin,igridtemp,ifilret + 623 format (1x, + & ' grid temp= ',f9.3,' igrid temp= ',f9.3,' ifilret= ',i3) + write (6,*) ' --- grid temp raw= ',gridpoint_maxmin + endif + + enddo report_temp_loop + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get T-mean for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/gettrk_gen_modules.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_modules.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/gettrk_gen_modules.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_modules.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/gettrk_gen_modules.f_original b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_modules.f_original similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/gettrk_gen_modules.f_original rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/gettrk_gen_modules.f_original diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile index 010e694618..96a1b9f17c 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc +FCOMP= mpiifort -nofree +CCOMP= mpiicc FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) CFLAGS= -O3 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_hera similarity index 86% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_hera index b39567e899..6d6c0465d9 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_hera @@ -1,10 +1,10 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc -FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 -CFLAGS= -O2 -axSSE4.2,AVX,CORE-AVX2 +FCOMP= mpiifort -nofree +CCOMP= mpiicc +FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) +CFLAGS= -O2 gettrk_gen_gfs: gettrk_gen_main_gfs.f gettrk_gen_modules.o module_waitfor.o cwaitfor.o @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_jet new file mode 100644 index 0000000000..0338ae6c0d --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_jet @@ -0,0 +1,37 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +FCOMP= mpiifort -nofree +CCOMP= mpiicc +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +CFLAGS= -O3 -axSSE4.2,AVX,CORE-AVX2 + +gettrk_gen_gfs: gettrk_gen_main_gfs.f gettrk_gen_modules.o module_waitfor.o cwaitfor.o + @echo " " + @echo " Compiling the main tracking program and subroutines....." + $(FCOMP) $(FFLAGS) gettrk_gen_modules.o module_waitfor.o cwaitfor.o gettrk_gen_main_gfs.f $(LIBS) -o gettrk_gen_gfs + @echo " " + +cwaitfor.o: cwaitfor.c + @echo " " + @echo " Compiling the waitfor C routine...." + $(CCOMP) $(CFLAGS) -c cwaitfor.c -o cwaitfor.o + +module_waitfor.o: module_waitfor.f + @echo " " + @echo " Compiling the waitfor fortran module...." + $(FCOMP) $(FFLAGS) -c module_waitfor.f -o module_waitfor.o + +gettrk_gen_modules.o: gettrk_gen_modules.f + @echo " " + @echo " Compiling the regular tracker fortran modules....." + $(FCOMP) $(FFLAGS) -c gettrk_gen_modules.f -o gettrk_gen_modules.o + @echo " " + +CMD = gettrk_gen_gfs + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_orion index e90eb8a782..2aac8db954 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_orion @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc +FCOMP= mpiifort -nofree +CCOMP= mpiicc #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) CFLAGS= -O3 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/module_waitfor.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/module_waitfor.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gen_gfs.fd/module_waitfor.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gen_gfs.fd/module_waitfor.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/cwaitfor.c b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/cwaitfor.c similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/cwaitfor.c rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/cwaitfor.c diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/gettrk_main_gfs.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_main_gfs.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/gettrk_main_gfs.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_main_gfs.f diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_main_gfs.f_07262019 b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_main_gfs.f_07262019 new file mode 100644 index 0000000000..73dc691e32 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_main_gfs.f_07262019 @@ -0,0 +1,25609 @@ + program trakmain +c +c$$$ MAIN PROGRAM DOCUMENTATION BLOCK +c +c Main Program: GETTRK Track model vortices +C PRGMMR: MARCHOK ORG: NP22 DATE: 2002-05-20 +c +c ABSTRACT: This program tracks the average of the max or min +c of several parameters in the vicinity of an input +c first guess (lat,lon) position of a vortex in order to give +c forecast position estimates for that vortex for a given numerical +c model. For the levels 700 & 850 mb, the tracked parameters are: +c Relative vorticity (max), wind magnitude (min), and geopotential +c height (min). Also tracked is the min in the MSLP. So many +c parameters are tracked in order to provide more accurate position +c estimates for weaker storms, which often have poorly defined +c structures/centers. Currently, the system is set up to be able +c to process GRIB input data files from the GFS, MRF, UKMET, GDAS, +c ECMWF, NGM, NAM and FNMOC/NAVGEM models. Two 1-line files +c are output from this program, both containing the forecast fix +c positions that the tracker has obtained. One of these output +c files contains the positions at every 12 hours from forecast +c hour 0 to the end of the forecast. The other file is in ATCF +c format, which is the particular format needed by the Tropical +c Prediction Center, and provides the positions at forecast hours +c 12, 24, 36, 48 and 72, plus the maximum wind near the storm center +c at each of those forecast hours. +c +c Program history log: +c 98-03-16 Marchok - Original operational version. +c 98-07-15 Marchok - Added code to calculate radii of gale-, storm-, +c and hurricane-force winds in each quadrant. +c 99-04-01 Marchok - Added code to be able to read in 4-digit years +c off of the TC Vitals records. +c Added code, including subroutine is_it_a_storm, +c to make a better determination of whether or +c not the center that was found at each time is +c the center of a storm, and not just a passing +c vort max, etc. +c 99-06-15 Marchok - Fixed a bug in calcdist that was triggered by a +c rounding error sending a number just above 1 +c into ACOS to get the distance between 2 +c identical points (which, obviously, is 0). +c 00-06-20 Marchok - Added GDAS option for vortex relocation work. +c Changed nhalf from 3 to 5. Relaxed the +c requirements for pthresh and vthresh. +c 00-11-30 Marchok - Added ability to handle GFDL and NCEP Ensemble +c model data. Extended time range to be able to +c handle 5-day capability. Forecast hours are +c now input via a namelist (easiest way to account +c for NAM, GFS and GFDL having different forecast +c lengths at 00/12z and 06/18z). Model ID's are +c now input via a namelist (makes it easier, for +c example, to run for many different ensemble +c members). Added new output, the atcfunix +c format, needed for 5-day forecasts. +c 01-08-24 Marchok Fixed a bug in rvcal and getgridinfo. When a +c grid that was south-->north is flipped in +c conv1d2d_real to be north-->south, the scanning +c mode flag remains 64 and what we would consider +c the max and min latitudes are reversed, so I +c added code to correct this in both routines. +c 02-05-20 Marchok Weakened the mslp gradient threshold and v850 +c threshold in is_it_a_storm to cut down on the +c number of dropped storms. +c 03-03-18 Marchok Fixed a bug in get_ij_bounds that was allowing +c a cos(90) and cos(-90), which then led to a +c divide by zero. +c 05-08-01 Marchok Updated to allow tracking of ECMWF hi-res, ECMWF +c ensemble, CMC hi-res, CMC ensemble, NCEP +c ensemble. +c 06-11-07 Marchok Updated to locate, and report to the atcfunix +c file, the value of the gridpoint minimum value +c of mslp. Previously, the barnes-averaged +c value had been used. +c 08-01-10 Marchok Changed the storm ID for genesis tracking so +c that the ID includes info +c on storm detection location & time. Added +c algorithms for Hart's cyclone phase space. +c Added new output fields to the atcfunix +c records, actually creating a modified atcfunix +c record, to include things such as the mean & +c max values of zeta850 & zeta700 centered on +c the storm, the speed & direction of storm +c translation, and the Hart CPS parameters. +c 10-01-07 Marchok - input grib lead time can be hrs or minutes +c - added code for warm core check +c - added code to detect genesis +c - added code to report on sfc wind structure +c - added buffer ("grid_buffer") to avoid fixing +c center to boundaries on regional grids +c - modified rvcal to report missing zeta values +c as background coriolis instead of -999, since +c the -999 was messing up center-fixing +c - added 10-m wind and sfc zeta as center-fixing +c parms. +c +c 10-05-25 Slocum Add verbose feature to code +c 0 = Not terminal output, 1 = error messages only +c 2 = all output +c +c 10-05-26 Marchok - added flags and code to check the temporal +c consistency of the mslp closed contour and +c Vt850 checks for tcgen and midlat cases. +c +c 13-04-01 Marchok Added code to upgrade the wind radii diagnosid. +c Hurricane Sandy exposed an issue with the +c tracker for large storms. The code was modified +c to use an iterative technique that can +c diagnose radii for large storms but still +c accurately diagnost radii for small storms. See +c subroutine getradii for more details. +c +c 15-11-01 Marchok Replaced the routine which tracks the wind +c minimum at the center of a storm, as that +c routine proved troublesome with very hi-res +c grids (0.02-deg) from HWRF for very small +c storms. This has been replaced with a routine +c that looks for "wind circulation difference", +c whereby the center for this parm is located at +c the spot where the tangential wind circulation +c minus the wind magnitude at the candidate +c center position is maximized. ALSO: Added in +c tracking of thickness as an additional +c tracked parm. ALSO: Added a separate verbose +c flag for only the GRIB2 read diagnostics, which +c can be voluminous. +c +c 16-09-01 Marchok Added in the ability to read in NetCDF files. +c As with GRIB data, the NetCDF data must be on +c a lat/lon grid. +c +c 17-08-31 Marchok Added a logical bitmap capability for NetCDF +c files to prevent the accessing of missing data. +c Also modified the code to permit more accurate +c reporting of the grid point value of the +c minimum SLP for reporting to the atcfunix file. +c Finally, fixed a bug (reported by JTWC) whereby +c radii were being reported for thresholds that +c were in exceedance of the tracker-diagnosed +c Vmax (e.g., 34-kt radii for a storm with +c Vmax = 25 kts). +c +c Input files: +c unit 11 Unblocked GRIB1 file containing model data +c unit 12 Text file containing TC Vitals card for current time +c unit 31 Unblocked GRIB index file +c +c Output files: +c unit 61 Output file with forecast positions every 12h from +c vt=00h to the end of the forecast +c unit 62 Output file in ATCF format, with forecast positions +c at vt = 12, 24, 36, 48 and 72h, plus wind speeds. +c unit 63 Output file with forecast wind radii for 34, 50 and +c 64 knot thresholds in each quadrant of each storm. +c +c Subprograms called: +c read_nlists Read input namelists for input date & storm number info +c read_tcv_card Read TC vitals file to get initial storm position +c getgridinfo Read GRIB file to get basic grid information +c tracker Begin main part of tracking algorithm +c +c Attributes: +c Language: Standard Fortran_90 +c +c$$$ +c +c------- +c +c LOCAL: +c +c ifhours: Integer array holding numerical forecast times for +c the input model (99 = no more times available). +c These values are read in via a namelist. +c Model numbers used: (1) GFS, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) NAM, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble (13) SREF +c Ensemble, (14) NCEP Ensemble (from ensstat mean +c fields), (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) Ensemble RELOCATION (21) UKMET hi-res (NHC) +c (23) FNMOC Ensemble +c stormswitch: This switch tells how to handle each storm in +c the TCV file: +c 1 = process this storm for this forecast hour. +c 2 = Storm was requested to be tracked, but either +c the storm went off the grid (regional models), +c the storm dissipated, or the program was +c unable to track it. +c 3 = Storm was NOT requested to be tracked at all. +c storm: An array of type tcvcard. Each member of storm +c contains a separate TC Vitals card. +c maxstorm: Maximum number of storms the system is set up to +c handle at any 1 time. +c slonfg,slatfg: Holds first guess positions for storms. The +c very first, first guess position is read from the +c TC vitals card. (maxstorm,maxtime) +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms) +c + USE def_vitals; USE inparms; USE set_max_parms; USE level_parms + USE trig_vals; USE atcf; USE trkrparms; USE verbose_output + USE netcdf_parms +c + implicit none +c + logical(1) file_open + integer date_time(8) + character (len=10) big_ben(3) + character :: ncfile*180,ncfile_has_hour0*1 + integer itret,iggret,iicret,igcret,iret,ifhmax,maxstorm,numtcv + integer iocret,enable_timing,ncfile_id,ncfile_tmax,irnhret + integer, parameter :: lugb=11,lugi=31,lucard=12,lgvcard=14,lout=51 +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + +c -------------------------------------------------------- + + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: beginning ... ',i2.2,':',i2.2,':',i2.2) + + call w3tagb('GETTRK ',1999,0104,0058,'NP22 ') + + pi = 4. * atan(1.) ! Both pi and dtr were declared in module + dtr = pi/180.0 ! trig_vals, but were not yet defined. + ncfile_has_hour0 = 'n' ! Default value; set in read_netcdf_hours +c + call read_nlists (inp,trkrinfo,netcdfinfo) + enable_timing=trkrinfo%enable_timing + + call read_fhours (ifhmax) + + call read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_tcv_card, num vitals = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_tcv_card, rc= ',iret + endif + goto 890 + endif + + call read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_gen_vitals, total number of vitals (both' + & ,' TC and non-TC) now = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_gen_vitals, rc= ' + & ,iret + endif + goto 890 + endif + + if (inp%file_seq == 'onebig') then + if (trkrinfo%inp_data_type == 'netcdf') then + ncfile = netcdfinfo%netcdf_filename + print *,' ' + print *,'before open_ncfile call, ncfile= ',ncfile + call open_ncfile (ncfile,ncfile_id) + print *,'after open_ncfile call, ncfile_id= ',ncfile_id + call read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) + if (irnhret /= 0) then + print *,'(/,a32,a5,i4,/)','!!! ERROR: in read_netcdf_hours,' + & ,' rc= ',irnhret + goto 890 + endif + else + call open_grib_files (inp,lugb,lugi,'dummy','dummy',lout,iret) + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: in open_grib_files, rc= ' + & ,iret + goto 890 + endif + endif + endif + + call tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +890 continue + + igcret=0 + iicret=0 + iocret=0 + + inquire (unit=lugb, opened=file_open) + if (file_open) call baclose(lugb,igcret) + inquire (unit=lugi, opened=file_open) + if (file_open) call baclose(lugi,iicret) + inquire (unit=lout, opened=file_open) + if (file_open) call baclose(lout,iocret) + if ( verb .ge. 3 ) then + print *,'baclose: igcret= ',igcret,' iicret= ',iicret + print *,'baclose: iocret= ',iocret + endif + call w3tage('GETTRK ') +c + stop + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +c ABSTRACT: This subroutine is the core of the program. It contains +c the main loop for looping through all the forecast hours and all +c the storms. Basically, the way it works is that it has an outer +c loop that loops on the forecast hour. At the beginning of this +c loop, the data are read in for all parameters and levels needed +c for tracking. The full regional or global grid is read in. +c If vorticity was not read in (some of the centers do not send us +c vorticity), then vorticity calculations are done on the whole +c grid at both 850 and 700 mb. Then the program goes into the inner +c loop, which loops on storm number (program originally set up to +c handle a max of 15 storms). For each storm, subroutine +c find_maxmin is called for the following parameters: Rel Vort and +c geopotential hgt at 700 & 850 mb, and MSLP. Within find_maxmin, +c a barnes analysis is performed over the guess position of the +c storm to find the max or min value, and then iteratively, the +c grid size is cut in half several times and the barnes analysis +c rerun to refine the positioning of the max or min location. After +c the center positions for these parameters have been obtained, +c subroutine get_uv_center is called to get a center fix for the +c minimum in the wind field, specifically, a minimum in the +c magnitude of the wind speed (vmag). The calculation of the vmag +c minimum is done differently than the calculation for the other +c parameters; for vmag, the grid near the storm center guess +c position is interpolated down to a very fine grid, and then +c find_maxmin is called and a barnes analysis is done on that +c smaller grid. For vmag, there are no further calls made to barnes +c with a smaller grid, since the grid has already been interpolated +c down to a smaller grid. Once all of the parameter center fixes +c have been made, subroutine fixcenter is called to average these +c positions together to get a best guess fix position. Then a check +c is done with a call to subroutine is_it_a_storm to make sure that +c the center that we have found does indeed resemble a tropical +c cyclone. Finally, subroutine get_next_ges is called to make a +c guess position for the next forecast time for this storm. +c +c INPUT: +c inp contains input date and model number information +c maxstorm maximum # of storms to be handled +c numtcv number of storms read off of the tcvitals file +c ifhmax max number of analysis & forecast times to be handled +c trkrinfo derived type that holds/describes various tracker parms +c ncfile if the input data type is netcdf, then this ncfile +c variable contains the name of the netcdf file +c ncfile_id if the input data type is netcdf, then this ncfile_id +c variable contains an integer id assigned to the netcdf +c file from the open_ncfile subroutine +c ncfile_has_hour0 character flag (y|n) that, if the tracker is +c running on NetCDF data, tells if the NetCDF file +c actually contains hour0 data or not (some, like the +c 2016 version of FV3, do not). +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file itself in +c subroutine read_netcdf_fhours. +c +c OUTPUT: +c itret return code from this subroutine +c +c LOCAL PARAMETERS: +c storm contains the tcvitals for the storms +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c maxtime Max number of forecast times program can track +c maxtp Max number of tracked parameters program will track. +c Currently (7/2015), this maxtp is 11, and these 11 are +c listed just a few lines below. +c readflag L Indicates status of read for each of 16 parms: +c 1: 850 mb absolute vorticity +c 2: 700 mb absolute vorticity +c 3: 850 mb u-comp +c 4: 850 mb v-comp +c 5: 700 mb u-comp +c 6: 700 mb v-comp +c 7: 850 mb gp hgt +c 8: 700 mb gp hgt +c 9: MSLP +c 10: near-surface u-comp +c 11: near-surface v-comp +c 12: 500 mb u-comp +c 13: 500 mb v-comp +c 14: Mean temperature, centered at 400 mb +c 15: 500 mb gp hgt +c 16: 200 mb gp hgt +c 17: Land-Sea Mask (for use in tcgen applications, and +c even there, it's optional) +c +c calcparm L indicates which parms to track and which not to. +c Array positions are defined exactly as for clon +c and clat, listed next, except that, in general, when +c flag 3 is set to a value, flag 4 is set to the same +c value as 3, and when flag 5 is set to a value, flag +c 6 is set to the same value as 5. This is because +c 3 & 4 are for the 850 mb winds, and if either u or +c v is missing, we obviously can't calculate the +c magnitude of the wind. The same applies for 5 & 6, +c which are for the 700 mb winds. And also for reference, +c here is a list of all the variables & levels for the +c tracked parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms). +c For the third position (max_#_parms), here they are: +c 1: Relative vorticity at 850 mb +c 2: Relative vorticity at 700 mb +c 3: Wind circulation difference at 850 mb +c 4: NOT CURRENTLY USED +c 5: Wind circulation difference at 700 mb +c 6: NOT CURRENTLY USED +c 7: Geopotential height at 850 mb +c 8: Geopotential height at 700 mb +c 9: Mean Sea Level Pressure +c 10: Wind circulation difference at 10 m +c 11: Relative vorticity at 10 m +c 12: Lower-level thickness (500-850) +c 13: Upper-level thickness (200-500) +c 14: Deep-Layer thickness (200-850) +c +c xmaxwind Contains maximum near-surface wind near the storm +c center for each storm at each forecast hour. +c stderr Standard deviation of the position "errors" of the +c different parameters for each storm at each time. +c fixlat,fixlon: Contain the final coordinates for each storm at +c each forecast hour. These coordinates are a +c weighted average of all the individual parameter +c positions (hgt, zeta, mslp, vmag). +c cvort_maxmin: Contains the characters 'max' or 'min', and is +c used when calling the find_maxmin routine for the +c relative vorticity (Look for max in NH, min in SH). +c vradius Contains the distance from the storm fix position to +c each of the various near-surface wind threshhold +c distances in each quadrant. +c (3,4) ==> (# of threshholds, # of quadrants) +c See subroutine getradii for further details. +c wfract_cov Fractional coverage (areal coverage) of winds +c exceeding a certain threshold (34, 50, 64 kts) in +c each quadrant. +c (5,5,3) ==> (# of quadrants + 1, # of distance bins, +c # of thresholds). +c The "extra" array size for quadrants (5, instead of 4) +c is there to hold the total (i.e., "whole disc") +c statistics. +c See subroutine get_fract_wind_cov for further details +c +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c isastorm Character array used in the call to is_it_a_storm, +c tells whether the minimum requirement for an MSLP +c gradient was met (isastorm(1)), whether for the midlat +c and tcgen cases if a closed mslp contour was found +c (isastorm(2)), and if a circulation exists at 850 mb +c (isastorm(3)). Can have a value of 'Y' (requirement +c met), 'N' (requirement not met) or 'U' (requirement +c undetermined, due to the fact that no center location +c was found for this parameter). +c maxmini These 2 arrays contain the i and j indeces for the +c maxminj max/min centers that are found using the rough check +c in first_ges_ctr and subsequent routines. Only needed +c for a midlatitude or a genesis run, NOT needed for a +c TC tracker run. +c stormct Integer: keeps and increments a running tab of the +c number of storms that have been tracked at any time +c across all forecast hours. Used only for midlat or +c tcgen runs. +c gridprs This contains the actual value of the minimum pressure +c at a gridpoint. The barnes analysis will return an +c area-averaged value of pressure; this variable will +c contain the actual minimum value at a gridpoint near +c the lat/lon found by the barnes analysis. +c closed_mslp_ctr_flag This flag keeps track of the value of the +c closed contour flag returned from subroutine +c check_closed_contour. +c vt850_flag This flag keeps track of the value of the flag for +c the 850 mb Vt check. +c----- +c + USE def_vitals; USE inparms; USE tracked_parms; USE error_parms + USE set_max_parms; USE level_parms; USE grid_bounds; USE trkrparms + USE contours; USE atcf; USE radii; USE trig_vals; USE phase + USE gen_vitals; USE structure; USE verbose_output + USE waitfor_parms; USE module_waitfor; USE netcdf_parms + USE tracking_parm_prefs +c + implicit none +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (cint_stuff) contour_info +c + character, allocatable :: closed_mslp_ctr_flag(:,:)*1 + character, allocatable :: vt850_flag(:,:)*1 + character :: r34_check_okay*1,had_to_try_backup_850_vt_check*1 + character :: need_to_expand_r34(4)*1,ncfile_has_hour0*1 + character*(*), intent(in) :: ncfile + integer :: ncfile_id + integer, parameter :: nreadparms=17 + real, allocatable :: prstemp(:),iwork(:) + integer, parameter :: numdist=14,numquad=4,lout=51 + integer, allocatable :: prsindex(:) + integer imax,jmax,ifh,ist,irf,jj,istmp,ifhtemp,itret,ivpa + integer isiret1,isiret2,isiret3,idum,m,iix,jjx,imode,numtcv + integer iha,isa,iua,iva,iza,maxstorm,ivort,ifix,jfix,issret + integer imoa,imoca,iksa,isda,ileadtime,leadtime_check + integer ioaret,ioaxret,ifgcret,ifmret,igugret,isoiret,icccret + integer igrret,igmwret,iorret,ignret,iovret,icbret,igucret,ita + integer ifilret,ifret,iaret,isret,iotmret,iwa,iisa,sl_counter + integer iicret,igcret,pfcret,igwcret,imbowret,iatret + logical(1), allocatable :: valid_pt(:,:) + logical(1), allocatable :: masked_outc(:,:),masked_out(:,:) + logical(1) readflag(nreadparms),calcparm(maxtp,maxstorm) + logical(1) tracking_previously_known_storms + logical(1) need_to_flip_lats,need_to_flip_lons + logical(1) file_open,first_time_thru_getradii + character cvort_maxmin*3,isastorm(3)*1,ccflag*1,gotten_avg_value*1 + character cmaxmin*3,get_last_isobar_flag*1,wcore_flag*1 + character gfilename*120,ifilename*120,gridmove_status*7 + integer vradius(3,4),igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) + integer maxmini(maxstorm),maxminj(maxstorm),pdf_ct_bin(16) + integer ifcsthour,stormct,prevstormct,kf,istmspd,istmdir,iggret + integer igiret,iuret,jdum,icount,ilonfix,jlatfix,igpret,ifhmax + integer ibeg,jbeg,iend,jend,ix1,ix2,n,ilev,npts,icpsa,igzvret + integer igfwret,ioiret,igisret,iofwret,iowsret,igwsret,igscret + integer pdf_ct_tot,lugb,lugi,iret,icmcf,iccfh,ivt8f + integer waitfor_gfile_status,waitfor_ifile_status,ncfile_tmax + integer wait_max_ifile_wait,ivr,r34_good_ct,itha,ilma,inctcv + integer date_time(8) + character (len=10) big_ben(3) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridprs(maxstorm,maxtime) + real wfract_cov(5,5,3) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real ike(max_ike_cats) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmaxwind(maxstorm,maxtime),xmeanzeta + real stderr(maxstorm,maxtime),xval(maxtp),cps_vals(3) + real gridpoint_maxmin,dist,distnm,xknots,xmaxspeed + real uvgeslon,uvgeslat,xavg,stdv,search_cutoff,re,ri,dx,dy + real xinp_fixlat,xinp_fixlon,degrees,plastbar,rlastbar + real xinterval_fhr,cc_time_sum_tot,cc_time_sum_yes + real rmax,sdp,wdp,paramb,vtl_slope,vtu_slope + real xsfclon,xsfclat,cc_time_pct,radmax,r34_dist_thresh + real prev_latmax,prev_latmin,prev_lonmax,prev_lonmin + real vradius_km,hold_old_contint,tcv_max_wind_ms + real tcv_mslp_pa,r34_from_tcv,roci_from_tcv + real proci_from_tcv,prs_contint_thresh + integer enable_timing,igrct + character(pfc_cmd_len) :: pfc_final +c + prev_latmax = -999.0 + prev_latmin = -999.0 + prev_lonmax = -999.0 + prev_lonmin = -999.0 + enable_timing=trkrinfo%enable_timing + icmcf = 0 + ivt8f = 0 + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + allocate (closed_mslp_ctr_flag(maxstorm,ifhmax),stat=icmcf) + allocate (vt850_flag(maxstorm,ifhmax),stat=ivt8f) + ! Initialize flags to 'u', not 'n'. That way, + ! when we are evaluating its value back over recent past hours, + ! we can distinguish a "no" value from an initialized value of + ! 'u' for which a storm hadn't yet been detected. + closed_mslp_ctr_flag = 'u' + vt850_flag = 'u' + endif + + allocate (prsindex(maxstorm),stat=iisa) + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iisa /= 0 .or. iva /= 0 .or. iwa /= 0 .or. icmcf /= 0 .or. + & ivt8f /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating prsindex,' + print *,'!!! prstemp or iwork array for storms: iisa = ',iisa + print *,'!!! iva= ',iva,' iwa= ',iwa,' icmcf= ',icmcf + print *,'!!! ivt8f= ',ivt8f + endif + itret = 94 + return + endif + + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + clon = 0.0 + clat = 0.0 + stderr = stermn ! initialize stderr to 0.1 (error_parms) + itret = 0 + xmaxwind = 0.0 + stormct = 0 + + ! It is critical to initialize the gridprs array to something + ! greater than normal atmospheric pressures (I've chosen 9999.99 + ! mb). This is so that in the sort on pressure before stormloop, + ! the top of the sorting index array will be filled with pressure + ! values from active storms, while those inactive 9999 storms + ! will fill the bottom of the sorting index array (prsindex). + + gridprs = 999999.0 + fixlon = -999.0 + fixlat = -999.0 + + if (inp%file_seq == 'multi') then + ! Each tau will have a separate file, starting with unit + ! number 200 (GRIB data) and 5200 (GRIB index file) and + ! incrementing upwards from there for each tau. + if (trkrinfo%gribver == 1) then + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + else + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + endif + else + ! All lead times are included in one big file. These values + ! for lugb and lugi will remain static for all taus. + lugb = 11 + lugi = 31 + endif + + ifh = 1 + + if ( verb .ge. 3 ) then + print *,'top of tracker, ifh= ',ifh,' ifhmax= ',ifhmax + endif + + ifhloop: do while (ifh <= ifhmax) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------*' + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* New forecast hour: ',i4,':',i2.2) + print *,'*-------------------------------------------*' + endif + + if (inp%file_seq == 'multi') then + + lugb = lugb + 1 + lugi = lugi + 1 + + call get_grib_file_name (ifh,gfilename,ifilename) + + if (use_waitfor == 'y') then + + ! First check for existence of grib file.... + + call waitfor(trim(gfilename),waitfor_gfile_status + & ,wait_min_age,wait_min_size,wait_max_wait + & ,wait_sleeptime) + if (waitfor_gfile_status /= 0) then + print *,' ' + write(6,405) + write(6,406) wait_max_wait,trim(gfilename) + 405 format('ERROR: TIMEOUT from waitfor for GRIB file.') + 406 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + ! Now check for existence of index file. Use a separate + ! max_wait time -- a much shorter one -- since once the + ! grib file is there, the index file should appear within + ! a matter of seconds. Also, the index file is much + ! smaller, so set the wait_min_size accordingly. + + wait_max_ifile_wait = 180 + wait_min_size = 500 + call waitfor(trim(ifilename),waitfor_ifile_status + & ,wait_min_age,wait_min_size,wait_max_ifile_wait + & ,wait_sleeptime) + if (waitfor_ifile_status /= 0) then + print *,' ' + write(6,415) + write(6,416) wait_max_ifile_wait,trim(ifilename) + 415 format('ERROR: TIMEOUT from waitfor for INDEX file.') + 416 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + endif + + call open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: from open_grib_files, rc= ' + & ,iret + print *,'!!! Files after hour0 are missing, ' + & ,'exiting normally' + stop 0 + endif + endif + + if (trkrinfo%inp_data_type == 'grib') then + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is CLOSED' + endif + endif + + !-------------------------------------------------------------- + ! Within this next IF statement, we deal with writing out atcf + ! records for storms for the case in which we have netcdf data, + ! but that netcdf data does not have hour0 data (as of Nov 2016, + ! this is the case for FV3 data). In this case, we write out + ! missing values for the hour0 time, and then we update the + ! guess for next lead time by extrapolating data from TC Vitals. + ! Note in the IF statement itself, "iftotalmins" is the array + ! of *user-requested* lead times, meaning that the user has + ! requested to look at hour0, but the ncfile_has_hour0 flag + ! indicates the hour0 time is not in the NetCDF data. + !-------------------------------------------------------------- + + if (ifh == 1 .and. iftotalmins(ifh) == 0 .and. + & trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') then + + null_netcdf_hour0_storm_loop: do inctcv = 1,numtcv + + call output_atcfunix (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,inctcv + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',inctcv + write (6,431) storm(inctcv)%tcv_storm_id + & ,storm(inctcv)%tcv_storm_name + 431 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + + call advect_tcvitals_from_hour0 (slonfg,slatfg,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) + + if (iatret /= 0) then + fixlon (inctcv,ifh) = -999.0 + fixlat (inctcv,ifh) = -999.0 + stormswitch(inctcv) = 2 + cycle null_netcdf_hour0_storm_loop + endif + + stormswitch(inctcv) = 1 + + enddo null_netcdf_hour0_storm_loop + + ifh = ifh + 1 + cycle ifhloop + + endif + + !-------------------------------------------------------------- + ! Make call to getgridinfo in order to get info on the imax, + ! jmax, as well as the x- and y-increments, and also to see if + ! the grid is correctly oriented for the tracker so that the + ! data go north to south and west to east or if we need to flip + ! either the lats or the lons. + !-------------------------------------------------------------- + + if (trkrinfo%inp_data_type == 'grib') then + call getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) + else + print *,' ' + print *,'!!! ERROR: trkrinfo%inp_data_type NOT VALID ' + print *,'!!! trkrinfo%inp_data_type= ',trkrinfo%inp_data_type + print *,'!!! Should have value of grib or netcdf.' + print *,'!!! EXITING....' + print *,' ' + stop 93 + endif + + if (iggret == 0) then + if ( verb .ge. 1 ) then + print *,'TEST after getgridinfo in sub tracker, ' + & ,'iggret= ',iggret + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in getgridinfo, rc= ' + & ,iggret + endif + stop 95 + endif + + if (inp%modtyp == 'regional' .and. inp%nesttyp == 'moveable') + & then + if (glatmax == prev_latmax .and. glatmin == prev_latmin .and. + & glonmax == prev_lonmax .and. glonmin == prev_lonmin) then + ! The moveable, nested regional grid has not moved since + ! the last lead time. This could be an indication that the + ! model lost the storm and so the grid has not moved to + ! stay with the cyclone center. Set a flag to indicate this. + gridmove_status = 'stopped' + else + gridmove_status = 'moving' + endif + else + gridmove_status = 'notappl' + endif + + prev_latmax = glatmax + prev_latmin = glatmin + prev_lonmax = glonmax + prev_lonmin = glonmin + + gotten_avg_value = 'n' + +c First, allocate the working data arrays.... + + if (allocated(valid_pt)) deallocate (valid_pt) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + + ! Allocate all of the allocatable arrays.... + + allocate (valid_pt(imax,jmax),stat=ivpa) + allocate (zeta(imax,jmax,nlevzeta),stat=iza) + allocate (u(imax,jmax,nlevs),stat=iua) + allocate (v(imax,jmax,nlevs),stat=iva) + allocate (hgt(imax,jmax,nlevhgt),stat=iha) + allocate (slp(imax,jmax),stat=isa) + allocate (tmean(imax,jmax),stat=ita) + allocate (thick(imax,jmax,nlevthick),stat=itha) + allocate (lsmask(imax,jmax),stat=ilma) + allocate (masked_out(imax,jmax),stat=imoa) + allocate (masked_outc(imax,jmax),stat=imoca) + + ita=0 + icpsa=0 + if (phaseflag == 'y') then + if (phasescheme == 'cps' .or. phasescheme == 'both') then + if (allocated(cpshgt)) deallocate (cpshgt) + allocate (cpshgt(imax,jmax,nlevs_cps),stat=icpsa) + endif + endif + + if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. + & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. + & itha /= 0 .or. imoa /= 0 .or. imoca /= 0 .or. ilma /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating arrays.' + print *,'!!! iza = ',iza,' iua= ',iua,' iha= ',iha + print *,'!!! iva = ',iva,' isa= ',isa,' icpsa= ',icpsa + print *,'!!! iksa = ',iksa,' isda= ',isda,' ivpa= ',ivpa + print *,'!!! ita = ',ita,' imoa= ',imoa,' imoca= ',imoca + print *,'!!! itha = ',itha,' ilma= ',ilma + endif + itret = 94 + return + endif + + masked_out = .false. ! Initialize all pts to false at each hr + masked_outc = .false. ! Initialize all pts to false at each hr + + if ( verb .ge. 3 ) then + print *,'in beginning of tracker, imax= ',imax,' jmax= ',jmax + endif + +c Initialize all readflags to NOT FOUND for this forecast time, +c then call subroutine to read data for this forecast time. + + zeta = -9999.0 + u = -9999.0 + hgt = -9999.0 + v = -9999.0 + slp = -9999.0 + tmean = -9999.0 + + readflag = .FALSE. + + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: b4 getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + + if (trkrinfo%inp_data_type == 'grib') then + call getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,32) date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: after getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + +c Count how many parms were successfully read for this fcst time. +c Also, for right now, put the value of readflag into all of the +c calcparms for parameters 3 through 9. Note that in getdata we +c read in 17 parms, but in this next loop we only check the +c readflags up to maxtp (= 14 as of 7/2015). That's because +c parms 12 & 13 are for 500 mb u & v, which are not used for +c tracking, only for calculating the deep layer mean wind for +c the next guess, and parm 14 is the 300-500 mb mean temperature, +c which is used for determining storm phase. Parms 10 & 11 are +c for the near-surface winds, which are used in estimating surface +c winds near the storm, and will now also be used as a +c parameter for position estimates. Finally, parm 17 is the +c land-sea mask, which is not used as a tracking parm. + + idum = 0 + do irf = 1,nreadparms + if (readflag(irf)) idum = idum + 1 + if (irf > 2 .and. irf < 10) then + ! calcparm for parms > 9 is done further below. + do jj=1,maxstorm + calcparm(irf,jj) = readflag(irf) + enddo + endif + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Of ',nreadparms,' readable parms, you read in ',idum + print *,'parms for this fcst hour from the input grib file.' + endif + +c If not enough tracked parms were read in, exit the program.... + + if (idum == 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in subroutine tracker' + print *,'!!! Not enough tracked parms read in from getdata.' + print *,'!!! Check for a problem with the input GRIB file.' + print *,'!!! Model identifier = ',inp%model + print *,'!!! STOPPING EXECUTION FOR THIS MODEL' + endif + itret = 99 + ifhtemp = ifh + do while (ifhtemp <= ifhmax) + do istmp=1,maxstorm + fixlon (istmp,ifhtemp) = -999.0 + fixlat (istmp,ifhtemp) = -999.0 + enddo + ifhtemp = ifhtemp + 1 + enddo +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) + if (ifh == 1) then + ! Per Jim Gross (1/01), if the tracker ran but was unable + ! to get an initial fix (or, in this case, unable to get + ! the data needed to run), write out zeroes for the 00h + ! fixes to indicate that the tracker ran unsuccessfully, + ! but don't write out any subsequent forecast times + ! with zeroes.... + vradius = 0 + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initial value of 'undetermined' + do istmp = 1,maxstorm + if (stormswitch(istmp) /= 3) then + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0,-999.0,inp,istmp + & ,ifcsthour,0.0,0.0,vradius,maxstorm + & ,trkrinfo,-99.0,-99.0,-99.0,cps_vals + & ,wcore_flag,ioaxret) + call output_hfip (-999.0,-999.0,inp,istmp + & ,ifh,0.0,0.0,vradius,-99.0,ioaxret) + endif + enddo + endif + return + endif + +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for z850, z700 and mslp.... + + if (user_wants_to_track_gph850 == 'n' .or. + & user_wants_to_track_gph850 == 'N') then + do jj=1,maxstorm + calcparm(7,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_gph700 == 'n' .or. + & user_wants_to_track_gph700 == 'N') then + do jj=1,maxstorm + calcparm(8,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_mslp == 'n' .or. + & user_wants_to_track_mslp == 'N') then + do jj=1,maxstorm + calcparm(9,jj) = .FALSE. + enddo + endif + + +c Parameters 1 & 2 are abs vorticity at 850 & 700. If the data +c files had this parm at 850 & 700 (ECMWF & UKMET do NOT), then +c we don't need to re-calculate relative vorticity, we just need +c to subtract out the Coriolis component. If the files did not +c have vorticity, then we need to calculate relative vorticity. +c If we're able to read vorticity or calculate it, then set the +c vorticity calcparms to TRUE for all storms for now. + + vortloop: do ivort=1,2 + + if (ivort == 1) then + if (user_wants_to_track_zeta850 == 'n' .or. + & user_wants_to_track_zeta850 == 'N') then + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (ivort == 2) then + if (user_wants_to_track_zeta700 == 'n' .or. + & user_wants_to_track_zeta700 == 'N') then + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (readflag(ivort)) then + + call subtract_cor (imax,jmax,dy,ivort) + + do jj=1,maxstorm + calcparm(ivort,jj) = .TRUE. + enddo + else + if (ivort == 1) then + if (readflag(3) .and. readflag(4)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(1,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + endif + else + if (readflag(5) .and. readflag(6)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(2,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + endif + endif + endif + + enddo vortloop + + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for user preferences for the wind circulation +c difference at 850 & 700... + + if (readflag(3) .and. readflag(4)) then + if (user_wants_to_track_wcirc850 == 'n' .or. + & user_wants_to_track_wcirc850 == 'N') then + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(3,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + endif + + if (readflag(5) .and. readflag(6)) then + if (user_wants_to_track_wcirc700 == 'n' .or. + & user_wants_to_track_wcirc700 == 'N') then + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(5,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + endif + + +c Compute the sfc vorticity if sfc_u and sfc_v have been read in. + + if (readflag(10) .and. readflag(11)) then + + if (user_wants_to_track_wcircsfc == 'n' .or. + & user_wants_to_track_wcircsfc == 'N') then + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(10,jj) = .TRUE. + enddo + endif + + if (user_wants_to_track_zetasfc == 'n' .or. + & user_wants_to_track_zetasfc == 'N') then + do jj=1,maxstorm + calcparm(11,jj) = .FALSE. + enddo + else + ! The 3 in the next call to rvcal is to indicate the 3rd + ! level for the zeta array, which is for the surface (or + ! 10m) data. + call rvcal (imax,jmax,dx,dy,3,valid_pt) + do jj=1,maxstorm + calcparm(11,jj) = .TRUE. + enddo + endif + + else + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + calcparm(11,jj) = .FALSE. + enddo + endif + + +c Compute the thicknesses for 200-850, 200-500 and 500-850 mb +c if the gp hgt fields have been read in for 200, 500 and 850. + + if (readflag(7) .and. readflag(15) .and. readflag(16)) then + + call thickness_calc (imax,jmax,valid_pt) + + do jj=1,maxstorm + + if (user_wants_to_track_thick500850 == 'n' .or. + & user_wants_to_track_thick500850 == 'N') then + calcparm(12,jj) = .FALSE. + else + calcparm(12,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200500 == 'n' .or. + & user_wants_to_track_thick200500 == 'N') then + calcparm(13,jj) = .FALSE. + else + calcparm(13,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200850 == 'n' .or. + & user_wants_to_track_thick200850 == 'N') then + calcparm(14,jj) = .FALSE. + else + calcparm(14,jj) = .TRUE. + endif + + enddo + else + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Thickness will not be tracked since at least' + print *,'one of the gp height fields was not read in.' + print *,' readflag(7) -- 850 mb ---> ',readflag(7) + print *,' readflag(15) -- 500 mb ---> ',readflag(15) + print *,' readflag(16) -- 200 mb ---> ',readflag(16) + print *,' ' + endif + do jj=1,maxstorm + calcparm(12,jj) = .FALSE. + calcparm(13,jj) = .FALSE. + calcparm(14,jj) = .FALSE. + enddo + endif + +c --------------------------------------------------------------- +c Now call find_maxmin for the variables zeta, hgt and slp. Only +c process those storms for which stormswitch is set to 1. If a +c storm is selected to be processed, we still have to check the +c calcparm for each parameter, to make sure that the particular +c parm exists at that level and is able to be processed. +c +c The following commented-out data statements are just included +c as a reference so you can see the array positioning of the +c different parameters and levels that are read in: +c +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,sfc,sfc +c ,100,100,100,100,100/ +c data iglev /850,700,850,850,700,700,850,700,0,sfc,sfc +c ,500,500,400,500,200/ +c +c And also for reference, here are the variables / levels for +c the *tracked* parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c NOTE: For mid-latitude cases, we will track ONLY mslp, which +c is why we set all the other calcparms to 'false' just below. + + if (trkrinfo%type == 'midlat') then + do m = 1,maxstorm + calcparm(1,m) = .false. + calcparm(2,m) = .false. + calcparm(3,m) = .false. + calcparm(4,m) = .false. + calcparm(5,m) = .false. + calcparm(6,m) = .false. + calcparm(7,m) = .false. + calcparm(8,m) = .false. + calcparm(10,m) = .false. + calcparm(11,m) = .false. + calcparm(12,m) = .false. + calcparm(13,m) = .false. + calcparm(14,m) = .false. + enddo + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + call sort_storms_by_pressure (gridprs,ifh,maxstorm,prsindex + & ,issret) + if ( (ifh == 1) .or. + & (ifh == 2 .and. trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') ) then + stormct = numtcv + endif + endif + + prevstormct = stormct + tracking_previously_known_storms = .true. + + stormloop: do sl_counter = 1,maxstorm + + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initialized value of 'undetermined' + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + ist = prsindex(sl_counter) + else + ist = sl_counter + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + + if (ist == (prevstormct + 1)) then + + ! For the mid-latitude and tropical cyclogenesis cases, we + ! need to scan the mslp field to find new storms. If we + ! are at this point inside the if statement in stormloop, + ! then that means we have looped through and attempted to + ! track all storms that have already been found up to this + ! point in the forecast, and we need to scan the field for + ! any new storms at this forecast hour. If this is for + ! forecast hour = 0, then right off the bat we may be + ! scanning the field (if there were no tcvitals records + ! read in for this forecast), since ist = 1 and + ! (prevstormct + 1) = 0 + 1 = 1. All that the call just + ! below to first_ges_center does is return a rough idea + ! of the location of new lows; more specific locations are + ! obtained through the barnes analysis tracking algorithm + ! further below. + + if (readflag(9)) then + if (ifh > 1) then + ! We need the use of 2 different masks. One + ! (masked_out) is to be used when looking for new lows, + ! so that after we find a new low, we mask out the + ! surrounding area so we don't find it on a subsequent + ! search for this forecast hour. The other + ! (masked_outc) is used in the routine to check for a + ! closed contour. If checking for a closed contour + ! at, say 70W/25N, this and surrounding points may have + ! already been masked out in first_ges_center, so "N" + ! would misleadingly/incorrectly be returned from + ! check_closed_contour, so that is why we need 2 masks. + ! But now after the first forecast hour (t=0), the way + ! we have this set up is that we track previously known + ! storms first, and once we're done with them, we + ! search for new storms at that same forecast hour. + ! But when looking for new storms, we need to know the + ! positions of the previously tracked storms at this + ! current forecast hour, so we copy the masked_outc + ! array to masked_out in this case.... + + masked_out = masked_outc + + endif + call first_ges_center (imax,jmax,dx,dy,'mslp',slp + & ,'min',trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) + tracking_previously_known_storms = .false. + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In subroutine tracker, readflag' + print *,'!!! for mslp indicates that the mslp data' + print *,'!!! is not available for this forecast ' + print *,'!!! hour, and it is needed for a "midlat"' + print *,'!!! or "tcgen" run of the tracker. ' + print *,'!!! We will exit....' + print *,'!!! readflag(9) = ',readflag(9) + print *,'!!! ifh= ',ifh + print *,' ' + endif + itret = 98 + return + endif + endif + endif + + xval = 0.0 ! initialize entire xval array to 0 + isastorm = 'U' ! re-initialize flag for each time, each storm + + select case (stormswitch(ist)) + + case (1) + + vradius = 0 + + if ( verb .ge. 2 ) then + print *,' ---------------------------------------------' + print *,' | *** TOP OF STORM LOOP *** ' + print *,' | Beginning of storm loop in tracker for' + print *,' | Storm number ',ist + write (6,418) ifhours(ifh),ifclockmins(ifh) + 418 format (1x,' | Forecast hour: ',i4,':',i2.2) + print *,' | Storm name = ',storm(ist)%tcv_storm_name + print *,' | Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,' ---------------------------------------------' + print *,' ' + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3 + & ,'_',i3.3,a1,'_',i4.4,a1,'_',a3) + + endif +c First, make sure storm is within the grid boundaries... + + call check_bounds (slonfg(ist,ifh),slatfg(ist,ifh),ist,ifh + & ,trkrinfo,icbret) + if (icbret == 95) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + + if (slatfg(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + if (calcparm(1,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,1),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(1,ist),clon(ist,ifh,1),clat(ist,ifh,1) + & ,xval(1),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(2,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,2),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(2,ist),clon(ist,ifh,2),clat(ist,ifh,2) + & ,xval(2),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(7,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,1),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(7,ist) + & ,clon(ist,ifh,7),clat(ist,ifh,7),xval(7) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(8,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,2),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(8,ist) + & ,clon(ist,ifh,8),clat(ist,ifh,8),xval(8) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(9,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for mslp' + endif + + call find_maxmin (imax,jmax,dx,dy,'slp' + & ,slp,'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(9,ist) + & ,clon(ist,ifh,9),clat(ist,ifh,9),xval(9) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(11,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for sfc zeta' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,3),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(11,ist),clon(ist,ifh,11),clat(ist,ifh,11) + & ,xval(11),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 + + if (calcparm(12,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 500-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,1),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(12,ist) + & ,clon(ist,ifh,12),clat(ist,ifh,12),xval(12) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(13,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-500 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,2),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(13,ist) + & ,clon(ist,ifh,13),clat(ist,ifh,13),xval(13) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(14,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,3),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(14,ist) + & ,clon(ist,ifh,14),clat(ist,ifh,14),xval(14) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c Now get centers for wind circulation at 700 & 850 mb and +c at 10m. First, get a modified guess lat/lon position for +c wind circulation. Do this because we will be searching +c for this wind circulation center over a smaller area and +c so it's more crucial to have a better first guess position. +c This modified guess position will be an average of the first +c guess position for this time and the fix positions for this +c time from some of the other parameters. + + if (slatfg(ist,ifh) >= 0.0) then + cmaxmin = 'max' + else + cmaxmin = 'min' + endif + + if (calcparm(3,ist) .and. calcparm(4,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 850 mb ' + endif + + print *,' ' + print *,'Before first call to get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' inp%modtyp= ',inp%modtyp + print *,' cmaxmin= ',cmaxmin + print *,' nlev850= ',nlev850 + print *,' u(1,1,nlev850)= ',u(1,1,nlev850) + print *,' u(imax,jmax,nlev850)= ',u(imax,jmax,nlev850) + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' calcparm(3,ist)= ',calcparm(3,ist) + print *,' clon(ist,ifh,3)= ',clon(ist,ifh,3) + print *,' clat(ist,ifh,3)= ',clat(ist,ifh,3) + print *,' xval(3)= ',xval(3) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,141) date_time(5),date_time(6),date_time(7) + 141 format (1x,'TIMING: Before GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,850,valid_pt,calcparm(3,ist) + & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,142) date_time(5),date_time(6),date_time(7) + 142 format (1x,'TIMING: After GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,850,valid_pt,calcparm(3,ist) +c & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + endif + else + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + clon(ist,ifh,3) = 0.0 + clat(ist,ifh,3) = 0.0 + endif + endif + + if (calcparm(5,ist).and. calcparm(6,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 700 mb ' + endif + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,143) date_time(5),date_time(6),date_time(7) + 143 format (1x,'TIMING: Before GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,700,valid_pt,calcparm(5,ist) + & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,144) date_time(5),date_time(6),date_time(7) + 144 format (1x,'TIMING: After GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,700,valid_pt,calcparm(5,ist) +c & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + endif + else + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + clon(ist,ifh,5) = 0.0 + clat(ist,ifh,5) = 0.0 + endif + endif + + if (calcparm(10,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for the' + print *,'surface (10m) level' + endif + + ! NOTE: The 1020 in the call here is just a number/code + ! to indicate to the subroutine to process sfc winds. + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,145) date_time(5),date_time(6),date_time(7) + 145 format (1x,'TIMING: Before GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,1020,valid_pt,calcparm(10,ist) + & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) + & ,trkrinfo,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,146) date_time(5),date_time(6),date_time(7) + 146 format (1x,'TIMING: After GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,1020,valid_pt,calcparm(10,ist) +c & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) +c & ,trkrinfo,igucret) + + if (igwcret /= 0) then + calcparm(10,ist) = .FALSE. + endif + else + calcparm(10,ist) = .FALSE. + clon(ist,ifh,10) = 0.0 + clat(ist,ifh,10) = 0.0 + endif + endif + +c ------------------------------------------------------ +c All of the parameter center fixes have been done. Now +c average those positions together to get the best guess +c fix position. If a center fix is able to be made, then +c call subroutine get_max_wind to get the maximum near- +c surface wind near the center, and then call get_next_ges +c to get a guess position for the next forecast hour. + + if (stormswitch(ist) == 1) then + + call fixcenter (clon,clat,ist,ifh,calcparm + & ,slonfg(ist,ifh),slatfg(ist,ifh),inp + & ,stderr,fixlon,fixlat,xval,maxstorm,ifret) + + if (ifret == 0) then + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'regional')then + if (fixlon(ist,ifh) > (trkrinfo%eastbd + 7.0) .or. + & fixlon(ist,ifh) < (trkrinfo%westbd - 7.0) .or. + & fixlat(ist,ifh) > (trkrinfo%northbd + 7.0) .or. + & fixlat(ist,ifh) < (trkrinfo%southbd - 7.0)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! will NOT be made for this time due' + print *,'!!! the storm being more than 7 degrees' + print *,'!!! outside the user-specified lat/lon' + print *,'!!! bounds for this run. We will stop' + print *,'!!! tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + 432 format (1x,'!!! Fcst hr = ',i4,':',i2.2) + print *,'!!! fixlat= ',fixlat(ist,ifh) + print *,'!!! fixlon= ',fixlon(ist,ifh) + print *,'!!! User East Bound = ',trkrinfo%eastbd + print *,'!!! User West Bound = ',trkrinfo%westbd + print *,'!!! User North Bound = ',trkrinfo%northbd + print *,'!!! User South Bound = ',trkrinfo%southbd + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + endif + cycle stormloop + endif + endif + else + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + +c Just because we've found a center doesn't mean there is +c actually a storm there. I noticed in the first year that +c for some decaying or just weak storms, the tracker would +c identify a center to follow, but it may have only been +c a weak trough passing by, or something else that's not +c our storm. This next subroutine checks to see that the +c surface pressure gradient and/or tangential winds at +c 850 mb resemble a storm. It is called twice; the first +c time for MSLP, the 2nd time for 850 mb winds. We will +c apply these storm-checking criteria if either the mslp +c or v850 check come back negative. Remember, there +c is the possibility that centers could not be found for +c 1 or both of these parameters, in which case the isastorm +c flag will have a value of 'U', for "undetermined". + + isiret1 = 0; isiret2 = 0; isiret3 = 0 + + print *,' ttest, ifret= ',ifret + + if (ifret == 0) then + + print *,' ttest, calcparm(9,ist)= ',calcparm(9,ist) + + if (calcparm(9,ist)) then + + ! Do a check of the mslp gradient.... + + print *,' ttest, in IF part: ' + print *,' clon(ist,ifh,9)= ',clon(ist,ifh,9) + print *,' clat(ist,ifh,9)= ',clat(ist,ifh,9) + print *,' xval(9)= ',xval(9) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,clon(ist,ifh,9),clat(ist,ifh,9) + & ,xval(9),trkrinfo,isastorm(1),isiret1) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for mslp (e.g., + ! maybe the mslp fix was too far away from the + ! guess?), then this check isn't performed. We are + ! changing this so that the mslp gradient check will + ! still be performed, but using the mean fixlat and + ! fixlon positions as the center. Still, we first + ! need to check to see if mslp was even read in. If + ! it wasn't, then we are just out of luck. + + print *,' ttest, in ELSE part: ' + + if (trkrinfo%use_backup_mslp_grad_check == 'y' .or. + & trkrinfo%use_backup_mslp_grad_check == 'Y') then + + print *,' ttest ELSE, readflag(9)= ',readflag(9) + + if (readflag(9)) then + + print *,'ttest ELSE A, ist= ',ist,' ifh= ',ifh + print *,'ttest ELSE A, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE A, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,9999.0,ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + + print *,'ttest ELSE B, ifilret= ',ifilret + + if (ifilret == 0) then + + print *,'ttest ELSE B, ifilret= ',ifilret + print *,'ttest ELSE B, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE B, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,gridpoint_maxmin,trkrinfo,isastorm(1) + & ,isiret1) + + if (isiret1 == 0) then + ! Even though calcparm(9) is FALSE and mslp + ! will not be used for center-fixing + ! purposes, we need to fill the clat and clon + ! arrays just a few lines below so that + ! calls to fix_latlon_to_ij below do not + ! get screwed up. So, into the clat and clon + ! arrays we put the mean fixlat and fixlon + ! positions for this lead time. + clat(ist,ifh,9) = fixlat(ist,ifh) + clon(ist,ifh,9) = fixlon(ist,ifh) + xval(9) = gridpoint_maxmin + endif + + endif + + endif + + endif + + endif + + ! If we have found a valid mslp gradient, then make + ! a call to fix_latlon_to_ij to (1) get the actual + ! gridpoint value of the mslp (the value previously + ! stored in xval(9) is an area-averaged value coming + ! from the barnes analysis), and (2) to get the + ! (i,j) indices for this gridpoint to be used in the + ! call to check_closed_contour below. + ! + ! NOTE: If a mslp fix was not made, or if the mslp + ! "isastorm" flag comes back as no, we make the same + ! call to fix_latlon_to_ij, but we use the mean fix + ! position as our input to search around, and then + ! basically we just find the lowest mslp near that + ! mean fix position. There is a check on the value + ! of xinp_fixlat and xinp_fixlon to make sure that + ! they contain valid values and not just the + ! initialized -999 values. + + if (isiret1 == 0 .and. isastorm(1) == 'Y') then + xinp_fixlat = clat(ist,ifh,9) + xinp_fixlon = clon(ist,ifh,9) + if (verb >= 3) then + print *,' ttest at location C IF....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + else + xinp_fixlat = fixlat(ist,ifh) + xinp_fixlon = fixlon(ist,ifh) + if (verb >= 3) then + print *,' ttest at location C ELSE....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + endif + + if (xinp_fixlat > -99.0 .and. xinp_fixlon > -990.0) + & then + if (verb >= 3) then + print *,' ttest at location D' + endif + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,xinp_fixlon,xinp_fixlat + & ,xval(9),ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (verb >= 3) then + print *,' ttest at location E, ifilret= ',ifilret + endif + if (ifilret == 0) then + gridprs(ist,ifh) = gridpoint_maxmin + else + ! Search went out of regional grid bounds.... + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + print *,' ttest at location F' + + ! For a "tracker" case, check to see if the user has + ! requested to compute and write out the ROCI. If + ! so, then we make a call to check_closed_contour, + ! being sure to specify 999 as the number of levels + ! to check.... + + if (isiret1 == 0 .and. isastorm(1) == 'Y' .and. + & trkrinfo%type == 'tracker') then + + if (trkrinfo%want_oci) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + if (xval(9) < 1100.0) then + ! Pressure units are in mb... + prs_contint_thresh = 4.0 + elseif (xval(9) >80000.0) then + ! Pressure units are in Pa... + prs_contint_thresh = 400.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' tracker. The mslp value' + print *,' (xval(9)) is not in range.' + print *,' before call to' + print *,' check_closed_contour.' + print *,' xval(9) = ',xval(9) + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + if (trkrinfo%contint < prs_contint_thresh) then + hold_old_contint = trkrinfo%contint + trkrinfo%contint = prs_contint_thresh + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before going into routine to diagnose' + print *,'the ROCI for a tracker run, the ' + print *,'requested contour interval is being ' + print *,'adjusted up (coarser) to avoid having' + print *,'the contour check routine break and ' + print *,'return an invalid value.' + print *,'User-requested contint value (Pa) = ' + & ,hold_old_contint + print *,'Modified contint value (Pa) = ' + & ,trkrinfo%contint + endif + endif + + masked_outc = .false. + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ' + & ,rlastbar,' nm' + print *,' ' + endif + + endif + + endif + + ! For the midlat & tcgen cases, do a check to see if + ! there is a closed mslp contour. The ifix and jfix + ! values passed into check_closed_contour are the + ! values for the (i,j) at the gridpoint minimum, + ! which was obtained just above from the call to + ! fix_latlon_to_ij. + ! UPDATE 7/12/2016 tpm: A change was made to fix a + ! hole in the logic. Previously, for a genesis run + ! (type = midlat or tcgen), if a fix was not made + ! for mslp, then the isastorm(1) flag would not be + ! 'Y', and so the call to check_closed_contour in + ! the following IF statement would not be made, and + ! that would prevent the mask from getting updated + ! for this particular storm, allowing the same storm + ! to be detected when the scan for new storms takes + ! place at this lead time (i.e., after all previously- + ! known storms from the last lead time have been + ! tracked). As a fix, if that isastorm(1) flag is not + ! 'Y', then we call a new subroutine which updates the + ! mask based on the circulation at 850 mb. + + if (isastorm(1) == 'Y' .and. isiret1 == 0 .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ',rlastbar + & ,' nm' + print *,' ' + endif + + ! This next bit of code adds a second layer of closed + ! contour checking. This is to decrease the + ! occurrence of interrupted midlat and tcgen tracks, + ! which usually happens when the closed contour + ! criterion is not met for one time period. So in + ! this next code, we check to see if the ccflag was + ! 'y' for at least half the time over the last 24h. + ! For time periods shorter than 24h (e.g., the storm + ! was just detected at 144h and we are now at 156h), + ! the threshold is still that for at least half of + ! the time the system has been detected as a storm, + ! it must have a ccflag value of 'y'. + + if (ccflag == 'y') then + closed_mslp_ctr_flag(ist,ifh) = 'y' + else + closed_mslp_ctr_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & closed_mslp_ctr_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (closed_mslp_ctr_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.50) then + ccflag = 'y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE ON CLOSED CONTOUR CHECK: The' + print *,' ccflag returned for this hour was' + print *,' NO, but a check of recent ccflags' + print *,' indicates that more than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + ccflag = 'n' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!! NOTE ON CLOSED CONTOUR CHECK: The' + print *,'!! ccflag returned for this hour was' + print *,' NO, and a check of recent ccflags' + print *,' indicates that less than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + if (ccflag == 'y') then + isastorm(2) = 'Y' + else if (ccflag == 'n') then + isastorm(2) = 'N' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*---------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*---------------------------------------*' + print *,' ' + endif + + + else if (isastorm(1) /= 'Y' .and. + & calcparm(3,ist) .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + ! The isastorm(1) flag indicates that a mslp gradient + ! could not be found at this lead time, so the mask + ! cannot be updated using mslp. Instead, + ! do a check of the 850 mb wind circulation + ! surrounding the 850 wind circulation fix, and then + ! set the mask to be TRUE for all points within the + ! area where mean cyclonic Vt exceed +1 m/s.... + +c call check_closed_contour (imax,jmax,ifix,jfix,slp +c & ,valid_pt,masked_outc,ccflag,'min',trkrinfo +c & ,999,contour_info,get_last_isobar_flag,plastbar +c & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Calling mask_based_on_wind_circ at ' + & ,ifcsthour + endif + + call mask_based_on_wind_circ (imax,jmax,dx,dy,850 + & ,valid_pt,masked_outc,trkrinfo + & ,clon(ist,ifh,3),clat(ist,ifh,3),inp%modtyp + & ,imbowret) + + endif + + ! For tropical cyclones, check the avg 850 mb tangential + ! windspeed close to the storm center.... + + if (trkrinfo%type == 'tcgen' .or. + & trkrinfo%type == 'tracker') then + + had_to_try_backup_850_vt_check = 'n' + + if (calcparm(3,ist)) then + + if (verb .ge. 3) then + print *,' ' + print *,'Checking 850 mb Vt speed using 850 mb ' + print *,'wind circulation fix: ' + print *,' 850 mb wcirc fix lon= ',clon(ist,ifh,3) + print *,' 850 mb wcirc fix lat= ',clat(ist,ifh,3) + print *,' Multi-parm fix lon= ',fixlon(ist,ifh) + print *,' Multi-parm fix lat= ',fixlat(ist,ifh) + print *,' ' + endif + + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,clon(ist,ifh,3),clat(ist,ifh,3) + & ,xval(3),trkrinfo,isastorm(3),isiret3) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for 850 mb wind + ! circulation (maybe the 850 mb wind circulation fix + ! was too far away from the guess?), then this check + ! isn't performed. We are changing this so that the + ! 850 mb Vt wind speed check will still be + ! performed, but using the mean fixlat and fixlon + ! positions as the center. Still, we first need to + ! check to see if 850 mb u-comp and v-comp were even + ! read in. If they weren't, then we are just out + ! of luck. + + had_to_try_backup_850_vt_check = 'y' + isiret3 = -99 + + if (trkrinfo%use_backup_850_vt_check == 'y' .or. + & trkrinfo%use_backup_850_vt_check == 'Y') then + + if (readflag(3) .and. readflag(4)) then + + if (verb .ge. 3) then + print *,' ' + print *,'!!! NOTE: 850 mb wcirc fix not ' + print *,'available. We are instead ' + print *,'checking 850 mb Vt speed using ' + print *,'multi-parm fix position: ' + print *,' Multi-parm fix lon= ' + & ,fixlon(ist,ifh) + print *,' Multi-parm fix lat= ' + & ,fixlat(ist,ifh) + print *,' ' + endif + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,0.00,trkrinfo,isastorm(3),isiret3) + + endif + + endif + + endif + + if (calcparm(3,ist) .or. + & (had_to_try_backup_850_vt_check == 'y' .and. + & isiret3 == 0) ) then + + if (trkrinfo%type == 'tcgen') then + ! This next bit of code adds a second layer of 850 + ! mb Vt magnitude checking. This is to decrease + ! the occurrence of interrupted tcgen tracks, + ! which occasionally happens for weak storms when + ! this criterion is not met for one time period. + ! So in this next code, we check to see if the + ! vt850_flag was 'y' for at least 75% of the time + ! over the last 24h. For time periods shorter + ! than 24h (e.g., the storm was just detected at + ! 144h and we are now at 156h), the threshold is + ! still that for at least 75% of the time the + ! system has been detected as a storm, it must + ! have a vt850_flag value of 'y'. + + if (isastorm(3) == 'Y') then + vt850_flag(ist,ifh) = 'y' + else + vt850_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & vt850_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - + & fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (vt850_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / + & cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.75) then + isastorm(3) = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ NOTE ON Vt_850 CHECK: The ' + print *,' isastorm flag returned for ' + print *,' this hour was NO, but a' + print *,' check of recent vt850_flags' + print *,' indicates that more than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE ON Vt_850 CHECK: The ' + print *,'!!! isastorm flag returned for ' + print *,' this hour was NO, and a' + print *,' check of recent vt850_flags ' + print *,' indicates that less than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + endif + + endif + endif + + else + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + isastorm(1) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! could not be made for mslp, ' + print *,'!!! therefore we will stop tracking ' + print *,'!!! for this storm.' + endif + + else + isastorm(1) = 'N' + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a TC tracker case, a fix could' + print *,'!!! not be made using any tracked parms,' + print *,'!!! therefore we will stop tracking for' + print *,'!!! this storm.' + endif + + endif + + if ( verb .ge. 3 ) then + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + + endif + + if (isiret1 /= 0 .or. isiret2 /= 0 .or. isiret3 /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: One of the calls to ' + print *,'!!! is_it_a_storm produced an error.' + print *,'!!! Chances are this is from a call to ' + print *,'!!! get_ij_bounds, meaning we are too close' + print *,'!!! to a regional grid boundary to do this ' + print *,'!!! analysis. Processing will continue....' + print *,'!!! isiret1= ',isiret1,' isiret2= ',isiret2 + print *,'!!! isiret3= ',isiret3 + endif + + endif + + if (isastorm(1) == 'N' .or. isastorm(2) == 'N' .or. + & isastorm(3) == 'N') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! At least one of the isastorm flags from' + print *,'!!! subroutine is_it_a_storm is "N", so ' + print *,'!!! either we were unable to find a good ' + print *,'!!! mslp gradient and/or a valid 850 mb ' + print *,'!!! circulation for the storm at this time,' + print *,'!!! or, for the cases of midlat or tcgen ' + print *,'!!! tracking, a closed mslp contour could ' + print *,'!!! not be found, thus we will stop tracking' + print *,'!!! this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! mslp gradient flag = ',isastorm(1) + print *,'!!! closed contour flag = ',isastorm(2) + print *,'!!! 850 mb winds flag = ',isastorm(3) + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + + ! Now do another check for the tracker and tcgen cases. + ! If the isastorm flags for mslp gradient and v850 BOTH + ! came back positive AND you have been able to locate an + ! 850 mb vort center, just do a check to make sure that + ! the distance between the 850 vort center and the mslp + ! center is not too great. + + if (trkrinfo%type == 'tracker' .or. + & trkrinfo%type == 'tcgen') then + if (isastorm(1) == 'Y' .and. isastorm(3) == 'Y' .and. + & calcparm(1,ist) .and. stormswitch(ist) == 1) then + +c if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) >= 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) < 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else +c trkrinfo%max_mslp_850 = 323.0 +c endif + + call calcdist (clon(ist,ifh,9),clat(ist,ifh,9) + & ,clon(ist,ifh,1),clat(ist,ifh,1),dist + & ,degrees) + + if (dist > trkrinfo%max_mslp_850) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, the dist betw' + print *,'!!! the mslp center & the 850 zeta ' + print *,'!!! center is too great, thus we will' + print *,'!!! stop tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + print *,'!!! Actual distance (km) = ',dist + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Actual distance between the parm centers' + print *,'for 850 zeta and mslp is ',dist,' (km)' + print *,'Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + endif + + endif + endif + endif + + ! Do one final check. Check the new fix position and + ! the old fix position and calculate the speed that the + ! storm would have had to travel to get to this point. + ! If that speed exceeds a certain threshold (~60 kt), + ! assume you're tracking the wrong thing and quit. + ! Obviously, only do this for times > 00h. The check + ! in the if statement to see if the previous hour's + ! lats and lons were > -999 is for the midlat and + ! tcgen cases -- remember, they can have genesis at + ! any hour of the forecast, in which case the previous + ! forecast hour's lat & lon would be -999. + + if (ifh > 1 .and. stormswitch(ist) == 1) then + if (fixlon(ist,ifh-1) > -999.0 .and. + & fixlat(ist,ifh-1) > -999.0 ) then + + if (trkrinfo%type == 'midlat') then + xmaxspeed = maxspeed_ml + else + xmaxspeed = maxspeed_tc + endif + + call calcdist (fixlon(ist,ifh-1),fixlat(ist,ifh-1) + & ,fixlon(ist,ifh),fixlat(ist,ifh),dist + & ,degrees) + + ! convert distance from km to nm and get speed. + + distnm = dist * 0.539638 + xinterval_fhr = fhreal(ifh) - fhreal(ifh-1) + xknots = distnm / xinterval_fhr + + if (xknots > xmaxspeed) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, calculated spd' + print *,'!!! of the storm from the last position' + print *,'!!! to the current position is too high,' + print *,'!!! so we will stop tracking this storm' + print *,'!!! (For fear that we are not actually ' + print *,'!!! tracking our storm, but have instead' + print *,'!!! locked onto some other feature....)' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max speed allowed (kt) = ',xmaxspeed + print *,'!!! Actual speed (kt) = ',xknots + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'The average speed that the storm moved' + print *,'at since the previous forecast time is' + & ,xknots,' knots.' + endif + + endif + + endif + + endif + + endif + +c Now get the maximum near-surface wind speed near the storm +c center (get_max_wind). Also, call getradii to get the +c radii in each storm quadrant of gale-force, storm-force +c and hurricane force winds. + + if (readflag(10) .and. readflag(11) .and. ifret == 0 + & .and. stormswitch(ist) == 1) then + call get_max_wind (fixlon(ist,ifh),fixlat(ist,ifh) + & ,imax,jmax,dx,dy,valid_pt,levsfc + & ,xmaxwind(ist,ifh),trkrinfo,rmax,igmwret) +c if (igmwret /= 0 .and. gridmove_status == 'stopped') then + if (igmwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Return code from get_max_wind is /= 0. ' + print *,'!!! rcc= igmwret= ',igmwret + print *,'!!! Also, this is a moveable, regional grid' + print *,'!!! and the grid did not change from last' + print *,'!!! lead time to current one, so what has' + print *,'!!! likely happened is that the storm has ' + print *,'!!! moved close to the edge of the nested ' + print *,'!!! grid domain, but the nested grid itself' + print *,'!!! had stopped moving, probably because it' + print *,'!!! dropped or lost the storm.' + print *,'!!! ' + print *,'!!! TRACKING WILL STOP FOR THIS STORM' + print *,'!!! ' + endif + + stormswitch(ist) = 2 + cycle stormloop + endif + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + + ! For the radii, we encountered a problem with radmax + ! being too small. It was set at 650 km. Hurricane + ! Sandy exceeded this in the models, so the values + ! returned from getradii were close to the default + ! radmax value of 650 km (350 nm), instead of higher. + ! To fix it, we now use an iterative technique, where + ! we start with radmax as a small value (500 km). If + ! getradii returns a value for R34 in a quadrant that + ! does not exceed 0.97*radmax, then that value is ok. + ! If it does exceed 0.97*radmax, then we bump up radmax + ! by 50 km and call getradii again, looking to diagnose + ! radii only in those quadrants where the + ! need_to_expand_r34 flag = 'n'. BTW... note the + ! initial IF statement... we will only go into this + ! routine if the max wind just diagnosed for this lead + ! time is at least 34 kts (17.5 m/s). + + if (xmaxwind(ist,ifh) >= 17.5) then + + vradius = 0 + first_time_thru_getradii = .true. + r34_check_okay = 'n' + do ivr = 1,4 + need_to_expand_r34(ivr) = 'y' + enddo + radmax = 500.0 ! Initial radmax, in km + + igrct = 1 + + if ( verb .ge. 3 ) then + write (6,242) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 242 format (1x,'TIMING: b4 getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + getrad_iter_loop: do while + & (r34_check_okay == 'n' .and. radmax <= 1050.) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,244) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 244 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + call getradii (fixlon(ist,ifh),fixlat(ist,ifh),imax + & ,jmax,dx,dy,valid_pt,storm(ist)%tcv_storm_id + & ,ifcsthour,xmaxwind(ist,ifh),vradius + & ,trkrinfo,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) + + if (igrret /= 0) then + if (verb >= 3) then + print *,' ' + print *,'!!! ERROR: Return code from getradii = ' + & ,igrret + print *,'!!! Searching for radii will not be ' + print *,'!!! completed for this lead time and' + print *,'!!! all radii values will be set to ' + print *,'!!! missing.' + print *,' ' + exit getrad_iter_loop + endif + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,245) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 245 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + first_time_thru_getradii = .false. + igrct = igrct + 1 + r34_dist_thresh = 0.97 * radmax + r34_good_ct = 0 + do ivr = 1,4 + vradius_km = float(vradius(1,ivr)) / 0.5396 + if (vradius_km < r34_dist_thresh) then + r34_good_ct = r34_good_ct + 1 + need_to_expand_r34(ivr) = 'n' + endif + enddo + if (r34_good_ct == 4) then + r34_check_okay = 'y' + endif + radmax = radmax + 50.0 + enddo getrad_iter_loop + + if ( verb .ge. 3 ) then + write (6,246) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 246 format (1x,'TIMING: after getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + endif + + endif + +c If the user has requested so, then call a routine to +c determine the type of cyclone, using Bob Hart's +c cyclone phase space (CPS) algorithms. It is only used +c for times after t=0, since for the first check (of the +c "parameter B" thickness asymmetry), we need to know +c in which direction the storm is moving. Pulling that +c storm movement data off of the tcvitals is not reliable +c since the model storm may not be moving in the same +c direction as the observed storm. However, we could do +c an upgrade later where this storm movement data is +c pulled from the "genesis vitals", which are derived +c from the model forecast data itself, not the obs. + + if (phaseflag == 'y' .and. stormswitch(ist) == 1) then + wcore_flag = 'u' ! 'u' = undetermined + call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag,igpret) + endif + + if (structflag == 'y' .or. ikeflag == 'y') then + call get_sfc_center (fixlon(ist,ifh),fixlat(ist,ifh) + & ,clon,clat,ist,ifh,calcparm,xsfclon + & ,xsfclat,maxstorm,igscret) + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,er_wind,sr_wind,er_vr,sr_vr + & ,er_vt,sr_vt,maxstorm,trkrinfo,igwsret) + if (igwsret == 0) then + call output_wind_structure (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),er_wind,sr_wind + & ,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iowsret) + endif + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,wfract_cov,pdf_ct_bin + & ,pdf_ct_tot,maxstorm,trkrinfo,igfwret) + if (igfwret == 0) then + call output_fract_wind (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),wfract_cov,'earth' + & ,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) + endif + endif + + if (ikeflag == 'y' .and. stormswitch(ist) == 1) then + call get_ike_stats (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,ike,sdp,wdp,maxstorm + & ,trkrinfo,igisret) + if (igisret == 0) then + call output_ike (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),ike,sdp,wdp,maxstorm + & ,ioiret) + endif + endif + +c Now print out the current fix position and intensity +c (in knots) to standard output. Conversion for m/s to +c knots (1.9427) is explained in output_atcf. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to fixcenter, fix positions at ' + write (6,442) ifhours(ifh),ifclockmins(ifh) + 442 format (1x,'forecast hour= ',i4,':',i2.2,' follow:') + print *,' ' + endif + + if (ifret == 0 .and. stormswitch(ist) == 1) then + + if ( verb .ge. 3 ) then + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + & ,int((xmaxwind(ist,ifh)*1.9427) + 0.5) + print *,' ' + endif + + ! Only call output routines every atcffreq/100 hours.... + + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + + if (leadtime_check == 0) then + + ifcsthour = ileadtime / 100 + + call output_atcfunix (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + + ! Get the storm motion vector and the speed of + ! motion so that we can output this in the + ! "atcf_sink" forecast text file. + + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'vitals',trkrinfo + & ,ignret) + else + istmdir = -999 + istmspd = -999 + ignret = 0 + endif + + if ( verb .ge. 3 ) then + write (6,617) istmspd,istmdir,ignret + 617 format (1x,'+++ RPT_STORM_MOTION: istmspd= ',i5 + & ,' istmdir= ',i5,' rcc= ',i3) + endif + + ! Call a routine to find the mean & max relative + ! vorticity near the storm at 850 & 700. These will + ! be written out to the "atcf_sink" fcst text file. + + imeanzeta = -99 + igridzeta = -99 + call get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo + & ,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + endif + + call output_atcf_sink (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta + & ,igridzeta,cps_vals,plastbar,rlastbar + & ,ioaxret) + + if (inp%model == 12 .and. ifcsthour == 0) then + ! Write vitals for GFS ens control analysis + call output_tcvitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,iovret) + + endif + endif + + ! The exception here is for the call to the output_hfip + ! routine, which will be called for every lead time + ! that is processed.... + + call output_hfip (fixlon(ist,ifh),fixlat(ist,ifh),inp,ist + & ,ifh,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,rmax,ioaxret) + else + + if ( verb .ge. 3 ) then + write (6,452) 'fixpos ',storm(ist)%tcv_storm_id + & ,' fhr= ',ifhours(ifh),ifclockmins(ifh) + & ,' Fix not made for this forecast hour' + 452 format (1x,a7,1x,a4,a6,i4,':',i2.2,a36) + + print *,' ' + print *,'!!! RETURN CODE from fixcenter not equal to 0,' + print *,'!!! or output from is_it_a_storm indicated the' + print *,'!!! system found was not our storm, or the ' + print *,'!!! speed calculated indicated we may have ' + print *,'!!! locked onto a different center, thus a fix' + print *,'!!! was not made for this storm at this ' + print *,'!!! forecast hour.' + print *,'!!! mslp gradient check = ',isastorm(1) + print *,'!!! mslp closed contour check = ',isastorm(2) + print *,'!!! 850 mb winds check = ',isastorm(3) + print *,'!!! fixcenter return code = ifret = ',ifret + print *,' ' + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + +c if (inp%model == 1 .or. inp%model == 8 .or. +c & inp%model == 22) then +cPENG + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + + ! For the vt=00h lead time, if the tracker failed to + ! locate a position, we are going to write out an + ! atcfunix record that contains the position, + ! intensity, mslp and 34-kt wind radii from TC Vitals + ! for this storm and initial time. Only do this for + ! the GFS or GDAS runs of the tracker. + + tcv_max_wind_ms = float(storm(ist)%tcv_vmax) + tcv_mslp_pa = float(storm(ist)%tcv_pcen) * 100.0 + + ! Convert tcvitals NE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15ne) + if (r34_from_tcv > 0.0) then + vradius(1,1) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,1) = 0 + endif + + ! Convert tcvitals SE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15se) + if (r34_from_tcv > 0.0) then + vradius(1,2) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,2) = 0 + endif + + ! Convert tcvitals SW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15sw) + if (r34_from_tcv > 0.0) then + vradius(1,3) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,3) = 0 + endif + + ! Convert tcvitals NW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15nw) + if (r34_from_tcv > 0.0) then + vradius(1,4) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,4) = 0 + endif + + ! Convert tcvitals roci from km to nm + + if (storm(ist)%tcv_penvrad > 0) then + roci_from_tcv = float(storm(ist)%tcv_penvrad) + rlastbar = roci_from_tcv * 0.5396 + else + rlastbar = -99.0 + endif + + ! Convert tcvitals pressure at roci from km to nm + + if (storm(ist)%tcv_penv > 0) then + proci_from_tcv = float(storm(ist)%tcv_penv) + plastbar = proci_from_tcv * 100.0 + else + plastbar = -99.0 + endif + + write (6,291) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + & ,atcfymdh + 291 format (1x,'NOTE: TCVITALS_USED_FOR_ATCF_F00 ' + & ,' Storm ID: ',a4,' Storm name: ',a9 + & ,' YMDH: ',i10) + + call output_atcfunix (slonfg(ist,ifh) + & ,slatfg(ist,ifh),inp,ist + & ,ifcsthour,tcv_max_wind_ms + & ,tcv_mslp_pa,vradius,maxstorm,trkrinfo + & ,plastbar,rlastbar,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + else + + ! For all other models, we print out missing + ! data values at tau=00h if the tracker was + ! unable to find the storm.... + + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + + endif + + imeanzeta = -99 + igridzeta = -99 + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (trkrinfo%type == 'tracker') then + ! Update 11/11: For a 'tracker' run, i.e., one in + ! which we know that there is an observed storm in + ! the area, we will assume that there was some type + ! of problem in the initialization that prevented + ! the storm from being found. In this case, even + ! though we have written out zeroes for the 00h + ! time, we want to at least try tracking again at + ! the next lead time. Requested by HWRF folks.... + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',ist + write (6,301) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + 301 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + call get_next_ges (slonfg,slatfg,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + stormswitch(ist) = 1 + endif + + endif + cycle stormloop + endif + + +c Now get first guess for next forecast time's position. +c But first, if this is the first time level (ifh=1) and +c the user has requested that storm vitals be output (this +c is usually only done for model analyses in order to get +c an analysis position from one time to the next), we will +c write out a storm vitals record for this time level. +c Note that we have already gotten the next guess position +c info just above for the case of the repeated analysis +c data, so we'll just output the genesis vitals record. + + if (ifh <= ifhmax) then + if (ifh == 1 .and. trkrinfo%out_vit == 'y') then + call output_gen_vitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,istmspd,istmdir,iovret) + endif + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: Problem getting first guess ' + print *,'!!! position for next lead time. Return' + print *,'!!! code from call to get_next_ges = ' + print *,'!!! ignret = ',ignret + print *,'!!! Storm name = ' + & ,storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! TRACKING WILL STOP FOR THIS STORM.' + endif + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + else + istmdir = -999 + istmspd = -999 + endif + endif + + case (2) + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Case 2 in tracker for stormswitch' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + endif + + case (3) + continue + +c print *,' ' +c print *,'!!! Case 3 in tracker for stormswitch' +c print *,'!!! Storm name = ',storm(ist)%tcv_storm_name +c print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + if (leadtime_check == 0) then + ifcsthour = ileadtime / 100 + endif + if (trkrinfo%inp_data_type == 'grib') then + call output_tracker_mask (masked_outc,valid_pt,ifh + & ,ifcsthour,imax,jmax,iotmret) + endif + endif + + if(use_per_fcst_command=='y') then +c User wants us to run a command per forecast time + +! Replace %[FHOUR] with forecast hour, %[FMIN] with forecast minute. + +! The %[] format is chosen to avoid shell syntax errors if someone +! includes unknown %[] constructs. A stray , for example, +! would generate syntax errors or unexpected results in some +! shells. + +! If an unrecognized %[xxx] sequence is used, it will be retained in +! the final command. This allows the underlying command to detect +! the unreplaced %[] and use suitable default values or abort, as +! appropriate. + + pfc_final=per_fcst_command + call argreplace(pfc_final,pfc_cmd_len,'%[FHOUR]', & + & ifhours(ifh)) + call argreplace(pfc_final,pfc_cmd_len,'%[FMIN]', & + & iftotalmins(ifh)) + + if(verb.ge.2) then + print *,' ' + print *,'!!! Running per-fcst command' + print *,'!!! Unparsed = ',trim(per_fcst_command) + print *,'!!! Parsed = ',trim(pfc_final) + endif + call run_command(trim(pfc_final),pfcret) + if(pfcret/=0 .and. verb.ge.1) then + print *,' ' + print *,'!!! Non-zero exit status from per-fcst command' + print *,'!!! Command = ',trim(pfc_final) + print *,'!!! Exit status = ',pfcret + print *,'!!! Continuing anyway...' + elseif(pfcret==0 .and. verb.ge.2) then + print *,' ' + print *,'!!! Per-fcst command returned success status (0)' + endif + endif + + ifh = ifh + 1 + if (ifh > ifhmax) exit ifhloop + + if (inp%file_seq == 'multi') then + call baclose(lugb,igcret) + call baclose(lugi,iicret) + if ( verb .ge. 3 ) then + print *,'baclose return code for unit ',lugb,' = igcret = ' + & ,igcret + print *,'baclose return code for unit ',lugi,' = iicret = ' + & ,iicret + endif + endif + + enddo ifhloop +c +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) +c + 73 format ('fixpos ',a4,' fhr= ',i4,':',i2.2,' Fix position= ' + & ,f7.2,'E (',f6.2,'W)',2x,f7.2,' Max Wind= ',i3,' kts') + + if (allocated(prstemp)) deallocate (prstemp) + if (allocated(prsindex)) deallocate (prsindex) + if (allocated(iwork)) deallocate(iwork) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(vt850_flag)) deallocate (vt850_flag) + if (allocated(closed_mslp_ctr_flag)) + & deallocate (closed_mslp_ctr_flag) + if (allocated(netcdf_file_time_values)) + & deallocate (netcdf_file_time_values) + if (allocated(nctotalmins)) + & deallocate (nctotalmins) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine argreplace(arg,n,name,val) + ! This subroutine is used to generate the pre-forecast-command + ! It will edit the command (argument "arg") and replace string + ! name with value val. That is how the per-forecast-command + ! has these modifications: + + ! %[FHOUR] -> replace with -> last forecast hour + ! %[FMIN] -> replace with -> last forecast minute + + implicit none + + integer, intent(in) :: n + character(n), intent(inout) :: arg + character(*), intent(in) :: name + integer, intent(in) :: val + + integer found,namelen,i1,i2 + character(n) :: out + + found=index(arg,name) + namelen=len(name) + i1=found-1 ! last char that is before name + i2=found+namelen ! index of last char in name + + if(found==0) return + + out=' ' + + if(found>1 .and. i21) then +! special case: name is at end of string +! hope the value fits... + write(out,'(A,I0)') arg(1:i1),val + elseif(i2 + & ,'... gopen_i_file= ...',a,'...') + + print *,'gopen_g_file= ',gopen_g_file,'....' + print *,'gopen_i_file= ',gopen_i_file,'....' + + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + inquire (unit=lout, opened=output_file_open) + if (output_file_open) then + iooret = 0 + else + fnameo(1:5) = "fort." + write(fnameo(6:7),'(I2)') lout + call baopenw (lout,fnameo,iooret) + endif + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + inquire (file=gopen_g_file, opened=file_open4) + if (file_open4) then + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is OPEN' + else + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is CLOSED' + endif + + inquire (file=gopen_i_file, opened=file_open5) + if (file_open5) then + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is OPEN' + else + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'gettrk baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine open_ncfile (filename,ncid) + +c ABSTRACT: This subroutine opens a netcdf file specified by the +c input file "ncfile" and returns the netcdf file id that will be +c associated with that file. +c +c INPUT: +c ncfile character full-path file netcdf name +c +c OUTPUT: +c ncfile_id integer, netcdf id assigned to the netcdf file + + implicit none + + include "netcdf.inc" + + character*(*), intent(in) :: filename + integer, intent(out) :: ncid + integer :: status + + status = nf_open (filename, NF_NOWRITE, ncid) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine open_ncfile +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine is_it_a_storm (imax,jmax,dx,dy,cparm,ist + & ,defined_pt,parmlon,parmlat + & ,parmval,trkrinfo,stormcheck,isiret) + +c ABSTRACT: This subroutine is called after the center of the storm +c has been fixed. Its purpose is to determine whether or not +c the center that was found is actually a storm, and not just some +c passing trough (this has happened in the case of decaying or weak +c storms). It's called twice -- once to check for a minimum MSLP +c gradient, and once to check for a circulation at 850 mb. The +c subroutine input parameter "cparm" determines which parameter to +c check for. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is to be checked: +c slp = mslp, for a check of mslp gradient +c v850 = tangential winds at 850 mb +c ist integer storm number (internal to the tracker) +c defined_pt Logical; bitmap indicating if valid data at that pt. +c parmlon Longitude of the max/min value for the input parameter +c parmlat Latitude of the max/min value for the input parameter +c parmval Data value at parm's max/min point (used for mslp call) +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c stormcheck Character; set to 'Y' if mslp gradient or 850 mb +c tangential winds check okay. +c isiret Return code for this subroutine. +c + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE tracked_parms; USE atcf; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + real vt,vtavg,vr,parmlat,parmlon,parmval,dist + real pthresh,vthresh,degrees,dx,dy,dell,ri,radinf + real pgradient,xmaxpgrad + character(*) cparm + logical(1) defined_pt(imax,jmax) + character*1 stormcheck + integer isiret,imax,jmax,ist,npts,ilonfix,jlatfix,igvtret + integer ibeg,iend,jbeg,jend,ivt,i,j,iix,jix,bskip,igiret + + isiret = 0 + stormcheck = 'N' + + dell = (dx+dy)/2. + +c First define the radius of influence, which depends on the +c grid spacing of the model data being used. The ceiling statement +c for npts in the first if statement is needed in case the +c resolution of the grib files eventually goes very low, down to +c say a half degree or less, in order to cover enough points in +c the search. + + if (dell < 1.24) then ! GFS, MRF, NAM, NGM, NAVGEM, GDAS, + ! GFDL, NCEP Ensemble & Ensemble + ! Relocation, SREF Ensemble + ri = ritrk_most + if (cparm == 'slp') then + radinf = 300.0 + else + radinf = 225.0 + endif + npts = ceiling(radinf/(dtk*(dx+dy)/2.)) + else if (dell >= 1.24 .and. dell < 2.49) then ! UKMET + ri = ritrk_most + radinf = 275.0 + npts = 2 + else ! ECMWF + ri = ritrk_coarse + radinf = 350.0 + npts = 1 + endif + + pthresh = trkrinfo%mslpthresh ! These are read in in + vthresh = trkrinfo%v850thresh ! subroutine read_nlists.... + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,parmlon,parmlat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij B, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij B, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print*,' ' + print*,'!!! ERROR in is_it_a_storm from call to' + print*,'!!! get_ij_bounds, stopping processing for ' + print*,'!!! storm number ',ist + endif + + isiret = 92 + return + endif + +c If the input cparm is slp, then check to see that the MSLP +c gradient in any direction from the MSLP center is at least +c 1mb / 200km, or 0.005mb/km. This is based on discussions with +c Morris & Bob, who have had good results using a 2mb/200km +c requirement. Since their model has a much finer resolution than +c all of the models we run the tracker on AND a much better +c depiction of the hurricane vortex, we do not use a requirement +c as strict as theirs, and so make the requirement only half as +c strong as theirs. +c +c If the input cparm is v850, then check to see that there is +c a circulation at 850 mb. We will do this by calculating the +c tangential wind of all points within a specified radius of +c the 850 minimum wind center, and seeing if there is a net +c average tangential wind speed of at least 5 m/s. +c +c UPDATE APRIL 2000: I've relaxed the thresholds slightly from +c 0.005 mb/km to 0.003 mb/km, and the wind threshold from +c 5 m/s to 3 m/s. Also, note that a special case for GDAS has +c been hardwired in that is weaker (0.002 mb/km and 2 m/s). +c That weaker GDAS requirement is for Qingfu's relocation stuff. +c +c UPDATE JULY 2001: The relaxed requirement put in place in +c April 2000 for the GDAS relocation has also been put in place +c for the GFS ensemble relocation. + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the loop. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, ilonfix= ',ilonfix + & ,' jlatfix= ',jlatfix + print *,'ibeg jbeg iend jend = ',ibeg,jbeg,iend,jend + print *,'cparm= ',cparm,' parmlon parmlat = ',parmlon,parmlat + print *,'parmval= ',parmval + print *,' ' + endif + + vtavg = 0.0 + ivt = 0 + + xmaxpgrad = -999.0 + + jloop: do jix = jbeg,jend,bskip + iloop: do iix = ibeg,iend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine is_it_a_storm' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + i = iix - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine ' + print *,'!!! is_it_a_storm for a non-global grid.' + print *,'!!! STOPPING....' + print *,'!!! i= ',i,' imax= ',imax + print *,' ' + endif + + stop 97 + endif + endif + + call calcdist(parmlon,parmlat,glon(i),glat(j),dist,degrees) + + if (dist > radinf .or. dist == 0.0) cycle + + if (defined_pt(i,j)) then + + if (cparm == 'slp') then + pgradient = (slp(i,j) - parmval) / dist + if (pgradient > xmaxpgrad) xmaxpgrad = pgradient + + if ( verb .ge. 3 ) then + write (6,93) i,j,glon(i),glat(j),dist,slp(i,j),pgradient + endif + + if (pgradient > pthresh) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, valid pgradient found.' + print '(a23,f8.5)',' pgradient threshold = ',pthresh + print '(a23,f8.5)',' pgradient found = ',pgradient + print *,'mslp center = ',parmlon,parmlat,parmval + print *,'pgrad loc = ',glon(i),glat(j),slp(i,j) + endif + + stormcheck = 'Y' + exit jloop + endif + endif + + if (cparm == 'v850') then + call getvrvt (parmlon,parmlat,glon(i),glat(j) + & ,u(i,j,nlev850),v(i,j,nlev850),vr,vt,igvtret) + if ( verb .ge. 3 ) then + write (6,91) i,j,glon(i),glat(j),u(i,j,nlev850) + & ,v(i,j,nlev850),vr,vt + endif + + vtavg = vtavg + vt + ivt = ivt + 1 + endif + + endif + + enddo iloop + enddo jloop + + 91 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' u= ',f8.4,' v= ',f8.4,' vr= ',f9.5,' vt= ',f9.5) + + 93 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' dist= ',f8.2,' slp= ',f10.2,' pgradient= ',f8.5) + + if (stormcheck /= 'Y' .and. cparm == 'slp') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, valid pgradient NOT FOUND.' + write (6,94) '!!! (Max pgradient less than ',pthresh,' mb/km)' + 94 format (1x,a29,5x,f8.5,a7) + write (6,95) '!!! Max pgradient (mb/km) found = ',xmaxpgrad + 95 format (1x,a34,f8.5) + print *,' ' + endif + + endif + + if (cparm == 'v850') then + + if (ivt > 0) then + vtavg = vtavg / float(ivt) + else + vtavg = 0.0 + endif + + if (parmlat > 0) then + if (vtavg >= vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (>= +',vthresh,' m/s for a NH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed +',vthresh + & ,' m/s (NH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + else + if (vtavg <= -vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (<= -',vthresh,' m/s for a SH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed -',vthresh + & ,' m/s (SH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + endif + + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag,igpret) +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure or phase of a cyclone. Initially, we +c will just have it use the Hart cyclone phase space (CPS) scheme. + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trkrparms; USE grid_bounds + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character wcore_flag*1,okay_to_call_cps_routines*1 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real cps_vals(3) + real dx,dy,paramb,vtl_slope,vtu_slope + integer imax,jmax,igpret,igcpret,ist,ifh,maxstorm + integer igvpret,igcv1ret,igcv2ret + logical(1) valid_pt(imax,jmax) +c + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,611) + write (6,613) + write (6,615) + write (6,*) ' ' + + 611 format(1x,'#-----------------------------------------------#') + 613 format(1x,'# start of routine to determine cyclone phase...#') + 615 format(1x,'#-----------------------------------------------#') + endif + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + if (ifh > 1 .or. (ifh == 1 .and. trkrinfo%type == 'tracker')) + & then + + ! This condition that ifh > 1 is so that we *not* do the cps + ! stuff for fhour=0 if it's a tcgen or midlat case, since we + ! don't know the model storm motion direction for the + ! analysis. For a regular case where type = 'tracker', we + ! have the observed storm's heading direction from tc vitals, + ! so we can use that (even though the model's storm direction + ! may differ slightly from the observed storm). This current + ! if statement and the ones below carefully check for these + ! various instances. + + okay_to_call_cps_routines = 'n' + + if (ifh > 1) then + if (fixlon(ist,ifh-1) > -990.0 .and. + & fixlat(ist,ifh-1) > -990.0) then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level ' + print *,' >< since the fixlon and fixlat at the ' + print *,' >< previous lead time are undefined.' + print *,' >< This is likely the first found position' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + endif + endif + elseif (ifh == 1 .and. trkrinfo%type == 'tracker') then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level.' + print *,' >< The likely reason is that ifh=0 and' + print *,' >< this is a genesis case, so we do not ' + print *,' >< know the storm motion direction.' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + print *,' >< trkrinfo%type ',trkrinfo%type + endif + endif + + if (okay_to_call_cps_routines == 'y') then + + ! Similarly, these next two conditions (previous lat and + ! previous lon > -999) are in there in case we're doing a + ! tcgen or midlat case and this is the *first* time level + ! within a forecast that the storm has been detected (again, + ! we don't yet know the storm heading). + + call get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'lower',vtl_slope + & ,maxstorm,igcv1ret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'upper',vtu_slope + & ,maxstorm,igcv2ret) + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh) + & ,paramb,vtl_slope,vtu_slope + endif + + cps_vals(1) = paramb + cps_vals(2) = vtl_slope + cps_vals(3) = vtu_slope + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diagnostics were requested but will NOT' + print *,' >< be performed for this time level since we ' + print *,' >< are either at the first time level for a ' + print *,' >< genesis case (type = midlat or tcgen), or' + print *,' >< we are at any time level in which for some' + print *,' >< reason the fixlon and fixlat at the' + print *,' >< previous time level are not defined.' + print *,' >< ifh= ',ifh + endif + + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diags were requested but will NOT be' + print *,' >< performed for this time level since we are at' + print *,' >< time level 1 for a genesis case ' + print *,' >< (type = midlat or tcgen) and we cannot' + print *,' >< diagnose the model direction of storm' + print *,' >< movement. ifh= ',ifh + endif + + endif + + endif + + 73 format ('cps_stats: ',a4,' lead time= ',i3,':',i2,' paramb= ' + & ,f8.2,' vtl= ',f9.2,' vtu= ',f9.2) + + + if (phasescheme == 'vtt' .or. phasescheme == 'both') then + call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + + 631 format(1x,'#-------------------------------------------------#') + 633 format(1x,'# End of routine to determine cyclone phase... #') + 635 format(1x,'#-------------------------------------------------#') + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines "Parameter B", which determines +c the degree of thermal symmetry between the "left" and "right" +c hemispheres of a storm, in the layer between 900 and 600 mb. +c We evaluate only those points that are within 500 km of the +c storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zthicksum(2) + real rlonc,rlatc,rlonb,rlatb,xdist,degrees,d,cosarg + real st_heading,st_heading_rad,ricps,dx,dy + real pt_dir,pt_dir_rad,zthick,hemval,paramb + real zthick_right_mean,zthick_left_mean + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer left_ct,right_ct,hemis,icount,maxstorm,ip + logical(1) valid_pt(imax,jmax) +c + ricps = 500.0 + +c ----------------------------------------------------------------- +c First, determine the angle that the storm took getting from the +c last position to the current one. If this is for ifh=1 for a +c regular type=tracker case, we will just use the storm direction +c as read from the tcvitals card. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + if (d == 0.0) then + + ! Storm is stationary... + st_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + print '(a43,f9.3)' + & ,' In get_cps_paramb, model storm heading = ' + & ,st_heading + print *,' ' + endif + +c ----------------------------------------------------------------- +c Now call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_paramb from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + igcpret = 92 + return + endif + +c ----------------------------------------------------------------- +c Now loop through all of the points of the subdomain. If the +c point is further than 500 km from the storm center, discard it. +c Otherwise, evaluate the angle from the storm center to this point +c to determine the hemisphere of the point, that is, if the point +c is to the left or the right of the storm track. +c ----------------------------------------------------------------- + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the + ! loop for the evaluation of parameter B. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + left_ct = 0 + right_ct = 0 + zthicksum = 0 + icount = 0 + +c print *,'CPS CORE: ibeg= ',ibeg,' iend= ',iend +c print *,'CPS CORE: jbeg= ',jbeg,' jend= ',jend + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + icount = icount + 1 + +c print *,'CPS CORE: ist= ',ist,' ifh= ',ifh,' j= ',j,' i= ',i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_paramb, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Parameter B will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_paramb' + print *,'!!! for a non-global grid.' + print *,'!!! Parameter B will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_PARAMB....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon= ',glon(ip),' glat= ',glat(j) + print *,'!!! Parameter B will not be computed.' + print *,'!!! EXITING GET_CPS_PARAMB....' + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + + !---------------------------------------------------------- + ! Calculate angle from storm center to point, in a 0-360 + ! framework, clockwise positive. + !---------------------------------------------------------- + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-fixlon(ist,ifh)) * dtr + rlatb = fixlat(ist,ifh) * dtr + d = degrees * dtr + + if (d > 0.) then + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_dir_rad = acos(cosarg) + else + pt_dir_rad = 2*pi - acos(cosarg) + endif + else + pt_dir_rad = 0.0 + endif + + pt_dir = pt_dir_rad / dtr + + !------------------------------------------------------------ + ! Based on the angle that the point is from the storm center, + ! determine if the point is to the left or the right of the + ! storm track. + !------------------------------------------------------------ + + if (st_heading >= 180.0) then + if ((st_heading - pt_dir) > 0.0 .and. + & (st_heading - pt_dir) <= 180) then + hemis = 2 + left_ct = left_ct + 1 + else + hemis = 1 + right_ct = right_ct + 1 + endif + else + if ((pt_dir - st_heading) > 0.0 .and. + & (pt_dir - st_heading) <= 180) then + hemis = 1 + right_ct = right_ct + 1 + else + hemis = 2 + left_ct = left_ct + 1 + endif + endif + + !------------------------------------------------------------ + ! Calculate the 600-900 mb thickness at this point and add + ! the thickness value to the array for the correct "storm + ! hemisphere". + !------------------------------------------------------------ + + zthick = cpshgt(ip,j,7) - cpshgt(ip,j,1) + zthicksum(hemis) = zthicksum(hemis) + zthick + + if ( verb .ge. 3 ) then + write (6,51) rlonb/dtr,rlatb/dtr,rlonc/dtr,rlatc/dtr + & ,st_heading,pt_dir,hemis,zthick + endif + + enddo iloop + enddo jloop + + 51 format (1x,'stlon stlat = ',2(f6.2,2x),' ptlon ptlat = ' + & ,2(f6.2,2x),' sthead= ',f6.2,' ptdir= ',f6.2,' hemis= ' + & ,i1,' zthick= ',f7.2) + +c ------------------------------------------------------------------ +c Now calculate parameter B. The hemval parameter = +1 for storms +c in the Northern Hemisphere and -1 for Southern Hemisphere storms. +c ------------------------------------------------------------------ + + zthick_right_mean = zthicksum(1) / float(right_ct) + zthick_left_mean = zthicksum(2) / float(left_ct) + + if (fixlat(ist,ifh) < 0.0) then + hemval = -1.0 + else + hemval = 1.0 + endif + + paramb = hemval * (zthick_right_mean - zthick_left_mean) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' right_ct= ',right_ct,' left_ct= ',left_ct + print *,' zthicksum(1)= ',zthicksum(1) + print *,' zthicksum(2)= ',zthicksum(2) + print *,' zthick_right_mean= ',zthick_right_mean + print *,' zthick_left_mean= ',zthick_left_mean + print *,' hemval= ',hemval + print *,' END of get_cps_paramb, paramb= ',paramb + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,clayer,vth_slope,maxstorm,igcvret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines the thermal wind profile for +c either the lower troposphere (i.e., between 600 and 900 mb) or the +c upper troposphere (i.e., between 300 and 600 mb). We evaluate +c only those points that are within 500 km of the storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character clayer*5 + real tmp1,tmp2,tmp3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zmax(7),zmin(7),zdiff(7),xlolevs(7),xhilevs(7),plev(7) + real dlnp(7),dzdlnp(7),dz(7),lnp(7) + real vth_slope,xdist,degrees,d,cosarg + real ricps,dx,dy,R2 + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j,k,kix + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igcvret,igiret + integer kbeg,kend,maxstorm,ip + logical(1) valid_pt(imax,jmax) + + data xlolevs /900.,850.,800.,750.,700.,650.,600./ + data xhilevs /600.,550.,500.,450.,400.,350.,300./ +c data xlolevs /90000.,85000.,80000.,75000.,70000.,65000.,60000./ +c data xhilevs /60000.,55000.,50000.,45000.,40000.,35000.,30000./ +c + ricps = 500.0 + plev = 0.0 + + if (clayer == 'lower') then + kbeg = 1 + kend = 7 + plev = xlolevs + else + kbeg = 7 + kend = 13 + plev = xhilevs + endif + +c ----------------------------------------------------------------- +c First, call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_vtl from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + igcvret = 92 + return + endif + +c ------------------------------------------------------------------ +c Now loop through all of the points of the subdomain at each level. +c If a point is further than 500 km from the storm center, discard +c it. Otherwise, evaluate the gp height at the point to determine +c if it is a max or a min for the given level. Store the max and +c min height at each level in an array. +c ------------------------------------------------------------------ + +c ! We will want to speed things up for finer resolution grids. +c ! We can do this by skipping some of the points in the +c ! loop for the evaluation of parameter B. +c +c if ((dx+dy)/2. > 0.20) then +c bskip = 1 +c else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then +c bskip = 2 +c else if ((dx+dy)/2. <= 0.10) then +c bskip = 3 +c endif + + bskip = 1 ! Don't do any skipping for now.... + + zmax = -9999999.0 + zmin = 9999999.0 + zdiff = 0.0 + lnp = 0.0 + + levloop: do k = kbeg,kend + + if (kbeg == 7) then + ! processing upper layers (600-300 mb) + kix = k - 6 + else + ! processing lower layers (900-600 mb) + kix = k + endif + + lnp(kix) = log(plev(kix)) + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_vth, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_vth' + print *,'!!! for a non-global grid.' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_VTH....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j,' k= ',k + & ,' clayer= ',clayer + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon(ip)= ',glon(ip),' glat= ',glat(j) + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! EXITING GET_CPS_VTH....' + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + + tmp1 = zmax(kix) + tmp2 = cpshgt(ip,j,k) + tmp3 = zmin(kix) + + zmax(kix) = max(tmp1,tmp2) + zmin(kix) = min(tmp3,tmp2) + +c zmax(kix) = max(zmax(kix),cpshgt(ip,j,k)) +c zmin(kix) = min(zmin(kix),cpshgt(ip,j,k)) + + enddo iloop + enddo jloop + + zdiff(kix) = zmax(kix) - zmin(kix) + + enddo levloop + +c ------------------------------------------------------------------ +c Now calculate the vertical derivative of the gp height, that is, +c d(dz)/d(ln(p)). Here, zdiff is the gp height perturbation at a +c given level, calculated in the loop above; dz is the vertical +c change in that perturbation from one level to the next. +c ------------------------------------------------------------------ + + dz = 0.0 + dlnp = 0.0 + dzdlnp = 0.0 + + do k = 2,7 + dz(k) = zdiff(k) - zdiff(k-1) + dlnp(k) = log(plev(k)) - log(plev(k-1)) + dzdlnp(k) = dz(k) / dlnp(k) + enddo + +c ------------------------------------------------------------------ +c Now call a correlation routine to get the slope of a regression +c line. The independent variable that we input is dlnp, the change +c in log of pressure with height. The dependent variable is +c dzdlnp, the vertical change in the height perturbation with +c respect to the change in pressure. The slope that is returned +c defines whether we've got a cold core or warm core system. +c See Hart (MWR, April 2003, Vol 131, pp. 585-616) for more +c details, specifically his Fig. 3 and the discussion surrounding. +c Note that in the call to calccorr, we are sending only 6 of the +c 7 elements of the dlnp and dzdlnp arrays, beginning with the +c 2nd element of each. That's because the first array value for +c each of those arrays is empty, since in the loop just above, we +c start with kbeg+1, not kbeg. +c ------------------------------------------------------------------ + + call calccorr(lnp(2),zdiff(2),6,R2,vth_slope) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ In get_cps_vth, values for vth follow for ' + & ,'lead time= ',ifhours(ifh),':',ifclockmins(ifh),' ' + & ,storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' ... clayer = ',clayer + print *,' ' + endif + + do k = kbeg,kend + + if (kbeg == 7) then + kix = k - 6 + else + kix = k + endif + + if ( verb .ge. 3 ) then + print *,' ' + write (6,31) k,plev(kix),zmax(kix),zmin(kix),zdiff(kix) + if (kix > 1) then + write (6,32) plev(kix),log(plev(kix)) + & ,plev(kix-1),log(plev(kix-1)) + write (6,33) dz(kix),dlnp(kix),dzdlnp(kix) + else + write (6,34) + endif + endif + + enddo + + 31 format (1x,' +++ k= ',i2,' press= ',f8.1,' zmax= ',f7.2 + & ,' zmin= ',f7.2,' zdiff= ',f7.2) + 32 format (1x,' ln(',f7.1,')= ',f9.6,' ln(',f7.1,')= ',f9.6) + 33 format (1x,' dz= ',f10.2,' dlnp= ',f13.6,' dzdlnp= ',f12.3) + 34 format (1x,' --- First level... no derivatives done...') +c + return + end +c +C---------------------------------------------------- +C +C---------------------------------------------------- + subroutine calccorr(xdat,ydat,numpts,R2,slope) +c +c This subroutine is the main driver for a series of +c other subroutines below this that will calculate the +c correlation between two input arrays, xdat and ydat. +c +c INPUT: +c xdat array of x (independent) data points +c ydat array of y (dependent) data points +c numpts number of elements in each of xdat and ydat +c +c OUTPUT: +c R2 R-squared, the coefficient of determination +c slope Slope of regression line +c +c xdiff array of points for xdat - xmean +c ydiff array of points for ydat - ymean +c yestim array of regression-estimated points +c yresid array of residuals (ydat(i) - yestim(i)) + + USE verbose_output + + implicit none + + real xdat(numpts),ydat(numpts) + real xdiff(numpts),ydiff(numpts) + real yestim(numpts),yresid(numpts) + real xmean,ymean,slope,yint,R2 + integer numpts,i + +c + call getmean(xdat,numpts,xmean) + call getmean(ydat,numpts,ymean) +c + call getdiff(xdat,numpts,xmean,xdiff) + call getdiff(ydat,numpts,ymean,ydiff) +c + call getslope(xdiff,ydiff,numpts,slope) + yint = ymean - slope * xmean +c + call getyestim(xdat,slope,yint,numpts,yestim) + call getresid(ydat,yestim,numpts,yresid) +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * CPS Thermal wind regression details * ' + print *,' *--------------------------------------------------* ' + endif + + call getcorr(yresid,ydiff,numpts,R2) + + if ( verb .ge. 3 ) then + print *,' i ydat xdat ydiff xdiff e' + & ,' e2 ydiff2' + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + do i = 1,numpts + write(6,'(2x,i3,2x,f7.2,2x,f7.4,2x,f7.2,2x,f7.4,3(2x,f7.2))') + & i,ydat(i),xdat(i),ydiff(i) + & ,xdiff(i),yresid(i),yresid(i)*yresid(i) + & ,ydiff(i)*ydiff(i) + enddo + + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + print *,' ' + write (6,'(1x,a13,f9.3,3x,a5,f7.2)') ' means: y: ',ymean + & ,' x: ',xmean + + write (6,*) ' ' + write (6,30) 'slope= ',slope,' y-intercept = ',yint + 30 format (2x,a7,f10.3,a23,f10.3) + if (slope .gt. 0.0) then + write(6,40) 'Regression equation: Y = ',yint,' + ',slope + else + write(6,40) 'Regression equation: Y = ',yint,' - ' + & ,abs(slope) + endif + 40 format (2x,a27,f8.2,a3,f8.2,'X') +c + print *,' ' + write (6,'(1x,a17,f7.4,5x,a7,f7.4)') ' R2(r_squared) = ',R2 + & ,' r = ',sqrt(R2) + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * End of regression details * ' + print *,' *--------------------------------------------------* ' + endif + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getmean(xarr,inum,zmean) +c +c This subroutine is part of the correlation calculation, +c and it simply returns the mean of the input array, xarr. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c +c OUTPUT: +c zmean mean of data values in xarr + + implicit none + + real xarr(inum) + real xsum,zmean + integer i,inum +c + xsum = 0.0 + do i = 1,inum + xsum = xsum + xarr(i) + enddo +c + zmean = xsum / float(MAX(inum,1)) +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getdiff(xarr,inum,zmean,zdiff) +c +c This subroutine is part of the correlation calculation, +c and it returns in the array zdiff the difference values +c between each member of the input array xarr and the +c mean value, zmean. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c zmean mean of input array (xarr) +c +c OUTPUT: +c zdiff array containing xarr(i) - zmean + + implicit none + + real xarr(inum),zdiff(inum) + real zmean + integer i,inum +c + do i = 1,inum + zdiff(i) = xarr(i) - zmean + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + + subroutine getslope(xarr,yarr,inum,slope) +c +c This subroutine is part of the correlation calculation, +c and it returns the slope of the regression line. +c +c INPUT: +c xarr input array of xdiffs (x - xmean) +c yarr input array of ydiffs (y - ymean) +c inum number of points in x & y arrays +c +c OUTPUT: +c slope slope of regression line + + real xarr(inum),yarr(inum) + real slope,sumxy,sumx2 + integer i,inum + +c First sum up the xarr*yarr products.... + + sumxy = 0.0 + do i = 1,inum + sumxy = sumxy + xarr(i) * yarr(i) + enddo + +c Now sum up the x-squared terms.... + + sumx2 = 0.0 + do i = 1,inum + sumx2 = sumx2 + xarr(i) * xarr(i) + enddo + +c Now get the slope.... + + slope = sumxy / sumx2 + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getyestim(xarr,slope,yint,inum,yestim) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the predicted y-values using the +c regression equation that has been calculated. +c +c INPUT: +c xarr array of x data points +c slope slope of the calculated regression line +c yint y-intercept of the calculated regression line +c inum number of input points +c +c OUTPUT: +c yestim array of y pts estimated from regression eqn. + + implicit none + + real xarr(inum),yestim(inum) + real slope,yint + integer i,inum +c + do i = 1,inum + yestim(i) = yint + xarr(i) * slope + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getresid(yarr,yestim,inum,yresid) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the residual values between the +c input y data points and the y-estim predicted y values. +c +c INPUT: +c yarr array of y data points +c yestim array of y pts estimated from regression eqn. +c inum number of input points +c +c OUTPUT: +c yresid array of residuals (ydat(i) - yestim(i)) + + implicit none + + real yarr(inum),yestim(inum),yresid(inum) + integer i,inum +c + do i = 1,inum + yresid(i) = yarr(i) - yestim(i) + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getcorr(yresid,ydiff,inum,R2) +c +c This subroutine is part of the correlation calculation, +c and it does the actual correlation calculation. +c +c INPUT: +c yresid array of residuals (ydat(i) - yestim(i)) +c ydiff array of points for ydat - ymean +c inum number of points in the arrays +c +c OUTPUT: +c R2 R-squared, the coefficient of determination + + USE verbose_output + + implicit none + + real yresid(inum),ydiff(inum) + real R2,sumyresid,sumydiff + integer i,inum +c + sumyresid = 0.0 + sumydiff = 0.0 + + do i = 1,inum + sumyresid = sumyresid + yresid(i) * yresid(i) + sumydiff = sumydiff + ydiff(i) * ydiff(i) + enddo + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,30) 'Sum of y-residuals squared (e2) = ',sumyresid + write (6,30) 'Sum of y-diffs squared (ydiff2) = ',sumydiff + write (6,*) ' ' + 30 format (1x,a35,f15.2) + endif + +c if (sumydiff == 0.0) then +c R2=1.0 +c else +c R2 = 1 - sumyresid / sumydiff +c endif +c PENG 05/14/2018 Bug-fixed for R2 calculation with FENS job crashed. + if (sumyresid .lt. sumydiff) then + if (sumydiff .le. 0.000001) then + R2 = 1.0 + else + R2 = 1 - sumyresid / sumydiff + endif + else + R2=0.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. Here, we are only looking +c at the mid-to-upper tropospheric warm anomaly at the center of +c the storm. The temperature data that we are searching through in +c the tmean array should be the 300-500 mb mean temperature data. +c The criteria in this algorithm are based loosely on Vitart's +c criteria for warm core checking, but the nuts & bolts of the +c subroutine use algorithms from this tracker, including the barnes +c analysis. First, we locate the warm core with the find_maxmin +c routine. Then we use the check_closed_contour routine to see if +c there is a closed temperature contour surrounding the warm core. +c +c INPUT: +c inp +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c inp contains input date and model number information +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c ist integer storm number (internal to the tracker) +c ifh integer index for lead time +c trkrinfo derived type containing grid info on user boundaries +c fixlon array containing found fix longitudes +c fixlat array containing found fix latitudes +c valid_pt Logical; bitmap indicating if valid data at that pt. +c maxstorm maximum # of storms to be handled +c +c OUTPUT: +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c igvpret Return code for this subroutine. +c +c LOCAL: +c wcore_mean_val barnes-averaged value of the temperature at the +c location where the tracker found the warm core. +c wcore_point_max max temperature found at a gridpoint near the +c location where the tracker found the warm core using +c barnes analysis. + + USE set_max_parms; USE grid_bounds; USE trkrparms; USE contours + USE tracked_parms; USE gen_vitals; USE def_vitals; USE inparms + USE phase + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo,wcore_trkrinfo + type (cint_stuff) wcore_contour_info + type (datecard) inp + + character*1 get_last_contour_flag,wcore_flag + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dx,dy,wcore_mean_val,wcore_mean_lon,wcore_mean_lat + real wcore_point_max,tlastcont,rlastcont,tlastout,rlastout + integer imax,jmax,igvpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer icount,maxstorm,ip,ifmret,ifilret,ifix,jfix,icccret + integer num_check_conts + logical(1) valid_pt(imax,jmax),compflag,wcore_mask(imax,jmax) + logical(1) output_file_open +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of get_vtt_phase *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for warm core at hour ',i4,':',i2.2) + write (6,103) wcore_depth + 103 format (1x,'* Warm core depth threshold (wcore_depth) = ',f7.2) + print *,'*-------------------------------------------------*' + endif + +c ------------------------------------------------------------ + wcore_mask = .false. + wcore_mean_lon = -999.0 + wcore_mean_lat = -999.0 + wcore_trkrinfo = trkrinfo ! set equal to values from trkrinfo... + wcore_trkrinfo%contint = wcore_depth ! ...except use the warm + ! core contour interval specified by + ! the user in the extrkr.sh script. + +c ------------------------------------------------------------ +c First, call find_maxmin to locate the warm core + + call find_maxmin (imax,jmax,dx,dy,'tmp' + & ,tmean,'max',ist,fixlon(ist,ifh),fixlat(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,compflag + & ,wcore_mean_lon,wcore_mean_lat,wcore_mean_val + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + + if (verb .ge. 3) then + print *,' ' + print *,'After call to find_maxmin for wcore, ifmret= ',ifmret + print *,' wcore_mean_val= ',wcore_mean_val + endif + +c ------------------------------------------------------------ +c Once find_maxmin returns a value and a location for the +c barnes-averaged value of a warm core, then make a call to +c fix_latlon_to_ij to (1) get the actual gridpoint value of the +c temperature (the value stored in wcore_mean_val is an +c area-averaged value coming from the barnes analysis), and +c (2) to get the (i,j) indeces for this gridpoint to be used in +c the call to check_closed_contour below. + + if (wcore_mean_lat > -99.0 .and. wcore_mean_lon > -990.0) then + call fix_latlon_to_ij (imax,jmax,dx,dy,tmean,'max' + & ,valid_pt,wcore_mean_lon,wcore_mean_lat + & ,wcore_mean_val,ifix,jfix,wcore_point_max,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Warm core stats: ' + write (6,105) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_mean_lon,360.-wcore_mean_lon + & ,wcore_mean_lat,wcore_mean_val + write (6,106) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,ifix,jfix,wcore_point_max + endif + + else + ! Search went out of regional grid bounds.... + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN get_vtt_phase. The call to ' + print *,'!!! fix_latlon_to_ij returned a non-zero return ' + print *,'!!! code, which means that the search for the fix' + print *,'!!! i and j went out of bounds for a regional ' + print *,'!!! grid. This should have been caught in a ' + print *,'!!! previous call to find_maxmin for one of the ' + print *,'!!! various fix parms. In any event, we will not' + print *,'!!! search for a warm core for this storm and ' + print *,'!!! lead time.' + print *,' ' + write (6,115) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,'U',-999.99,-9999.99 + endif + + igvpret = 95 + wcore_flag = 'u' + return + endif + endif + + 105 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' mean_lon: ',f7.2,'E' + & ,1x,'(',f7.2,'W)',2x,'mean_lat: ',f7.2,2x + & ,'wcore_mean_val(K): ',f12.3) + 106 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' ifix: ',i5,2x + & ,' jfix: ',i5,2x,'wcore_point_max(K): ',f12.3) + + +c ------------------------------------------------------------ +c The Vitart scheme specifies that the temperature must decrease +c by at least 1.0C in all directions from the warm core center +c within a distance of 8 deg. A rigorous check of this criterion +c is performed here by utilizing the check_closed_contour routine. +c If we have a closed contour in the temperature field +c surrounding the warm core (using a 1 deg K interval), that +c criterion is satisfied. For diagnostic purposes, we set the +c value of num_check_conts to 999 in order to keep searching for +c all contours surrounding the warm core, and this allows us to +c get an idea of the "depth" or magnitude of the warm core when +c the tlastcont and rlastcont values are returned. + + wcore_contour_info%numcont = maxconts + num_check_conts = 999 + + get_last_contour_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,tmean + & ,valid_pt,wcore_mask,wcore_flag,'max',wcore_trkrinfo + & ,num_check_conts,wcore_contour_info,get_last_contour_flag + & ,tlastcont,rlastcont,icccret) + + if (wcore_flag == 'y') then + tlastout = tlastcont + rlastout = rlastcont/0.539638 + else + tlastout = -999.0 + rlastout = -9999.0 + endif + + if ( verb .ge. 3 ) then + write (6,115) storm(ist)%tcv_storm_id,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_flag,tlastout,rlastout + + 115 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2 + & ,' wcore_flag= ',a1,2x,' Temp of last contour(K) = ' + & ,f7.2,2x,'Radius of last contour(km) = ',f8.2) + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_sfc_center (xmeanlon,xmeanlat,clon + & ,clat,ist,ifh,calcparm,xsfclon,xsfclat + & ,maxstorm,igscret) +c +c ABSTRACT: This subroutine computes a modified lat/lon fix position +c to use as the input center position for the subroutines that +c follow which calculate surface-wind related values. The reason +c for this is that since we are concerned with the positioning of +c low-level wind features (e.g., rmax), we want the center position +c to be based solely on low-level features. We'll use mslp and the +c min in the sfc wind speed. If a center fix was unable to be made +c at this forecast hour for mslp and low-level winds, then we will +c stick with just using the mean position we got using all the other +c parameters. +c +c INPUT: +c xmeanlon The mean center longitude computed from all the various +c parameter fixes found in array clon +c xmeanlat The mean center latitude computed from all the various +c parameter fixes found in array clat +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Index for storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c (if a parameter fix could not be made at this forecast +c hour, then calcparm is set to false for this time for +c that parameter). +c maxstorm Maximum number of storms that can be tracked +c +c OUTPUT: +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c igscret Return code from this subroutine + + USE set_max_parms + USE verbose_output + + implicit none + + integer ist,ifh,ipct,igscret,maxstorm + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmeanlon,xmeanlat + real xsfclon,xsfclat,xlonsum,xlatsum + logical(1) calcparm(maxtp,maxstorm) + + ipct = 0 + xlonsum = 0.0 + xlatsum = 0.0 + + ! Do NOT include MSLP for the surface center at this time. +c if (calcparm(9,ist)) then +c ipct = ipct + 1 +c xlonsum = xlonsum + clon(ist,ifh,9) +c xlatsum = xlatsum + clat(ist,ifh,9) +c endif + + if (calcparm(10,ist)) then +c ! NOTE: Put double weighting on surface wind center if +c ! the tracker was able to find a center for it.... +c ipct = ipct + 2 +c xlonsum = xlonsum + 2.*clon(ist,ifh,10) +c xlatsum = xlatsum + 2.*clat(ist,ifh,10) + ! Just use single weighting for the sfc wcirc fix + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,10) + xlatsum = xlatsum + clat(ist,ifh,10) + endif + + if (calcparm(11,ist)) then + ! This is for the sfc vorticity center.... + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,11) + xlatsum = xlatsum + clat(ist,ifh,11) + endif + + if (ipct > 0) then + xsfclon = xlonsum / float(ipct) + xsfclat = xlatsum / float(ipct) + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In get_fract_wind_cov, CANNOT get modified fix ' + print *,'!!! position because the parameter fixes for mslp' + print *,'!!! and the sfc winds could not be obtained at this' + print *,'!!! forecast hour. ist= ',ist,' ifh= ',ifh + print *,'!!! We will use the fixlon and fixlat values for' + print *,'!!! this forecast hour.' + endif + + xsfclon = xmeanlon + xsfclat = xmeanlat + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ In get_sfc_center, modified fix (mslp + sfc_winds)' + print *,'+++ position follows: ' + print *,'+++ ' + print *,'+++ mslp: lon: ',clon(ist,ifh,9),' lat: ' + & ,clat(ist,ifh,9) + print *,'+++ sfc_winds: lon: ',clon(ist,ifh,10),' lat: ' + & ,clat(ist,ifh,10) + print *,'+++ sfc_vorticity: lon: ',clon(ist,ifh,11),' lat: ' + & ,clat(ist,ifh,11) + print *,'+++ multi-parm mean: lon: ',xmeanlon,' lat: ' + & ,xmeanlat + print *,'+++ sfc-only mean: lon: ',xsfclon,' lat: ',xsfclat + endif + + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,er_wind,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm + & ,trkrinfo,igwsret) +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure of the low level winds of a cyclone. +c The algorithm will search out at specified distances from the +c storm center along arcs in each quadrant of the storm, +c evaluating the winds every 15 degrees along the arc. In each +c arc, start 7.5 degrees in, then make stops at 22.5, 37.5, +c 52.5, 67.5, and 82.5 degrees. At each of those points, we +c will bilinearly interpolate the winds to the points along those +c arcs. Then we compute a quadrant average of the wing magnitude, +c as well as the mean Vt and Vr values. This will be done +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 earth-relative +c quadrants: NE, SE, SW and NW. For the storm-relative estimates, +c these mean values of the wind will be computed for the same +c relative quadrants (front-right, back-right, back-left, front- +c left, but with respect (positive clockwise) to the +c direction of storm motion. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated +c er_wind: Quadrant winds in earth-relative framework +c sr_wind: Quadrant winds in storm-relative framework +c er_vr: Quadrant radial winds in earth-relative framework +c sr_vr: Quadrant radial winds in storm-relative framework +c er_vt: Quadrant tangential winds in earth-relative framework +c sr_vt: Quadrant tangential winds in storm-relative framework + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,num_qtr_azim=6 + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igvtret,ipct,maxstorm,iazim,azimuth_ct + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,xsfclon,xsfclat,wmag,wmag_sum + real vr,vt,vr_sum,vt_sum + logical(1) valid_pt(imax,jmax) +c + data rdist/10.,25.,50.,75.,100.,125.,150.,200.,250.,300.,350. + & ,400.,450.,500./ + + igwsret = 0 + + er_wind = 0.0 + sr_wind = 0.0 + er_vr = 0.0 + er_vt = 0.0 + sr_vr = 0.0 + sr_vt = 0.0 + +c ----------------------------------------------------------------- +c Now determine the angle that the storm took getting from the +c last position to the current one. If this is the initial time, +c use the observed direction of motion from the TC Vitals. This +c may not match up with the model storm's initial direction of +c motion, but it is all we have available to us in order to get +c a heading estimate for the initial time. This storm heading +c information will be used for the storm-relative profiles. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_wind_structure, fhr= ',fhreal(ifh) + & ,' ',storm(ist)%tcv_storm_id + & ,' ',storm(ist)%tcv_storm_name + print '(a25,a23,f9.3)',' In get_wind_structure, ' + & ,' model storm heading = ',st_heading + print *,' ' + endif + + endif + +c ----------------------------------------------------------------- +c Get the profiles for the earth-relative coordinate system. +c Start with NE, then SE, SW, and NW. First go through +c radiusloop, which goes from one radial distance to the next, +c then do the quadloop, which goes through each quadrant, and +c then within each quadrant, the qtr_azimloop goes through for +c six points along an arc, spaced 15 degrees apart, starting at +c 7.5 degrees clockwise from the north. +c ----------------------------------------------------------------- + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *****************************************************' + print *,' Wind Structure: distbear bilin interp starts here.' + print *,' *****************************************************' + print *,' ' + endif + + radiusloop1: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- ER structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop1: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + ! In each quadrant, run through six points along an + ! arc and evaluate the winds. + + qtr_azimloop1: do iazim = 1,num_qtr_azim + + bear = ((iquad-1) * 90.) + ((iazim-1) * 15.) + 7.5 + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' earth-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,' ' + print '(5(a10,f7.2))',' sfclat= ',xsfclat + & ,' sfclon= ',xsfclon + & ,' rdist= ',rdist(idist),' targlat= ',targlat + & ,' targlon= ',targlon + print '(19x,a8,f7.2,35x,a9,f7.2)','sfclon= ',360.-xsfclon + & ,'targlon= ',360.-targlon + endif + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop1 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + er_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + er_vr(iquad,idist) = vr_sum / float(azimuth_ct) + er_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + er_wind(iquad,idist) = -999.0 + er_vr(iquad,idist) = -999.0 + er_vt(iquad,idist) = -999.0 + endif + + enddo quadloop1 + + enddo radiusloop1 + +c ----------------------------------------------------------------- +c Get the profiles for the storm-relative coordinate system. +c Start with the front-right quadrant and go clockwise through +c back-right, back-left and front-left. +c ----------------------------------------------------------------- + + radiusloop2: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- SR structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop2: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + qtr_azimloop2: do iazim = 1,num_qtr_azim + +c temp_bear = st_heading + ((iquad-1) * 90.) + 45. + + temp_bear = st_heading + ((iquad-1) * 90.) + & + ((iazim-1) * 15.) + 7.5 + bear = mod(temp_bear,360.) + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' storm-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop2 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + sr_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + sr_vr(iquad,idist) = vr_sum / float(azimuth_ct) + sr_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + sr_wind(iquad,idist) = -999.0 + sr_vr(iquad,idist) = -999.0 + sr_vt(iquad,idist) = -999.0 + endif + + enddo quadloop2 + + enddo radiusloop2 +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,calcparm,wfract_cov,pdf_ct_bin,pdf_ct_tot,maxstorm + & ,trkrinfo,igfwret) +c +c ABSTRACT: This subroutine determines the fractional areal coverage +c of winds exceeding various thresholds within specified arcs +c (e.g., 200 km, 400 km, etc) in each quadrant of a storm. The bins +c that are used go as follows: (1) 0-100; (2) 0-200; (3) 0-300; +c (4) 0-400; (5) 0-500. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real wfract_cov(numquad+1,numbin,numthresh) + real area_total_quad_bin(numquad,numbin) + real area_exceed_quad_bin(numquad,numbin,numthresh) + real xintlon,xintlat + real :: windthresh(numthresh) = (/17.5,25.74,32.94/) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,conv_ms_knots,vmagkts + real rads,ri,dell,vmag,xarea,grdintincr,xsfclon,xsfclat + real sum_exceed_area(numbin,numthresh) + real sum_total_area(numbin,numthresh) + integer pdf_ct_bin(16) + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igfwret,ipct,i,j,numinterp,ixoa,ixaa,iq,ib,it,ii + integer jlatfix,ilonfix,npts,ibeg,iend,jbeg,jend,ngridint,ni,nj + integer itret,igiret,idistbin,ipdfbin,pdf_ct_tot,maxstorm + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) + character got_pdf*6 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*5 :: cbin(5) = + & (/'0-100','0-200','0-300','0-400','0-500'/) + character*2 :: cthresh(3) = (/'34','50','64'/) +c + igfwret = 0 + conv_ms_knots = 1.9427 + rads = 500.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + + wfract_cov = 0.0 + area_total_quad_bin = 0.0 + area_exceed_quad_bin = 0.0 + sum_exceed_area = 0.0 + sum_total_area = 0.0 + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_fract_wind_cov from call to ' + print *,'!!! get_ij_bounds, stopping processing for storm' + print *,'!!! number ',ist + endif + + igfwret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_fract_wind_cov calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igfwret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + +c When evaluating the winds at a gridpoint, keep in mind that each +c gridpoint represents area around it. There are 2 special cases +c we need to watch out for. The first is for cases in which the +c area of a gridpoint straddles across a distance threshold, so +c that some of the gridpoint's area is in the "<200" bin, while +c some is in the "<100" bin. The other is for the case in which +c the area of a gridpoint straddles between 2 adjacent quadrants +c (e.g., a gridpoint exactly to the north of the center would have +c half its area in the NW quadrant and half in the NE quadrant). +c +c To properly "partition" and assign gridpoint areas, we need to +c interpolate the current grid down to a fine resolution. +c +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the guidelines that +c will be used, keeping in mind that we want the final grid spacing +c to be on the order of between 0.05 and 0.10 degree (finer than +c 0.05 deg is superfluous, and coarser than 0.10 deg is too coarse). +c +c Original grid size (deg) # of interps +c ------------------------- ------------ +c 0.8 <= g 4 +c 0.4 <= g < 0.8 3 +c 0.2 <= g < 0.4 2 +c 0.1 <= g < 0.2 1 +c g < 0.1 0 + + + if ((dx+dy)/2. >= 0.8) then + numinterp = 4 + else if ((dx+dy)/2. < 0.8 .and. (dx+dy)/2. >= 0.4) then + numinterp = 3 + else if ((dx+dy)/2. < 0.4 .and. (dx+dy)/2. >= 0.2) then + numinterp = 2 + else if ((dx+dy)/2. < 0.2 .and. (dx+dy)/2. >= 0.1) then + numinterp = 1 + else + numinterp = 0 + endif + + grdintincr = (dx+dy)/2. + do i = 1,numinterp + grdintincr = 0.5 * grdintincr + enddo + +c Now loop through the points in this subdomain, determine if any +c are within 500 km of the center, and then determine what quadrant +c the point is in relative to the center, and then calculate the +c fractional area coverage for winds. + + pdf_ct_tot = 0 + pdf_ct_bin = 0 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_fract_wind_cov, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_fract_wind_cov' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle iloop ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > (rads+(0.75*((dx+dy)/2.)*dtk*cos(glat(j)*dtr)))) + & then + + ! If the distance is greater than "rads" (500 km at initial + ! writing) plus another 3/4 of a gridpoint, then cycle. + ! The extra 3/4 of a gridpoint is to allow for the case of + ! some portion of the area around a gridpoint (whose + ! center point > 500 km) being within the 500 km arc... + ! although that is only factored in for grids with spacing + ! >= 0.1 deg. For smaller grids, where no interpolation is + ! done in this subroutine, then the distance to that point + ! is considered representative and the point is ignored if + ! it is not less than 500 km from the center. + + cycle iloop + + else + + ! First interpolate the area surrounding each grid point to + ! get fine resolution of lats & lons for determining how to + ! partition the area of a gridpoint among quadrants as well + ! as among distance thresholds. + + vmag = sqrt (u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + vmagkts = vmag * conv_ms_knots + + if (numinterp > 0) then + + grdintincr = ((dx+dy)/2.) / 2**numinterp ! "grid spacing" + ! of interpolated grid + ngridint = (2**numinterp) / 2 + + got_pdf = 'notyet' + + njloop: do nj= ngridint,-ngridint,-1 + + xintlat = glat(j) + float(nj) * grdintincr + + niloop: do ni= -ngridint,ngridint + + xintlon = glon(ii) + float(ni) * grdintincr + + call calcdist (xintlon,xintlat,xsfclon + & ,xsfclat,xdist,degrees) + + if (xdist <= 350. .and. got_pdf == 'notyet') then + ! The got_pdf flag is needed because in these loops + ! for niloop & njloop, we are actually looking at + ! tiny areas around the same grid point. So we + ! want to make sure we only count each gridpoint + ! once. + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + got_pdf = 'got_it' + endif + + if (xdist < 500.) then + + ! Compute area of this fraction of a grid box + xarea = (grdintincr * 111195) * + & (grdintincr * 111195 + & * cos(xintlat * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Go through a loop of the bins. The purpose of + ! this is that these "bins" all go from the + ! the center out to a specified radius, they are + ! NOT 100-km wide bins. So if we are dealing with + ! a point at r = 250 km, then that falls in the + ! 0-300 km bin, but it also falls in the 0-400 and + ! 0-500 km bins as well. So we need to run through + ! this binloop multiple times to get the area data + ! into multiple bins. Here are the bins & indices: + ! 1: 0-100 km + ! 2: 0-200 km + ! 3: 0-300 km + ! 4: 0-400 km + ! 5: 0-500 km + + binloop: do ib = idistbin,numbin + + if (xintlon >= xsfclon .and. + & xintlat >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (xintlon >= xsfclon .and. + & xintlat < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop + + endif + + enddo niloop + + enddo njloop + + else + + ! In this else statement is the case for a grid whose + ! resolution is already fine enough that we don't need + ! to interpolate any further. For example, we will have + ! the H*Wind data on a 0.05 degree grid, so that's already + ! fine enough. + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat + & ,xdist,degrees) + + if (xdist <= 350.) then + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + endif + + if (xdist < 500.) then + + ! Compute area of this grid box + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Why the binloop2? See explanation above in the "if" + ! part of this if-then block, where binloop is. + + binloop2: do ib = idistbin,numbin + + if (glon(ii) >= xsfclon .and. + & glat(j) >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (glon(ii) >= xsfclon .and. + & glat(j) < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop2 + + endif + + endif + + endif + + enddo iloop + + enddo jloop + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different quadrants, bins and thresholds... +c ------------------------------------------------- + + if ( verb .ge. 3 ) then + write (6,109) ' ' + & ,' ' + & ,' ' + write (6,109) ' Quadrant Bin Wind_Thresh ' + & ,'Fract_coverage (%) Area_exceeded' + & ,' Area_total' + write (6,109) ' -------- --- ----------- ' + & ,'------------------ -------------' + & ,' ----------' + write (6,109) ' ' + & ,' ' + & ,' ' + + do iq = 1,numquad + do ib = 1,numbin + do it = 1,numthresh + wfract_cov(iq,ib,it) = area_exceed_quad_bin(iq,ib,it) / + & area_total_quad_bin(iq,ib) + write (6,117) cquad(iq),cbin(ib),cthresh(it) + & ,wfract_cov(iq,ib,it)*100.0 + & ,area_exceed_quad_bin(iq,ib,it) + & ,area_total_quad_bin(iq,ib) + enddo + enddo + enddo + endif + + + 109 format (1x,a33,a37,a16) + 117 format (5x,a2,5x,a5,7x,a2,13x,f6.2,10x,f16.1,2x,f16.1) + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different bins and thresholds, but for the +c entire "disc" of the storm, that is, summing all +c quadrants together. +c ------------------------------------------------- + + do it = 1,numthresh + do ib = 1,numbin + do iq = 1,numquad + sum_total_area(ib,it) = sum_total_area(ib,it) + & + area_total_quad_bin(iq,ib) + sum_exceed_area(ib,it) = sum_exceed_area(ib,it) + & + area_exceed_quad_bin(iq,ib,it) + enddo + wfract_cov(5,ib,it) = sum_exceed_area(ib,it) + & / sum_total_area(ib,it) + enddo + enddo + + if ( verb .ge. 3 ) then + do ib = 1,numbin + do it = 1,numthresh + write (6,117) 'TT',cbin(ib),cthresh(it) + & ,wfract_cov(5,ib,it)*100.0 + & ,sum_exceed_area(ib,it) + & ,sum_total_area(ib,it) + enddo + enddo + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_ike_stats (imax,jmax,inp,dx,dy,ist,ifh + & ,fixlon,fixlat,xsfclon,xsfclat,valid_pt,calcparm + & ,ike,sdp,wdp,maxstorm,trkrinfo,igisret) +c +c ABSTRACT: This subroutine computes the Integrated Kinetic Energy +c (IKE) and Storm Surge Damage Potential (SDP) values, based on +c Powell (BAMS, 2007). At this time, we are only computing the IKE +c values for TS threshold (17.5 m/s) and above. We are not yet +c computing wind damage potential (WDP) since, per Mark Powell +c (4/2008), he is currently re-formulating an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c sdp Storm surge damage potential + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4 + integer npts,ipct,igisret,imax,jmax,ist,ifh,ilonfix,jlatfix + integer ibeg,jbeg,iend,jend,igiret,i,j,maxstorm,ii + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real ike(max_ike_cats) + real dx,dy,degrees,rads,ri,dell,xdist,vmag,xarea + real xsfclon,xsfclat,sdp,wdp + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) +c + igisret = 0 + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + rads = 400.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_ike_stats from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for storm ' + print *,'!!! number ',ist + endif + + igisret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_ike_stats calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igisret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + +c Search a grid of points near the storm center, evaluate if the +c storm is within the "rads" distance threshold. If so, compute +c the IKE values for all applicable thresholds (10, 18, 33 m/s). + + do j = jbeg,jend + do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ike_stats, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_ike_stats' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > rads) then + cycle + else + + vmag = sqrt(u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + + if (vmag > 10.0) then + ! Add gridpoint to IKE_10. Compute area first... + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + ike(1) = ike(1) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 18.0) then + ! Add gridpoint to IKE_ts. Area already computed for 10 + ike(2) = ike(2) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 33.0) then + ! Add gridpoint to IKE_h. Area already computed for 10 + ike(3) = ike(3) + (0.5 * (vmag**2) * xarea) + endif + + endif + + enddo + enddo + + ike(1) = ike(1) * 1.e-12 ! Convert from J to TJ + ike(2) = ike(2) * 1.e-12 ! Convert from J to TJ + ike(3) = ike(3) * 1.e-12 ! Convert from J to TJ + +c Compute the storm surge damage potential (sdp) + + if (ike(2) >= 0.0) then + sdp = 0.676 + (0.43 * sqrt(ike(2))) + & - (0.0176 * ((sqrt(ike(2)) - 6.5)**2) ) + else + sdp = -99.0 + endif + +c Print out the IKE and SDP statistics... + + if ( verb .ge. 3 ) then + print *,' IKE_10 (storm energy) = ',ike(1) + print *,' IKE_TS (tropical storm) = ',ike(2) + print *,' IKE_H (hurricane) = ',ike(3) + print *,' SDP = ',sdp + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine distbear (xlatin,xlonin,dist,bear,xlatt,xlont) +c +c ABSTRACT: Given an origin at latitude, longitude=xlato,xlono, +c this subroutine will locate a target point at a distance dist in +c km or nautical miles (depends on what you use for "rad_earth..." +c below), at bearing bear (degrees clockwise from north). +c Returns latitude xlatt and longitude xlont of target point. +c +c *** NOTE *** +c This subroutine was written to handle input lats & lons as this: +c All latitudes are in degrees, north positive and south negative. +c All longitudes are in degrees, west positive and east negative. +c *** **** *** +c +c However, for the longitudes, the rest of the tracker uses all +c 0-360 longitudes. Therefore, we need to convert the input lons +c and then once again convert the lons that are returned back to +c the calling routine. +c +c NOTE-- When origin is at north or south pole, bearing is no +c longer measured from north. Instead, bearing is measured +c clockwise from the longitude opposite that specified in xlono. +c Example-- if xlato=90., xlono=80., the opposite longitude is +c -100 (100 East), and a target at bearing 30. will lie on the +c -70. (70 East) meridian. +c +c AUTHOR: The core of this subroutine was written by Albion +c Taylor, another NOAA employee, in 1981. +c + USE trig_vals + + implicit none +c + real, parameter :: rad_earth_nm = 3440.170 ! radius of earth + real, parameter :: rad_earth_km = 6372.797 ! radius of earth + real xlato,xlono,dist,bear,xlatt,xlont,xlatin,xlonin + real cdist,sdist,clato,slato,clono,slono,cbear,sbear + real z,y,x,r,xlattz,xlontz,ddist,dbear,dxlato,dxlono +c + xlato = xlatin + xlono = xlonin + +cstr print *,' ' +cstr print *,'+++ At top of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlon= ',xlono,'E ',360.-xlono +cstr & ,'W xlat=',xlato +cstr print '(a6,f7.2,a8,f7.2)','dist= ',dist,' bear= ',bear + + if (xlono > 180.) then + ! Longitude input for this subroutine must be positive west + xlono = 360. - xlono + else + ! Longitude input for this subroutine must be negative east + xlono = -1. * xlono + endif + +cstr print '(a31,a8,f8.2)','After conversion for distbear, ' +cstr & ,' xlono= ',xlono + + ddist = dist + dbear = bear + dxlato = xlato + dxlono = xlono + + cdist = cos(ddist/rad_earth_km) + sdist = sin(ddist/rad_earth_km) + clato = cos(dtr*dxlato) + slato = sin(dtr*dxlato) + +cstr print *,'cdist= ',cdist,' sdist= ',sdist,' clato= ',clato +cstr & ,' slato= ',slato + + clono = cos(dtr*dxlono) + slono = sin(dtr*dxlono) + +cstr print *,'dxlono= ',dxlono,' clono= ',clono +cstr & ,' slono= ',slono + + cbear = cos(dtr*dbear) + sbear = sin(dtr*dbear) + +cstr print *,'cbear= ',cbear,' sbear= ',sbear + + z=cdist*slato + clato*sdist*cbear + y=clato*clono*cdist + sdist*(slono*sbear - slato*clono*cbear) + x=clato*slono*cdist - sdist*(clono*sbear + slato*slono*cbear) + +cstr print *,'z= ',z,' y= ',y,' x= ',x + + r = sqrt(x**2 + y**2) + +cstr print *,'r = sqrt(x**2 + y**2) = ',r + + xlattz = atan2(z,r)/dtr + +cstr print *,'xlattz = datan2(z,r)/dtr = ',xlattz + + xlatt = xlattz + + if (r <= 0.) go to 20 + + xlontz = atan2(x,y)/dtr + +cstr print *,'xlontz = atan2(x,y)/dtr = ',xlontz + +c xlont = xlontz + + ! Return the target longitude back to the calling routine + ! as a 0-360 positive east longitude.... + + xlont = mod(360.-xlontz,360.) + +c xlont = mod(360.+xlontz,360.) + +cstr print *,' ' +cstr print *,'At end of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlont= ',xlont,'E ' +cstr ,360.-xlont,'W xlatt=',xlatt + + return + 20 xlont=0. +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_uneven (targlat,targlon,dx,dy + & ,imax,jmax,trkrinfo,level,cparm,xintrp_val,ibiret) +c +c ABSTRACT: This subroutine performs a bilinear interpolation to get +c a data value at a given lat/lon that may be anywhere within a box +c defined by the four surrouding grid points. In the diagram below, +c remember that for our grids we are using in the tracker, the +c latitude index starts at the north pole and increases southward. +c The point "X" indicates the target lat/lon location of the value +c for which we are bilinearly interpolating. The values to and ta +c below are ratios that determine how geographically close the +c target location is to the point of origin (pt.1 (i,j)) in terms +c of both longitude (to) and latitude (ta). +c +c +c pt.1 pt.2 +c (i,j) (i+1,j) +c +c +c +c X +c +c pt.4 pt.3 +c (i,j+1) (i+1,j+1) +c + + USE grid_bounds; USE tracked_parms; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + character cparm*1 + real targlat,targlon,xintrp_val,dx,dy + real to,ta,d1,d2,d3,d4,z,eastlon + integer ie,iw,jn,js,ibiret,imax,jmax,level,nlev + + ibiret = 0 + +c -------------------------------------------------------------- +c For the latitudes and longitudes surrounding our target +c lat/lon location, convert the lat/lon values into i- and +c j-indices. +c -------------------------------------------------------------- + +c Find the j-indices for the points just to the north and the +c south of targlat.... + + if (targlat >= 0.0) then + ! For a northern hemisphere storm, jn is the j-index for the + ! point just to the *NORTH* (poleward) of targlat. + jn = int((glatmax - targlat)/dy + 1.) + js = jn + 1 + else + ! For a southern hemisphere storm, js is the j-index for the + ! point just to the *SOUTH* (poleward) of targlat. + js = ceiling((glatmax - targlat)/dy + 1.) + jn = js - 1 + endif + + ! Check to make sure that points are not being requested beyond + ! the northern or southern boundaries of the grid. This is most + ! likely to happen for a smaller, regional grid. + + if (jn > jmax .or. js > jmax) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jmax exceeded in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + + if (jn < 1 .or. js < 1) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jn < 0 or js < 0 in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + +c Find the i-indices for the points just to the east and the +c west of targlon.... + + ie = int((targlon - glonmin)/dx + 2.) + iw = ie - 1 + + ! Check for GM wrapping. Check ie to see if it is between the + ! most eastward gridpoint and the GM (i.e., on a 1-deg global + ! grid (360x181), it would be if targlon was between 359.0 (i=360) + ! and the GM (i=1, not i=361)). Similarly then, if we adjust ie + ! to then be 1, then we have a problem with iw, + ! since iw = 1 - 1 = 0. + + if (ie > imax) then + if (trkrinfo%gridtype == 'global') then + ie = ie - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ie > imax in subroutine ' + print *,'!!! bilin_int_uneven for a non-global grid. ' + print *,'!!! Returning to calling routine after ' + print *,'!!! assigning missing wind value of -99.' + print *,'!!! ie= ',ie,' imax= ',imax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + + if (iw < 1) then + if (trkrinfo%gridtype == 'global') then + iw = iw + imax + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: iw < 1 in subroutine bilin_int_uneven' + print *,'!!! for a non-global grid. Returning to calling ' + print *,'!!! routine after assigning missing wind value ' + print *,'!!! of -99. iw= ',iw + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' +++ Interpolating winds for cparm= ',cparm +ctmwc print '(6x,4(a4,i3))','jn= ',jn,' js= ',js,' iw= ',iw,' ie= ',ie +ctmwc endif + +c ---------------------------------------------------------------- +c Calculate the longitude (to) and latitude (ta) location ratios. +c Check for GM wrapping, as we can run into a problem here if +c interpolating for points that are just west of the GM, since we +c would be interpolating using values of longitude just west of +c GM (say, glon(iw)=359.5) and the GM (glon(ie) = 0.0). This +c makes for an incorrect "to" ratio below, with 0-359.5 in the +c denominator. We have to account for this.... +c ---------------------------------------------------------------- + + if (glon(iw) > 300.0 .and. + & (glon(ie) < 10. .and. glon(ie) >= 0.)) then + eastlon = 360. - glon(ie) + else + eastlon = glon(ie) + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,'glat(js)= ',glat(js),' glat(jn)= ',glat(jn) +ctmwc endif + + to = (targlon - glon(iw)) / (eastlon - glon(iw)) + ta = (targlat - glat(jn)) / (glat(js) - glat(jn)) + +c -------------------------------------------------------------- +c Copy the data values at the 4 known points into simple scalar +c variables +c -------------------------------------------------------------- + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cparm == 'u') then + d1 = u(iw,jn,nlev) + d2 = u(ie,jn,nlev) + d3 = u(ie,js,nlev) + d4 = u(iw,js,nlev) + else if (cparm == 'v') then + d1 = v(iw,jn,nlev) + d2 = v(ie,jn,nlev) + d3 = v(ie,js,nlev) + d4 = v(iw,js,nlev) + else if (cparm == 'm') then + d1 = lsmask(iw,jn) + d2 = lsmask(ie,jn) + d3 = lsmask(ie,js) + d4 = lsmask(iw,js) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in bilin_int_uneven.' + print *,'!!! Input cparm not recognized.' + print *,'!!! cparm= ',cparm + print *,'!!! EXITING....' + endif + + stop 95 + endif + + z = 1.9427 + +cstr print '(2x,4(a4,f8.2))',' d1= ',d1*z,' d2= ',d2*z +cstr & ,' d3= ',d3*z,' d4= ',d4*z + +c ------------------------------------------------------------- +c Compute the interpolated value +c ------------------------------------------------------------- + + xintrp_val = (1.-to) * (1.-ta) * d1 + & + to * (1.-ta) * d2 + & + to * ta * d3 + & + (1.-to) * ta * d4 + +cstr print '(2x,2(a11,f8.2))',' xintrp= ',xintrp_val,' (in kts)= ' +cstr & ,xintrp_val*z +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine sort_storms_by_pressure (gridprs,ifh,maxstorm,sortindex + & ,issret) +c +c ABSTRACT: This subroutine sorts storms by mslp. It is called by +c subroutine tracker just before the loop for "stormloop" is done +c for all the storms at a particular forecast hour. It is only +c called for the "midlat" and "tcgen" cases. The end result of +c this sort is an array (prsindex) that contains the indeces of +c the storms, arranged from lowest pressure to highest (and note +c that the "undefined" storms have a pressure of 9999.99 mb and +c thus get sorted to the bottom of the array). The purpose of +c doing this is so that we track the most intense storms first. +c Why go to the trouble? Imagine a scenario in which we are +c tracking a complex system in which there are 2 low pressure +c centers. Let's say that one is becoming dominant and +c intensifying, while the other is weakening. Now, let's assume +c that the weakening one eventually gets absorbed into the +c stronger, more dominant low. Now we only have 1 low, but if in +c the tracker stormloop, we first process the data for the +c weakening low, we will attribute the track to that storm, and +c then when we get to the point in the loop where we are trying +c to get the track for the stronger storm, we will (erroneously) +c stop the tracking for that storm since the storm center has +c already been attributed to the weaker storm. But by using this +c subroutine, we will track the stronger storm first, and thus +c avoid this problem. +c +c NOTE: The pressures used in the sort are those obtained at the +c previous forecast hour. At forecast hour = 0, just use the +c values as they were input to this routine, since they were +c found in first_ges_center from strongest to weakest already. +c +c INPUT: +c gridprs real array of storm mslp values +c ifh integer index for the current forecast hour +c maxstorm max num of storms that can be handled in this run +c +c OUTPUT: +c sortindex contains a sorted array of indeces. The orders +c sort routine does NOT rearrange the data. Rather, it +c returns this array of sorted indeces which point to +c the correct order of data values in the data array. +c issret return code from this subroutine +c + USE set_max_parms + USE verbose_output + + real, allocatable :: iwork(:) + real gridprs(maxstorm,maxtime) + integer ifh,maxstorm + integer sortindex(maxstorm) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: prstemp(:) +c + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iva /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub sort_storms_by_pressure allocating' + print *,'!!! prstemp or iwork arrays: ' + print *,'!!! iva= ',iva,' iwa= ',iwa + endif + + STOP 94 + return + endif + + if (ifh > 1) then + +c print *,' ' +c print *,'--- Before sort, original prs values follow:' +c print *,' ' + + do ist = 1,maxstorm + prstemp(ist) = gridprs(ist,ifh-1) +c write (6,81) ist,prstemp(ist)/100.0 + enddo + + imode = 2 + sortindex = 0 + call qsort (prstemp,sortindex,maxstorm) + +ccccc call orders (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) +ccccc call orders_4byte (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Pressure-sorted storm list:' + print *,' ' + + do ist = 1,maxstorm + if (prstemp(sortindex(ist))/100.0 < 9999.0) then + write (6,82) ist,sortindex(ist) + & ,prstemp(sortindex(ist))/100.0 + endif + enddo + + 81 format (1x,'ist= ',i5,' Original (unsorted) prstemp= ',f7.2) + 82 format (1x,'ist= ',i5,' sortindex(ist)= ',i5 + & ,' prstemp= ',f7.2) + endif + + else + do ist = 1,maxstorm + sortindex(ist) = ist + enddo + endif + + deallocate (prstemp); deallocate (iwork) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getvrvt (centlon,centlat,xlon,xlat + & ,udat,vdat,vr,vt,igvtret) +c +c ABSTRACT: This subroutine takes as input a u-wind and v-wind value +c at an input (xlon,xlat) location and returns the tangential and +c radial wind components relative to the input center lat/lon +c position (centlon,centlat). The only trick to this whole +c subroutine is figuring out the angle from the center point to the +c data point, and we do this by creating a triangle with the leg +c from the center point to the data point being the hypotenuse. +c +c NOTE: All longitudes must be in positive degrees east (0-360) !!! +c +c INPUT: +c centlon Longitude of center point +c centlat Latitude of center point +c xlon Longitude of pt at which vr & vt will be computed +c xlat Latitude of pt at which vr & vt will be computed +c udat u-value of wind at the point (xlon,xlat) +c vdat v-value of wind at the point (xlon,xlat) +c +c OUTPUT: +c vr Radial wind component at (xlon,xlat) wrt (centlon,centlat) +c vt Tang wind component at (xlon,xlat) wrt (centlon,centlat) +c igvtret Return code from this subroutine +c + USE trig_vals + USE verbose_output + + implicit none + + real centlon,centlat,xlon,xlat,udat,vdat,vr,vt,degrees,tmpxlon + real angle,xlondiff,xlatdiff,opp_dist,hyp_dist,sin_value + real cos_value,adj_dist,tmpangle,sin_angle,cos_angle + real uvrcomp,vvrcomp,uvtcomp,vvtcomp + integer igvtret +c + call calcdist(centlon,centlat,xlon,xlat,hyp_dist,degrees) + +c xxxx + + tmpxlon = xlon + + if (centlon > 330.0) then + + if (xlon > 360.0) then + + tmpxlon = xlon ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (xlon < 30.0) then + + tmpxlon = xlon + 360. ! In this case, the fix center is just + ! to the west of the GM with a lon (centlon) + ! > 330, while the point being evaluated + ! (xlon) is just east of the GM, but with a + ! lon (centlon) < 30. Need to adjust here to + ! to get the xlon in the 330+ frame of + ! reference. + + endif + + elseif (centlon >= 0 .and. centlon < 30.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = 360. - xlon + + endif + + elseif (centlon < 0.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = -1 * (360. - xlon) + + endif + + endif + + xlatdiff = abs(centlat - xlat) + xlondiff = abs(centlon - tmpxlon) + + if (centlon > 355.0) then + write (6,91) centlon,tmpxlon,hyp_dist,degrees,xlondiff + 91 format (1x,'centlon= ',f8.3,' tmpxlon= ',f8.3,' hyp_dist= ' + & ,f10.2,' degrees= ',f10.2,' xlondiff= ',f12.2) + endif + + if (xlondiff == 0 .and. xlatdiff > 0) then + + if (centlat > xlat) angle = 180 ! pt directly south of ctr + if (centlat < xlat) angle = 0 ! pt directly north of ctr + + else if (xlondiff > 0 .and. xlatdiff == 0) then + + if (centlon > tmpxlon) angle = 270 ! pt directly west of ctr + if (centlon < tmpxlon) angle = 90 ! pt directly east of ctr + + else + + ! This next part figures out the angle from the center point + ! (centlon,centlat) to the data point (tmpxlon,xlat). It does + ! this by setting up a triangle and then using inverse trig + ! functions to get the angle. Since this is a kludgy way to + ! do it that doesn't account for the curvature of the earth, + ! we'll do it 2 ways, using asin and then acos, then take the + ! average of those 2 for the angle. hyp_dist, calculated just + ! above, is the distance from the center pt to the data pt. + + opp_dist = xlatdiff/360. * ecircum + sin_value = opp_dist / hyp_dist + if (sin_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, sin_value > 1, setting to 1.' + print *,'!!! opp_dist= ',opp_dist,' hyp_dist= ',hyp_dist + print *,'!!! sin_value = ',sin_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + sin_value = 0.99999 + endif + sin_angle = asin(sin_value) / dtr + + call calcdist(centlon,centlat,tmpxlon,centlat,adj_dist,degrees) + cos_value = adj_dist / hyp_dist + if (cos_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, cos_value > 1, setting to 1.' + print *,'!!! adj_dist= ',adj_dist,' hyp_dist= ',hyp_dist + print *,'!!! cos_value = ',cos_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + cos_value = 0.99999 + endif + cos_angle = acos(cos_value) / dtr + + tmpangle = 0.5 * (sin_angle + cos_angle) + + ! The previous lines of code just calculated an angle between + ! 0 and 90. This next if structure adjusts that angle to + ! instead be between 0 and 360. + + if (centlat <= xlat .and. centlon <= tmpxlon) then + angle = 90 - tmpangle + else if (centlat > xlat .and. centlon <= tmpxlon) then + angle = 90 + tmpangle + else if (centlat >= xlat .and. centlon >= tmpxlon) then + angle = 270 - tmpangle + else if (centlat < xlat .and. centlon >= tmpxlon) then + angle = 270 + tmpangle + endif + + endif + + uvrcomp = udat * sin(angle * dtr) + vvrcomp = vdat * cos(angle * dtr) + vr = uvrcomp + vvrcomp + + uvtcomp = (-udat) * cos(angle * dtr) + vvtcomp = vdat * sin(angle * dtr) + vt = uvtcomp + vvtcomp + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcfunix (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in the new ATCF UNIX format. +c Unlike the old atcf DOS format in which you waited until the +c whole tracking was over to write the output for all forecast +c hours, with this atcfunix format, each time we are calling this +c subroutine, it is to only write out 1 record, which will be the +c fix info for a particular storm at a given time. Also, even +c though we have some data (GFS, NAM) at 6-hour intervals, Jim +c Gross informed me that TPC does not need the positions at such +c frequency, and keeping the reporting at 12 hour intervals is fine. +c +c While this new atcfunix format contains much more information than +c the old 1-line atcf dos message, for our purposes we will use the +c slots for mslp and wind radii. An example set of output records +c will look like the following: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c plastbar pressure of the outermost closed isobar +c rlastbar radius (nm) of the outermost closed isobar +c rmax radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c cps_vals real array with the values for the 3 cyclone phase +c space parameters: (1) is for Parameter B (thermal +c asymmetry); (2) is for lower level (600-900 mb) thermal +c wind; (3) is for upper level (300-600 mb) thermal wind. +c wcore_flag character for value of 300-500 mb warm core: y, n, or +c 'u' for undetermined. +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE phase + USE verbose_output + + type (datecard) inp + type (trackstuff) trkrinfo + + real cps_vals(3) + real outlon,outlat,rmax,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,irmax,output_fhr,ic,iplastbar,irlastbar + integer vradius(3,4),icps_vals(3) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + character comma_fill1*48,comma_fill2*31,comma_filler*79 + + if ( verb .ge. 3 ) then + print *,'TTT top of atcfunix, ist= ',ist,' ifh= ',ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcfunix. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'in output_atcfunix, tcv_storm_id= ' + & ,storm(ist)%tcv_storm_id + print *,'in output_atcfunix, tcv_storm_id(3:3)= ' + & ,storm(ist)%tcv_storm_id(3:3) + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' +!zhang case ('A','a'); basinid = 'NA' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + if (trkrinfo%want_oci) then + if (plastbar > 0.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -99 + endif + if (rlastbar > 0.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -99 + endif + else + iplastbar = -99 + irlastbar = -99 + endif + + if ( verb .ge. 3 ) then + print *, 'output: rlastbar=',rlastbar,' irlastbar=',irlastbar + print *, 'output: plastbar=',plastbar,' iplastbar=',iplastbar + endif + +c Now convert all of the cyclone phase space parameter values from +c real to integer. + + do ic = 1,3 + if (cps_vals(ic) > -9999.0) then + if (cps_vals(ic) >= 0.0) then + icps_vals(ic) = int(cps_vals(ic)*10. + 0.5) + else + icps_vals(ic) = int(cps_vals(ic)*10. - 0.5) + endif + else + icps_vals(ic) = -9999 + endif + enddo + + if (wcore_flag == 'y'.or. wcore_flag == 'Y') then + wcore_flag = 'Y' + elseif (wcore_flag == 'n' .or. wcore_flag == 'N') then + wcore_flag = 'N' + elseif (wcore_flag == 'u' .or. wcore_flag == 'U') then + wcore_flag = 'U' + else + wcore_flag = 'U' + endif + + comma_fill1 = ', 0, 0, , 0, , 0, 0, ,' + comma_fill2 = ' , , , 0, 0, 0, 0' + comma_filler = comma_fill1//comma_fill2 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + else + + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,'rmax= ',rmax,' irmax= ',irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,a79,', THERMO PARAMS' + & ,3(', ',i7),', ',a1,', ',i2,', DT, -999') + 91 format (a2,', ',a4,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,2(', ',i3),', ',a3) + +c bug fix for IBM: flush the output stream so it actually writes + flush(64) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each +c storm. This message contains the model identifier, the forecast +c initial date, and the positions for 0, 12, 24, 36, 48, 60 and 72 +c hours. In the case of the regional models (NGM, Eta), which +c only go out to 48h, zeroes are included for forecast hours +c 60 and 72. +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by Steve Lord for plotting purposes, and his +c plotting routines need the longitudes in 0 - 360, increasing +c westward. Thus, a necessary adjustment is made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + print *,'top of output_all' + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + intlon(ifh) = 0 + intlat(ifh) = 0 + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + endif + + enddo ifhloop + + print *,'before select case, atcfname= ' + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(5),intlon(5),intlat(9),intlon(9),intlat(13) + & ,intlon(13),intlat(17),intlon(17),intlat(21),intlon(21) + & ,0,0,storm(ist)%tcv_storm_id + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(3),intlon(3),intlat(5),intlon(5),intlat(7) + & ,intlon(7),intlat(9),intlon(9),intlat(11),intlon(11) + & ,intlat(13),intlon(13),storm(ist)%tcv_storm_id + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3),intlat(4) + & ,intlon(4),intlat(5),intlon(5),intlat(6),intlon(6) + & ,intlat(7),intlon(7),storm(ist)%tcv_storm_id + + case ('GDA','HDA') ! GDAS, HDAS + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),0,0,0,0,0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case default +c print *,'!!! ERROR in subroutine output_all. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + print *,'!!! Model name is not identified: ',atcfname + + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + 81 format (i2,a4,4i2.2,14i4,1x,a3) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm + & ,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each storm +c in ATCF format. This message contains the model identifier, the +c forecast initial date, and the positions for 12, 24, 36, 48 +c and 72 hours. This message also contains the intensity +c estimates (in knots) for those same hours. The conversion for +c m/s to knots is to multiply m/s by 1.9427 (3.281 ft/m, +c 1 naut mile/6080 ft, 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by the atcf system at TPC for plotting +c purposes, and the atcf plotting routines need the longitudes in +c 0 - 360, increasing westward. Thus, a necessary adjustment is +c made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real xmaxwind(maxstorm,maxtime) + real conv_ms_knots + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4,basinid*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + conv_ms_knots = 1.9427 + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + + intlon(ifh) = 0 + intlat(ifh) = 0 + + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + + endif + + enddo ifhloop + + basinid = ' ' + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid(1:2) = 'AL' + case ('E','e'); basinid(1:2) = 'EP' + case ('C','c'); basinid(1:2) = 'CP' + case ('W','w'); basinid(1:2) = 'WP' + case ('O','o'); basinid(1:2) = 'SC' + case ('T','t'); basinid(1:2) = 'EC' + case ('U','u'); basinid(1:2) = 'AU' + case ('P','p'); basinid(1:2) = 'SP' + case ('S','s'); basinid(1:2) = 'SI' + case ('B','b'); basinid(1:2) = 'BB' +cPENG case ('A','a'); basinid(1:2) = 'NA' + case ('A','a'); basinid(1:2) = 'AA' + case default; basinid(1:2) = '**' + end select + basinid(3:4) = storm(ist)%tcv_storm_id(1:2) + + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(5),intlon(5) + & ,intlat(9),intlon(9),intlat(13),intlon(13),intlat(17) + & ,intlon(17),0,0 + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,17)*conv_ms_knots) + 0.5) + & ,0 + & ,basinid,inp%byy + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble, ECMWF hi-res + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4),intlat(5) + & ,intlon(5),intlat(7),intlon(7) + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('GDA','HDA') ! GDAS, HDAS + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4) + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,0,0,0,0 + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case default +c print *,'!!! ERROR in subroutine output_atcf. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + end select + + enddo stormloop + + 82 format (i2,a4,4i2.2,10i4,5i3,1x,a4,i2.2) +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_hfip (outlon,outlat,inp,ist + & ,ifh,vmaxwind,xminmslp,vradius,rmax,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified ATCF UNIX format. +c The modification is to allow for sub-hourly output. That is, +c instead of just integer output hours, we can have output at +c 10, 15 or 20 past an hour. This necessitates a change in the +c "forecast hour" placeholder in the ATCF format. Instead of it +c being an I3, we'll make it an I5, with something like a lead time +c of 36.25h being rounded and truncated to 03625 for output. +c +c An example set of output records using the standard atcf format +c looks like the following: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c An example set of modified output records will look like the +c following, for the case of a lead time of 36:15 (36.25): +c +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifh index for the lead time array +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c rmax Radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms + USE verbose_output + + type (datecard) inp + + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,rmax + integer intlon,intlat,output_fhr,irmax,ileadtime + integer vradius(3,4) + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_hfip. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + ! ST: ifcsthour does not exist, so output_fhr is always + ! filled with invalid data here. However, output_fhr is + ! never used, so it is safe to remove. + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + ! output_fhr = ifcsthour + 3 + ileadtime = nint((fhreal(ifh) + 3.0) * 100.0) + else + ! output_fhr = ifcsthour + ileadtime = nint(fhreal(ifh) * 100.0) + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4),irmax + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4),irmax + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4),irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i5.5,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', 0, 0, ',i3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(69) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_fract_wind (outlon,outlat,xsfclon,xsfclat + & ,inp,ist,ifcsthour,vmaxwind,xminmslp,wfract_cov + & ,wfract_type,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values for the fractional areal coverage of various wind +c thresholds. In addition, this subroutine also writes out +c records to a file containing data on the PDF of wind magnitudes +c within r=350 km. +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with areal coverage thresholds. +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, NEE, 981, 857, 629, 810 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, NEE, 874, 732, 319, 610 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, NEE, 454, 327, 99, 270 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, AAE, 721, 721, 721, 721 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, AAE, 465, 465, 465, 465 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, AAE, 298, 298, 298, 298 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the pctgs for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind pctg info; all the +c other info is identical for each entry. +c +c Listed after the "XX" in each record is the radius from which +c the coverage is valid (000 km in this case); Next is the radius +c at which the coverage stops (100 km in this case). Next is the +c wind threshold (34, 50, 64). Next is an identifier for which +c quadrant the coverage starts in (first 2 characters are NE, SE, +c SW, NW); the last character indicates if the coverages are +c computed in the quadrants as earth-relative ("E") or +c storm-motion relative ("R"). The ones listed there as "AAE" +c are for the full disc (i.e., 4-quadrant average), earth-relative. +c Next are the wind coverage percentages, listed as percentage * 10 +c (e.g., 981 = 98.1%). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c wfract_cov percent areal coverage for various wind thresholds +c wfract_type 'earth' or 'storm' relative analysis +c pdf_ct_bin array for pdf of wind magnitudes within r=350 km +c pdf_ct_tot total count of pdf points for r < 350 km +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,pdfval + real wfract_cov(numquad+1,numbin,numthresh) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer :: windthresh(numthresh) = (/34,50,64/) + integer pdf_ct_bin(16) + integer intlon,intlat,output_fhr,intlon100,intlat100,pdf_ct_tot + integer maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (wfract_type == 'earth') then + wt = 'E' + else if (wfract_type == 'storm') then + wt = 'R' + else + wt = 'X' + endif + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'NE',wt + & ,int((1000.*wfract_cov(1,ib,it))+0.5) + & ,int((1000.*wfract_cov(2,ib,it))+0.5) + & ,int((1000.*wfract_cov(3,ib,it))+0.5) + & ,int((1000.*wfract_cov(4,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'AA',wt + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a6,i3.3,', ',i3.3,', ' + & ,i3,', ',a2,a1,4(', ',i4),', ',i4,a1,', ',i5,a1) + +c -------------------------------------------------- +c Now compute and write out the pdf values for the +c wind magnitude.... +c -------------------------------------------------- + + do ip = 1,16 + pdfval = float(pdf_ct_bin(ip)) / float(pdf_ct_tot) + write (76,85) atcfymdh,basinid,storm(ist)%tcv_storm_id(1:2) + & ,output_fhr,10*(ip-1),10*ip,pdf_ct_bin(ip) + & ,pdf_ct_tot,pdfval + enddo + + 85 format (1x,i10.10,3x,a2,a2,3x,i3,3x,i3.3,'_',i3.3,3x,i7,2x,i7 + & ,2x,f6.3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(73) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_wind_structure (outlon,outlat,xsfclon + & ,xsfclat,inp,ist,ifcsthour,vmaxwind,xminmslp,er_wind + & ,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values of the winds at specified distances along 45-degree +c radials in each storm quadrant. These are output +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 radials: NE, SE, +c SW and NW (45,135,225,315). For the storm-relative estimates, +c these radials will be computed at the same relative angles (i.e., +c 45,135,225,315), but with respect (positive clockwise) to the +c direction of storm motion. For example, for a storm moving with +c a heading of 280, the wind structure is evaluated at these +c radials: 325 (front-right; 45 deg CW from heading), 55 (back- +c right; 135 deg CW from heading), 145 (back-left; 225 deg CW from +c heading), 235 (front-left; 315 deg CW from heading). +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with wind values at the 13 specified distances +c (10, 25, 50, 75, 100, 150, 200, 250, 300, 350, 400, 450, 500 km) +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NEE, 1137, 1221, 854, 655, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SEE, 947, 982, 474, 396, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SWE, 645, 683, 328, 277, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NWE, 725, 753, 619, 429, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FRR, 1134, 1224, 852, 654, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BRR, 944, 984, 472, 393, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BLR, 649, 686, 321, 272, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FLR, 729, 756, 613, 421, etc., ... out to 500 km +c +c NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text. +c NOTE: These winds are in m/s coming into this routine and will +c be converted to knots*10 for output (e.g., 1221 = 122.1 kts) +c +c The "71" ID indicates earth-relative winds, the "72" ID indicates +c storm-relative winds. Here are the other IDs that will be used: +c 81: Tangential winds, earth-relative (m/s) +c 82: Tangential winds, storm-relative (m/s) +c 91: Radial winds, earth-relative (m/s) +c 92: Radial winds, storm-relative (m/s) +c +c Note that in this example, for this 36h forecast hour, there are +c 8 entries. This is so that we can include the wind values for +c the 4 different quadrants, for both the earth relative analyses +c (NEE, SEE, SWE, NWE) and the storm-relative analyses (FRR, BRR, +c BLR, FLR). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + integer ioutwind(numdist) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real outlon,outlat + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,id,intlon100,intlat100,ir + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*2 :: crel(4) = (/'FR','BR','BL','FL'/) + + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + +c Total wind (converted to knots*10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 71, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Total wind (converted to knots*10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 72, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 81, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 82, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 92, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a10,a2,a1,14(', ',i4) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(72) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_ike (outlon,outlat,xsfclon,xsfclat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,ike,sdp,wdp,maxstorm + & ,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the Integrated Kinetic Energy (IKE) and Storm Surge Damage +c Potential (SDP), based on Powell (BAMS, 2007). At this time, we +c are only computing the IKE values for TS threshold (17.5 m/s) and +c above. We are not yet computing wind damage potential (WDP) +c since, per Mark Powell (4/2008), he is currently re-formulating +c an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with WDP, SDP and IKE values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, 340, 560, 212, 174, 42, 93, 12, 0 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, WDP, SDP, I10, ITS, IH ,I2540,I4154, I55 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Values for WDP and SDP are multiplied by 10 in this routine +c before being written out. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c ike integrated kinetic energy, in units of TJ +c sdp storm surge damage potential +c wdp wind damage potential +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,sdp,wdp + real ike(max_ike_cats) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,intlon100,intlat100,maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (74,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, IKE',int((wdp*10)+0.5),int((sdp*10)+0.5) + & ,int(ike(1)+0.5),int(ike(2)+0.5),int(ike(3)+0.5) + & ,int(ike(4)+0.5),int(ike(5)+0.5),int(ike(6)+0.5) + & ,intlat100,clatns,intlon100,clonew +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a14,8(',',i5) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(74) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_phase (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,paramb,vtl_slope + & ,vtu_slope,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the three parameters that comprise Bob Hart's cyclone phase +c space (CPS). These parameters are his "parameter B", which +c assesses the left-right thermal asymmetry, and the upper +c troposphere (300-600 mb) and lower troposphere (900-600 mb) +c thermal wind values. +c +c LOCAL: +c +c Arrays: +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with paramb, vtl_slope and vtu_slope values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, 340, 560, 212 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, B, VTL, VTU +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c paramb thermal asymmetry +c vtl_slope thermal wind value for lower troposphere (900-600 mb) +c vtu_slope thermal wind value for upper troposphere (600-300 mb) +c +c OUTPUT: +c ioiret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + real outlon,outlat,paramb,vtl_slope,vtu_slope + real vmaxwind,conv_ms_knots,xminmslp + integer intlon,intlat,output_fhr + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (71,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 95, CPS',int(paramb+0.5),int(vtl_slope+0.5) + & ,int(vtu_slope+0.5) +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a14,3(',',i6)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(71) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf_gen (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different. Here's an example of the +c TPC standard atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c be the one that is pulled from the tcvitals or gen_vitals +c record: +c +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 50, NEQ, 155, 000, 000, 000 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 64, NEQ, 000, 000, 000, 000 +c +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd storm translation speed +c istmdir direction of storm movement +c plastbar pressure of last closed isobar +c rlastbar radius of last closed isobar +c rmax radius of max winds +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals; USE level_parms + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real outlon,outlat,plastbar,rlastbar,rmax + real vmaxwind,conv_ms_knots,xminmslp,mslp_outp_adj + real cps_vals(3) + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar,irmax + integer ivtl,ivtu,iparamb,output_fhr + integer vradius(3,4) + integer imeanzeta(nlevgrzeta),igridzeta(nlevgrzeta) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + + if ( verb .ge. 3) then + print *,'+++ Top of output_atcf_gen, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (rmax > -90.0) then + irmax = int(rmax + 0.5) + else + irmax = -99 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(66) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf_sink (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta,igridzeta + & ,cps_vals,plastbar,rlastbar,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The "sink" in the subroutine name indicates that this output +c contains the whole kitchen sink of forecast storm info. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different, and the part after the radii +c data is different. Here's an example of the TPC standard +c atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c indicate the lat/lon at which the storm was *first* found in +c the model. The position may be either found within this run +c of the tracker, or that position may have been pulled from the +c tcvitals or gen_vitals record: +c +c 2000092500_F000_206N_0623W_13L, 2000092500, 03, GFSO, 036 +c , 243N, 675W, 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c , PLAS, RLAS, RMX, DIR, SPD, B, VTU, VTL +c , Z8MN, Z8MX, Z7MN, Z7MX +c +c As noted above, there is extra info at the end, after the +c "34, NEQ, 242, 163, 124, 208" radii info. Here is a key +c to indicate what these items are: +c +c PLAS: Pressure (mb) of last closed isobar +c RLAS: Radius of the last closed isobar in nm, 0 - 9999 nm. +c RMX: Radius of max winds, 0 - 999 nm. +c DIR: Direction of storm motion. +c SPD: Speed of storm motion (m/s * 10). +c B: Hart's CPS "Parameter B" thickness asymmetry value (m). +c VTL: Hart's CPS thermal wind (Lower, 900-600) value. +c VTU: Hart's CPS thermal wind (Upper, 600-300) value. +c Z8MN: Mean value of 850 mb zeta surrounding storm. +c Z8MX: Max value of 850 mb zeta near storm. +c Z7MN: Mean value of 700 mb zeta surrounding storm. +c Z7MX: Max value of 700 mb zeta near storm. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd speed of storm translation +c istmdir direction of storm motion +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c plastbar pressure of last closed isobar (pa) +c rlastbar radius of last closed isobar (nm) +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real cps_vals(3) + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar + integer iparamb,ivtl,ivtu,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1 + + if ( verb .ge. 3 ) then + print *,'+++ Top of output_atcf_sink, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4)) + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',2(i4,', '),4(i3,', '),2(i5,', '),4(i4,', '),a9) + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',2(i4,', '),i3,', ',2(i4,', '),3(i6,', '),4(i6,', ') + & ,a9) + +c write (68,87) gstm%gv_gen_date,gstm%gv_gen_lat +c & ,gstm%gv_gen_latns,gstm%gv_gen_lon +c & ,gstm%gv_gen_lonew,gstm%gv_gen_type +c & ,inp%bcc,inp%byy,inp%bmm,inp%bdd,inp%bhh +c & ,adjustr(atcfname),ifcsthour,intlat,clatns,intlon,clonew +c & ,int((vmaxwind*conv_ms_knots) + 0.5) +c & ,int(xminmslp/100.0 + 0.5) +c & ,'XX, 34, NEQ' +c & ,istmspd,istmdir,imeanzeta(1),igridzeta(1) +c & ,imeanzeta(2),igridzeta(2) +c +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,6(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(68) + + return + end + +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_tcvitals (xlon,xlat,inp,ist,iovret) +c +c ABSTRACT: This subroutine outputs a tcvitals record. The +c lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE inparms; USE set_max_parms + USE verbose_output + + type (tcvcard) stm + type (datecard) inp + real xlon,xlat +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "storm" +c components for this storm, then we will change the specific +c components that we need to. + + stm = storm(ist) + + stm%tcv_center = 'AEAR' + + stm%tcv_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + stm%tcv_latns = 'S' + else + stm%tcv_latns = 'N' + endif + + if (xlon >= 180.) then + stm%tcv_lon = 3600 - int(xlon * 10. + 0.5) + stm%tcv_lonew = 'W' + else + stm%tcv_lon = int(xlon * 10. + 0.5) + stm%tcv_lonew = 'E' + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) stm + endif + + write (65,21) stm + + 21 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + +c +c bug fix for IBM: flush the output stream so it actually writes + flush(65) + + return + end + +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_gen_vitals (xlon,xlat,inp,ist,istmspd,istmdir + & ,iovret) +c +c ABSTRACT: This subroutine outputs a modified vitals record. +c The lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c The storm identifier is different than that for a standard +c tcvitals. The storm identifier contains the date/time that +c the storm was first identified, and the lat/lon position at +c which it was first identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE gen_vitals; USE inparms; USE set_max_parms + USE verbose_output + + implicit none + + type (gencard) gstm + type (datecard) inp + real xlon,xlat + integer ist,iovret,istmspd,istmdir +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the vitals record. + + if (gstm%gv_gen_date /= 99999) then + + if (gstm%gv_gen_type /= 'FOF') then + ! If this is not a 'FOF' storm (found on the fly storm), then + ! it must be a TC vitals storm, or a tropical cyclone, and we + ! don't want to create a vitals record for a tropical cyclone, + ! since we will rely on reading them from the TC Vitals + ! database instead. + return + endif + + else + + ! This storm is new in this forecast/analysis and was found on + ! the fly in the first time level for this run and there was no + ! previous vitals record for this system + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = 0 + + gstm%gv_gen_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_gen_latns = 'S' + else + gstm%gv_gen_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_gen_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'W' + else + gstm%gv_gen_lon = int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'E' + endif + + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + gstm%gv_obs_ymd = inp%bcc * 1000000 + & + inp%byy * 10000 + & + inp%bmm * 100 + & + inp%bdd + + gstm%gv_obs_hhmm = inp%bhh * 100 + + gstm%gv_obs_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_obs_latns = 'S' + else + gstm%gv_obs_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_obs_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'W' + else + gstm%gv_obs_lon = int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'E' + endif + + gstm%gv_stdir = istmdir + gstm%gv_stspd = istmspd + + gstm%gv_depth = 'U' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) gstm + endif + + write (67,21) gstm + + 21 format (i10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1,'_',a3,1x,i8,1x + & ,i4.4,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x + & ,i3,4(1x,i4),1x,a1) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(67) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_tracker_mask (masked_outc,lb,ifh,ifcsthour + & ,imax,jmax,iotmret) +c +c ABSTRACT: This subroutine outputs a GRIB record that contains the +c "mask" used to mask out areas surrounding low pressure centers +c that have been found during the search at each forecast hour. This +c mask is written out purely for diagnostic purposes. The GRIB +c identifier given to the mask in the pds is 850 mb height (you can +c make it anything you want). This is only done for the "midlat" +c and "tcgen" cases, since the runs for those cases use a mask while +c the regular "tracker" run (that is, the run which strictly tracks +c only those storms in the TC vitals file) does not. +c +c INPUT: +c masked_outc logical array containing mask +c ifh integer counter for current forecast hour +c ifcsthour integer current forecast hour +c imax num points is i-direction of input grid +c jmax num points is j-direction of input grid +c +c OUTPUT: +c iotmret return code from this subroutine + + implicit none +c + integer ifh,imax,jmax,iotmret,kf,igoret,iix,jjx,ipret + integer ifcsthour + integer kpds(200),kgds(200) + logical(1) masked_outc(imax,jmax),lb(imax,jmax) + real xmask(imax,jmax) +c + if (ifh == 1) then + call baopenw (77,"fort.77",igoret) + print *,'baopenw: igoret= ',igoret + + if (igoret /= 0) then + print *,' ' + print *,'!!! ERROR in sub output_tracker_mask opening' + print *,'!!! **OUTPUT** grib files. baopenw return codes:' + print *,'!!! grib file 1 return code = igoret = ',igoret + STOP 95 + return + endif + endif + + xmask = 0.0 + do jjx = 1,jmax + do iix = 1,imax + if (masked_outc(iix,jjx)) then + xmask(iix,jjx) = 1.0 + else + xmask(iix,jjx) = 0.0 + endif + enddo + enddo + + kf = imax * jmax + +c kpds(5) = 7 +c kpds(6) = 100 +c kpds(7) = 850 +c kpds(22) = 0 + + kpds(1) = 7 ; kpds(2) = 80 + kpds(3) = 255 ; kpds(4) = 192 + kpds(5) = 7 ; kpds(6) = 100 + kpds(7) = 850 ; kpds(8) = 99 + kpds(9) = 7 ; kpds(10) = 20 + kpds(11) = 12 ; kpds(12) = 0 + kpds(13) = 1 ; kpds(14) = ifcsthour + kpds(15) = 0 ; kpds(16) = 10 + kpds(17) = 0 ; kpds(18) = 1 + kpds(19) = 2 ; kpds(20) = 0 + kpds(21) = 20 ; kpds(22) = 0 + kpds(23) = 0 ; kpds(24) = 0 + kpds(25) = 0 + kgds(1) = 0 ; kgds(2) = imax + kgds(3) = jmax ; kgds(4) = -90000 + kgds(5) = 0 ; kgds(6) = 128 + kgds(7) = 90000 ; kgds(8) = 359750 + kgds(9) = 250 ; kgds(10) = 250 + kgds(11) = 64 ; kgds(12) = 0 + kgds(13) = 0 ; kgds(14) = 0 + kgds(15) = 0 ; kgds(16) = 0 + kgds(17) = 0 ; kgds(18) = 0 + kgds(19) = 0 ; kgds(20) = 255 + + write(*,980) kpds(1),kpds(2) + write(*,981) kpds(3),kpds(4) + write(*,982) kpds(5),kpds(6) + write(*,983) kpds(7),kpds(8) + write(*,984) kpds(9),kpds(10) + write(*,985) kpds(11),kpds(12) + write(*,986) kpds(13),kpds(14) + write(*,987) kpds(15),kpds(16) + write(*,988) kpds(17),kpds(18) + write(*,989) kpds(19),kpds(20) + write(*,990) kpds(21),kpds(22) + write(*,991) kpds(23),kpds(24) + write(*,992) kpds(25) + write(*,880) kgds(1),kgds(2) + write(*,881) kgds(3),kgds(4) + write(*,882) kgds(5),kgds(6) + write(*,883) kgds(7),kgds(8) + write(*,884) kgds(9),kgds(10) + write(*,885) kgds(11),kgds(12) + write(*,886) kgds(13),kgds(14) + write(*,887) kgds(15),kgds(16) + write(*,888) kgds(17),kgds(18) + write(*,889) kgds(19),kgds(20) + write(*,890) kgds(21),kgds(22) +c + 980 format('tmow kpds(1) = ',i7,' kpds(2) = ',i7) + 981 format('tmow kpds(3) = ',i7,' kpds(4) = ',i7) + 982 format('tmow kpds(5) = ',i7,' kpds(6) = ',i7) + 983 format('tmow kpds(7) = ',i7,' kpds(8) = ',i7) + 984 format('tmow kpds(9) = ',i7,' kpds(10) = ',i7) + 985 format('tmow kpds(11) = ',i7,' kpds(12) = ',i7) + 986 format('tmow kpds(13) = ',i7,' kpds(14) = ',i7) + 987 format('tmow kpds(15) = ',i7,' kpds(16) = ',i7) + 988 format('tmow kpds(17) = ',i7,' kpds(18) = ',i7) + 989 format('tmow kpds(19) = ',i7,' kpds(20) = ',i7) + 990 format('tmow kpds(21) = ',i7,' kpds(22) = ',i7) + 991 format('tmow kpds(23) = ',i7,' kpds(24) = ',i7) + 992 format('tmow kpds(25) = ',i7) + 880 format('tmow kgds(1) = ',i7,' kgds(2) = ',i7) + 881 format('tmow kgds(3) = ',i7,' kgds(4) = ',i7) + 882 format('tmow kgds(5) = ',i7,' kgds(6) = ',i7) + 883 format('tmow kgds(7) = ',i7,' kgds(8) = ',i7) + 884 format('tmow kgds(9) = ',i7,' kgds(10) = ',i7) + 885 format('tmow kgds(11) = ',i7,' kgds(12) = ',i7) + 886 format('tmow kgds(13) = ',i7,' kgds(14) = ',i7) + 887 format('tmow kgds(15) = ',i7,' kgds(16) = ',i7) + 888 format('tmow kgds(17) = ',i7,' kgds(18) = ',i7) + 889 format('tmow kgds(19) = ',i7,' kgds(20) = ',i7) + 890 format('tmow kgds(20) = ',i7,' kgds(22) = ',i7) +c + print *,'just before call to putgb, kf= ',kf + call putgb (77,kf,kpds,kgds,lb,xmask,ipret) + print *,'just after call to putgb, kf= ',kf + if (ipret == 0) then + print *,' ' + print *,'+++ IPRET = 0 after call to putgb' + print *,' ' + else + print *,' ' + print *,'!!!!!! ERROR: IPRET NE 0 AFTER CALL TO PUTGB !!!' + print *,' ' + endif +c +c bug fix for IBM: flush the output stream so it actually writes + flush(6) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_next_ges (fixlon,fixlat,ist,ifh,imax,jmax + & ,dx,dy,modelid,valid_pt,readflag,maxstorm,istmspd + & ,istmdir,ctype,trkrinfo,ignret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. It does this by using two different +c methods and averaging the results from those two. The +c first method is a simple linear extrapolation made by +c basically drawing a line from the previous position +c through the current fix position and assuming straight +c line motion. The second method is to do a barnes +c smoothing of u & v in the vicinity of the storm at 850, +c 700 & 500 mb to get an average environmental wind +c vector at each level, and then move the storm according +c to the vector at each level. Then a weighted average is +c taken of all these positions from methods 1 & 2 to get +c the consensus for the guess position. NOTE: For a +c regional model and a storm that is relatively close to +c the model boundary, there is a strong possibility that +c the barnes analysis subroutine will fail due to trying +c to access grid points beyond the model's lateral +c boundary. In this case, the redlm & ridlm are halved +c and barnes is called again. If it still fails, then +c just use the result from method 1 as a default. +c +c INPUT: +c fixlon Array with longitudes of fix positions +c fixlat Array with latitudes of fix positions +c ist Storm number currently being processed +c ifh Forecast hour currently being processed +c imax Max number of pts in x-direction for this grid +c jmax Max number of pts in y-direction for this grid +c dx grid-spacing of the model in the i-direction +c dy grid-spacing of the model in the j-direction +c modelid Integer indicating what model's data is being processed +c valid_pt Logical; bitmap indicating if valid data at that pt. +c readflag Logical; Tells whether or not a variable was read in +c for this model +c maxstorm Max # of storms that can be handled in this run +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, eventually +c in the barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c istmspd The speed that the storm would have to move to get from +c the current position to the next guess position +c istmdir The direction in which the storm would have to move to +c get from the current position to the next guess position +c +c LOCAL: +c dt Number of seconds between successive forecast times +c for this particular model. +c dtkm Distance in meters of 1 degree latitude +c icutmax Max number of times to cut the ridlm and redlm in half, +c for use in calling barnes. If you're using a regional +c model and on the first call to barnes you try to access +c a point that's outside the model grid boundary, we'll +c call barnes again, but not before cutting the redlm and +c ridlm in half. icutmax says how many times to allow +c this cutting in half before giving up and just going +c with the extrapolation method. At first writing, we'll +c set icutmax to 2, so that it will allow the ridlm to +c get down to 500 km (originally 2000 km) and the redlm +c to 125 km (originally 500 km). +c *** NOTE: After testing the system, it was found that if +c we cut these radii, the u and v values that are +c calculated from barnes are too strongly influenced by +c the near-storm environment and, especially for asymmetric +c systems, resulted in u and v values being much too strong. +c As such, we will not allow these values to be cut, and if +c we hit the boundaries in barnes, we'll just use the +c extrapolation method, which has seemed to work just fine. +c +c OTHER: (slonfg, slatfg & storm defined in module def_vitals) +c slonfg Array containing first guess longitude positions +c slatfg Array containing first guess latitude positions +c storm Contains tcvitals information +c + USE radii; USE def_vitals; USE set_max_parms; USE grid_bounds + USE tracked_parms; USE level_parms; USE trig_vals; USE trkrparms + USE gen_vitals + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer icutmax,istmspd,istmdir,bskip,ileadtime,ifcsthour + integer ifh,ist,npts,ilonfix,jlatfix,ibeg,jbeg,iend,jend + integer igiret,ignret,icut,iuret,ivret,ibarnct,n,ix1,ix2 + integer icount,imax,jmax,modelid,maxstorm + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real barneswt,extrapwt,dtkm,dt,ucomp,vcomp,xdist,ydist,ydeg + real extraplat,avglat,cosfac,xdeg,extraplon,ylatdegmove_last + real xlondegmove_last,xnumh_last,ylatdegmove_last_perhour + real xlondegmove_last_perhour,xnumh_next,yoldavglat + real yoldcosfac,xdistmove_last,xdistmove_last_perhour + real ynewavglat,ynewcosfac,xdegnew,dx,dy,re,ri,ubar,vbar + real wgttot,uavg,vavg,reold,riold,barnlat,barnlon,wt_total + real tmp_fix_lon_curr,tmp_fix_lon_prev + character*1 :: in_grid, extrap_flag, barnes_flag + character(*) ctype + logical(1) valid_pt(imax,jmax),readflag(14) +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c For updating the first guess, if Method 1 and Method 2 are both +c able to be done, give the following weights to the 2 methods. +c + data barneswt /0.50/, extrapwt /0.50/ +c +c ------------------------------- +c METHOD 1: LINEAR EXTRAPOLATION +c ------------------------------- +c First, just do a simple linear extrapolation from the previous +c fix position through the current fix position. If it's the +c first time (vt=0), then use the storm motion vector and storm +c speed information from the TC Vitals card. +c + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(ist)%tcv_stdir == -99 .or. + & storm(ist)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GET_NEXT_GES, at fcst hour = 0, either ' + print *,'!!! storm motion or storm speed = -99 on TCV card.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(ist)%tcv_stdir + print *,'!!! storm motion speed= ',storm(ist)%tcv_stspd + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + extrap_flag = 'n' + else + ucomp = sin(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + vcomp = cos(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(ist,ifh) + ydeg + avglat = 0.5 * (extraplat + fixlat(ist,ifh)) + if (avglat > 89.5) avglat = 89.0 + if (avglat < -89.5) avglat = -89.0 + cosfac = cos(avglat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(ist,ifh) + xdeg + endif + else + +c Do a simple linear extrapolation of the current motion of the +c storm. Follow a line from the fix position from the last fix +c through the current fix and extrapolate out. To figure out the +c new latitude, just see how many deg lat the storm moved since +c last time and add it to the current fix latitude. To calculate +c the new fix longitude, though, we need to see how many deg lon +c the storm moved since the last time, convert that to the +c distance (km) the storm travelled in the x-direction (at an +c average latitude between the current and previous latitudes), +c and then add that distance on to the current longitude and +c convert that distance to the num of degrees the storm has +c travelled in the x-direction (at an average latitude between +c the current and next(extrap) latitudes). +c +c UPDATE Feb 2009: To account for the possibility of using +c irregularly spaced forecast hours (e.g., 6,10,10.5,...etc), +c I had to modify this linear extrapolation. + + print *,' ' + print *,'xxxx get_next_ges, prev fix lon= ',fixlon(ist,ifh-1) + print *,'xxxx get_next_ges, curr fix lon= ',fixlon(ist,ifh) + print *,' ' + + if (fixlat(ist,ifh-1) > -900.0 .and. + & fixlon(ist,ifh-1) > -900.0) then + + ylatdegmove_last = fixlat(ist,ifh) - fixlat(ist,ifh-1) + + tmp_fix_lon_curr = fixlon(ist,ifh) + tmp_fix_lon_prev = fixlon(ist,ifh-1) + + if (tmp_fix_lon_prev < 0.0 .and. tmp_fix_lon_prev > -25.0) + & then + ! previous lon position is within 25 deg west of the GM + ! and is listed in negative degrees. + if (tmp_fix_lon_curr < 0.0 .and. tmp_fix_lon_curr > -25.0) + & then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 1 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both negative. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr < 25.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 2 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous time' + print *,' is negative, while lon for current time' + print *,' is positive, but within 0-25 deg East.' + print *,' All ok!' + endif + endif + + elseif (tmp_fix_lon_prev > 335.0 .and. + & tmp_fix_lon_prev <= 360.0) then + ! previous lon position is within 25 deg west of the GM + ! and is listed in positive degrees. + if (tmp_fix_lon_curr > 335.0 .and. + & tmp_fix_lon_curr <= 360.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 3 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both positive. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr <= 25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 4 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between 0 & 25. Current tmp_lon' + print *,' has been adjusted to be > 360 for the' + print *,' purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < 0.0 .and. + & tmp_fix_lon_curr >= -25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 5 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is west of the GM and' + print *,' is between 0 & -25. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < -335.0 .and. + & tmp_fix_lon_curr >= -360.0) then + tmp_fix_lon_curr = 720.0 - tmp_fix_lon_curr + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 6 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between -335 & -360. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + + endif + + endif + + xlondegmove_last = tmp_fix_lon_curr - tmp_fix_lon_prev + + xnumh_last = fhreal(ifh) - fhreal(ifh-1) + + ylatdegmove_last_perhour = ylatdegmove_last / xnumh_last + xlondegmove_last_perhour = xlondegmove_last / xnumh_last + + xnumh_next = fhreal(ifh+1) - fhreal(ifh) + + extraplat = fixlat(ist,ifh) + & + (ylatdegmove_last_perhour * xnumh_next) + + yoldavglat = 0.5 * (fixlat(ist,ifh) + fixlat(ist,ifh-1)) + yoldcosfac = cos (dtr * yoldavglat) + xdistmove_last = xlondegmove_last * dtk * yoldcosfac + + xdistmove_last_perhour = xdistmove_last / xnumh_last + + ynewavglat = 0.5 * (extraplat + fixlat(ist,ifh)) + ynewcosfac = cos(dtr * ynewavglat) + xdegnew = (xdistmove_last_perhour * xnumh_next) + & / (dtk * ynewcosfac) + extraplon = tmp_fix_lon_curr + xdegnew + + else + + if ( verb .ge. 3 ) then + print *,' ' + write(6,92) '!!! IN GET_NEXT_GES, at fcst hour = ' + & ,ifhours(ifh),ifclockmins(ifh) + print *,'!!! the lon and lat positions for the previous' + print *,'!!! forecast hour are -999, meaning that this is a' + print *,'!!! new storm, so we cannot use the extrap method.' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + 92 format (1x,a36,i4,':',i2.2) + + extrap_flag = 'n' + + endif + + endif + +c ------------------------------- +c METHOD 2: Barnes analysis +c ------------------------------- +c Do a barnes analysis on the u & v components of the wind near the +c storm to get an average u & v, then advect the storm according to +c the average wind vector obtained. The call to get_ij_bounds is +c needed in order to restrict the number of grid points that are +c searched in the barnes subroutine. See Abstract from this +c subroutine for further details. + + npts = ceiling(ridlm/(dtk*((dx+dy)/2))) + + call get_ij_bounds (npts,0,ridlm,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for ' + print *,'!!! storm number ',ist + endif + + ignret = 92 + return + endif + + if (verb >= 3) then + print *,' ' + print *,' +++ In get_next_ges after call to get_ij_bounds,' + print *,' getting bounds for the barnes analysis...' + print *,' glatmax= ',glatmax,' glatmin= ',glatmin + print *,' glonmax= ',glonmax,' glonmin= ',glonmin + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + print *,' ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + endif + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if ((dx+dy)/2 > 0.20) then + bskip = 1 + else if ((dx+dy)/2 > 0.10 .and. (dx+dy)/2 <= 0.20) then + bskip = 2 + else if ((dx+dy)/2 > 0.05 .and. (dx+dy)/2 <= 0.10) then + bskip = 3 + else if ((dx+dy)/2 > 0.03 .and. (dx+dy)/2 <= 0.05) then + bskip = 5 + else if ((dx+dy)/2 <= 0.03) then + bskip = 10 + endif + +c Calculate average wind at each level (currently: 850, 700 & 500) + + re = redlm + ri = ridlm + icut = 0 + + if (trkrinfo%type == 'midlat') then + icutmax = 2 + else + icutmax = 1 + endif + + radmaxloop: do while (icut <= icutmax .and. in_grid == 'n') + + ubar = 0.0; vbar = 0.0 + iuret = 0; ivret = 0 + wgttot = 0.0 + ibarnct = 0 + barnes_flag = 'n' + + levelloop: do n=1,nlevg + + select case (n) + case (1); ix1=3; ix2=4 ! For 850 mb readflags + case (2); ix1=5; ix2=6 ! For 700 mb readflags + case (3); ix1=12; ix2=13 ! For 500 mb readflags + end select + + if (readflag(ix1) .and. readflag(ix2)) then + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,u(1,1,n),valid_pt + & ,bskip,re,ri,uavg,icount,ctype,trkrinfo,iuret) + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,v(1,1,n),valid_pt + & ,bskip,re,ri,vavg,icount,ctype,trkrinfo,ivret) + + if (iuret /= 0 .or. ivret /= 0) then + +c ...barnes probably tried to access a pt outside the grid +c domain. So, reduce by half the distance from the center +c of the farthest pt that barnes tries to access, exit this +c loop, and try it again with the smaller re and ri. + + iuret = 96; ivret = 96 + reold = re + riold = ri + re = 0.5 * re + ri = 0.5 * ri + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: While attempting to use the barnes ' + print *,'method to update the first guess, the ' + print *,'algorithm tried to access a grid point that ' + print *,'does not have valid data, meaning that too ' + print *,'large a radius is being searched. So, the 2 ' + print *,'radii, re and ri, are being halved and, if the' + print *,'value of icutmax > 0, the algorithm will be ' + print *,'run again. Otherwise, if icutmax = 0, only ' + print *,'the extrapolation method will be used.' + print *,'iuret= ',iuret,' ivret= ',ivret,' icut= ',icut + print *,'Old re = ',reold,' New re = ',re + print *,'Old ri = ',riold,' New ri = ',ri + endif + + exit levelloop + + else + ubar = ubar + wgts(n) * uavg + vbar = vbar + wgts(n) * vavg + wgttot = wgttot + wgts(n) + ibarnct = ibarnct + 1 + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, ix1= ',ix1,' ix2= ',ix2 + print *,' uavg= ',uavg,' vavg= ',vavg + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' n= ',n,' wgts(n)= ',wgts(n),' wgttot= ' + & ,wgttot + print *,' ibarnct= ',ibarnct + print *,' ' + print *,' ' + endif + endif + + endif + + enddo levelloop + + if (ibarnct > 0 .and. wgttot > 0.0) then + barnes_flag = 'y' + in_grid = 'y' + ubar = ubar / wgttot + vbar = vbar / wgttot + barnlat = fixlat(ist,ifh) + (vbar * dt)/dtkm + cosfac = cos (dtr * 0.5 * (fixlat(ist,ifh) + barnlat)) + barnlon = fixlon(ist,ifh) + (ubar * dt)/(dtkm * cosfac) + + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, mean stats follow: ' + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' wgttot= ',wgttot + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' barnlon= ',barnlon,' barnlat= ',barnlat + print *,' dt= ',dt,' dtkm= ',dtkm,' cosfac= ',cosfac + endif + + +c This next if statement says that if we've had to reduce the +c size of the barnes analysis domain twice already, then we've +c only done the analysis on a much smaller area, and this +c doesn't give us as good a picture of the average winds in the +c area of the storm, so reduce the emphasis we place on the +c barnes method. + + if (icut >= 2) barneswt = barneswt / 2. + + else + barnes_flag = 'n' + endif + + icut = icut + 1 + + enddo radmaxloop + +c --------------------- +c Average the results +c --------------------- +c Now do a weighted average of the positions obtained from the +c linear extrapolation and the barnes analysis methods. + + if (extrap_flag == 'y' .and. barnes_flag == 'y') then + wt_total = barneswt + extrapwt + slatfg(ist,ifh+1) = (barneswt * barnlat + extrapwt * extraplat) + & / wt_total + + ! Note that in any of these statements just below, in order for + ! any of these to be > 360, the original fixlon must be close + ! to 360, i.e., in the far eastern part of the grid, as opposed + ! to being in the far western part (e.g., 0-2 deg East or so). + ! Conversely, for any of these to be < 0, the original fixlon + ! must be close to 0, i.e., in the far *western* part of the + ! grid. + +c yyyy + + if (fixlon(ist,ifh) > 330.0) then + + ! In this part of the IF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being 330+, to be consistent with the fixlon for + ! this time. + + if (extraplon > 330. .and. barnlon > 330.) then + + continue ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (extraplon > 330. .and. + & (barnlon >= 0.0 .and. barnlon < 30.)) then + + ! extraplon > 330, but barnlon is in the 0-30 range, so + ! we need to convert the barnlon value to be 360+ + + barnlon = barnlon + 360. + + elseif (extraplon > 330. .and. barnlon < 0.) then + + ! extraplon > 330, but barnlon is < 0, so + ! we need to convert the barnlon value to be positive... + + barnlon = barnlon + 360. + + elseif (barnlon > 330. .and. + & (extraplon >= 0.0 .and. extraplon < 30.)) then + + ! barnlon > 330, but extraplon is in the 0-30 range, so + ! we need to convert the extraplon value to be 360+ + + extraplon = extraplon + 360. + + elseif (barnlon > 330. .and. extraplon < 0.) then + + ! barnlon > 330, but extraplon is < 0, so + ! we need to convert the extraplon value to be positive... + + extraplon = extraplon + 360. + + endif + + elseif (fixlon(ist,ifh) >= 0. and. fixlon(ist,ifh) < 30.0) then + + ! In this part of the ELSEIF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being in the reference of >360 since that is what the + ! code below this is expecting with the computation of + ! slonfg for the next lead time. + + if ((extraplon >= 0. .and. extraplon < 60.) .and. + & (barnlon >= 0. .and. barnlon < 60.)) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon < 0. .and. extraplon > -60.) .and. + & (barnlon < 0. .and. barnlon > -60.)) then + + ! convert extraplon and barnlon to be positive + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon < 0.) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon < 0.) then + + extraplon = extraplon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon > 330.) then + + barnlon = barnlon + 360. + + elseif (barnlon >= 330. .and. extraplon < 60.) then + + extraplon = extraplon + 360. + + elseif (extraplon >= 330. .and. barnlon < 60.) then + + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon > 330.) then + + extraplon = extraplon + 360. + + endif + + else + + continue ! extraplon and barnlon do not need to be modified + ! since there should be no way that a storm + ! currently east of 30E and west of 30W could make + ! it to the Greenwich Mer in one forecast interval + + endif + + print *,' ' + print *,'+++ In get_next_ges, before averaging the 2 methods, ' + print *,' Raw (no conversion for GM wrap) barnlon= ' + & ,barnlon + print *,' Raw (no conversion for GM wrap) extraplon= ' + & ,extraplon + + slonfg(ist,ifh+1) = (barneswt * barnlon + extrapwt * extraplon) + & / wt_total + + if (slonfg(ist,ifh+1) > 360.) then + ! If we've GM-wrapped past 360, adjust it to be 0-360... + slonfg(ist,ifh+1) = slonfg(ist,ifh+1) - 360. + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'y' .and. barnes_flag == 'n') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_next_ges, barnes method was not ' + print *,'!!! done for updating the first guess for this ' + print *,'!!! storm. Only the linear extrapolation method ' + print *,'!!! was used.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + slatfg(ist,ifh+1) = extraplat + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 0.0,0.0,0.0 + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'n' .and. barnes_flag == 'y') then + slatfg(ist,ifh+1) = barnlat + if (barnlon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (barnlon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = barnlon + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 360.-barnlon,barnlat + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges, new position guess not' + print *,'!!! made. Could not get guess using either barnes' + print *,'!!! method or extrapolation method.' + print *,'!!! extrap_flag = ',extrap_flag + print *,'!!! barnes_flag = ',barnes_flag + print *,'!!! Storm number = ',ist,' ifh = ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + write (6,41) 0.0,0.0,0.0 + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 95 + endif + + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| Current fix & updated fix positions |' + print *,'-------------------------------------------------- ' + print *,'| In get_next_ges, current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',ist + print *,'| Return code from get_next_ges = ',ignret + print *,'| Storm Name = ',storm(ist)%tcv_storm_name + print *,'| Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + write (6,21) fixlat(ist,ifh) + write (6,23) 360.-fixlon(ist,ifh),fixlon(ist,ifh) + write (6,25) slatfg(ist,ifh+1) + write (6,27) 360.-slonfg(ist,ifh+1),slonfg(ist,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current fix lat is ',f7.2) + 23 format (' | Current fix lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') +c 41 format (' --- barnlon= ',f7.2,'W barnlat= ',f7.2) +c 43 format (' --- extraplon= ',f7.2,'W extraplat= ',f7.2) + + 41 format (' --- barnlon= ',f7.2,'E (',f7.2 + & ,'W) barnlat= ',f7.2) + 43 format (' --- extraplon= ',f7.2,'E (',f7.2 + & ,'W) extraplat= ',f7.2) + +c Now calculate the speed that the storm would have to move at in +c order to make it to the next forecast position. We will use +c this information in writing out the "gen_vitals" record, if this +c is requested. + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh) + & ,slonfg(ist,ifh+1),slatfg(ist,ifh+1),dist,degrees) + + ! convert distance from km to meters, then get speed in m/s. + + distm = dist * 1000. + stmspd = distm / dt + istmspd = int ((stmspd * 10) + 0.5) + + xincr = slonfg(ist,ifh+1) - fixlon(ist,ifh) + yincr = slatfg(ist,ifh+1) - fixlat(ist,ifh) + + if ( verb .ge. 3 ) then + print *,'iocheck, dist= ',dist,' distm= ',distm + print *,'iocheck, stmspd= ',stmspd,' istmspd= ',istmspd + print *,'iocheck, xincr= ',xincr,' yincr= ',yincr + endif + + if (xincr < 0.0 .and. slonfg(ist,ifh+1) < 30.0 .and. + & fixlon(ist,ifh) > 300.0) then + ! This means we have a storm moving east across the GM, and + ! so we are subtracting, for example, something like + ! 0.5 - 359.5, so redo xincr, but add 360 to slonfg first... + xincr = (slonfg(ist,ifh+1) + 360.0) - fixlon(ist,ifh) + else if (xincr > 300.0) then + ! This means we have a storm moving west across the GM, and + ! so we are subtracting, for example, something like + ! 359.5 - 0.5, so redo xincr, but add 360 to fixlon first... + xincr = slonfg(ist,ifh+1) - (fixlon(ist,ifh) + 360.0) + endif + + if (xincr == 0.0) then + if (yincr == 0.0) then + stmdir = 0.0 + else if (yincr > 0) then + stmdir = 360.0 + else if (yincr < 0) then + stmdir = 180.0 + endif + else if (xincr > 0.0) then + if (yincr == 0.0) then + stmdir = 90.0 + else + arct = atan(yincr/xincr) + stmdir = 90. - arct / dtr + endif + else if (xincr < 0.0) then + if (yincr == 0.0) then + stmdir = 270.0 + else + arct = atan(yincr/xincr) + stmdir = 270. - arct / dtr + endif + endif + + istmdir = int (stmdir + 0.5) + if (istmdir > 360) then + istmdir = 360 + else if (istmdir < 0) then + istmdir = 0 + endif + + if ( verb .ge. 3 ) then + print *,'iocheck, stmdir= ',stmdir,' istmdir= ',istmdir + endif + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine advect_tcvitals_from_hour0 (fixlon,fixlat,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. As of 11/2016, it is called only for the case in +c which we've got NetCDF data and no hour0 data, and so we want to +c simply take the TC Vitals data and advect the current position to +c a position at the next lead time. We can't use the code in +c subroutine get_next_ges because there are certain allocatable +c arrays in that subroutine that need to have been allocated first, +c and at this point prior to the first lead time in hour0, they +c haven't been allocated. +c +c INPUT: +c inctcv Index for storm number currently being processed +c ifh Forecast hour currently being processed +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c iatret Return code from this subroutine +c + USE def_vitals; USE trkrparms; USE tracked_parms + USE verbose_output; USE trig_vals; USE set_max_parms + USE gen_vitals + + type (trackstuff) trkrinfo + integer iatret,inctcv + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real ucomp,vcomp,xdist,ydist,ydeg,dt,extraplat + real cosfac + real dtkm +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c ------------------------------------------------------------------ +c Using the storm motion vector and storm translation speed as read +c from the TC Vitals card, do a simple linear extrapolation from the +c current observed (TC Vitals) position and advect the storm to a +c position at the next lead time. +c ------------------------------------------------------------------ + + iatret = 0 + + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(inctcv)%tcv_stdir == -99 .or. + & storm(inctcv)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In advect_tcvitals_from_hour0, at fcst hour= 0' + print *,'!!! either storm motion or storm speed = -99 on ' + print *,'!!! TCV card, ist= inctcv= ',inctcv,' ifh= ',ifh + print *,'!!! Storm name = ',storm(inctcv)%tcv_storm_name + print *,'!!! Storm ID = ',storm(inctcv)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(inctcv)%tcv_stdir + print *,'!!! storm motion speed= ',storm(inctcv)%tcv_stspd + print *,'... CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS ...' + print *,' ' + print *,'... Instead, we will simply use the current ' + print *,'... observed position from TC Vitals and hope that' + print *,'... it is close enough at the next lead time for ' + print *,'... the tracker to be able to still track it.' + print *,' ' + endif + extraplat = slatfg(inctcv,ifh) + extraplon = slonfg(inctcv,ifh) + else + ucomp = sin(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + vcomp = cos(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(inctcv,ifh) + ydeg + cosfac = cos(extraplat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(inctcv,ifh) + xdeg + endif + else + print *,' ' + print *,'!!! ERROR: In advect_tcvitals_from_hour0, the value of' + print *,' ifh is > 1, and this routine should only be called' + print *,' if ifh=1 (i.e., for hour0). STOPPING....' + print *,' ' + stop 95 + endif + + slatfg(inctcv,ifh+1) = extraplat + + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon >360 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon < 0 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + else + slonfg(inctcv,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| In advect_tcvitals_from_hour0, info on the ' + print *,'| positions for the current and next lead times ' + print *,'| follow: ' + print *,'-------------------------------------------------- ' + print *,'| current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',inctcv + print *,'| Return code from advect_tcvitals_from_hour0= ',iatret + print *,'| Storm Name = ',storm(inctcv)%tcv_storm_name + print *,'| Storm ID = ',storm(inctcv)%tcv_storm_id + write (6,420) gstorm(inctcv)%gv_gen_date + & ,gstorm(inctcv)%gv_gen_fhr + & ,gstorm(inctcv)%gv_gen_lat + & ,gstorm(inctcv)%gv_gen_latns,gstorm(inctcv)%gv_gen_lon + & ,gstorm(inctcv)%gv_gen_lonew,gstorm(inctcv)%gv_gen_type + write (6,21) fixlat(inctcv,ifh) + write (6,23) 360.-fixlon(inctcv,ifh),fixlon(inctcv,ifh) + write (6,25) slatfg(inctcv,ifh+1) + write (6,27) 360.-slonfg(inctcv,ifh+1),slonfg(inctcv,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current TC Vitals lat is ',f7.2) + 23 format (' | Current TC Vitals lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') + + + return + end +c +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getradii (xcenlon,xcenlat,imax,jmax,dx,dy,valid_pt + & ,cstormid,ifcsthr,vmaxwind,vradius,trkrinfo + & ,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) +c +c ABSTRACT: This subroutine looks through the wind data near an +c input storm center (fixlon,fixlat) and gets the radii of various +c surface winds in each of the 4 storm quadrants (NE,NW,SE,SW). +c The wind thresholds that are sought are gale force (34kt|17.5m/s), +c storm force (50kt|25.7m/s), and hurricane force (64kt|32.9m/s). +c This subroutine calls the Cray subroutine orders, which is a +c Cray-optimized sort routine. +c +c UPDATE (AUG 2001): The Cray subroutine orders was ported to the +c SP by NCEP personnel. On the SP version, some changes were +c apparently made so that the size of the arrays for calling +c arguments 2, 3 and 4 (iwork, dtemp and isortix in my calling +c routine) must be the same. This was not the case on the Crays, +c and this was causing the tracker to crash for cases far north +c on fine grids (GFDL 1/3 grid). +c +c UPDATE (AUG 2012): The call to the Cray subroutine orders was +c replaced with a call to qsort, which uses a quicksort sorting +c algorithm. While this is not the fastest sorting routine out +c there, we don't do a lot of sorting here, and qsort is simple +c and it is portable. +c +c UPDATE (April 2013): For the radii, we encountered a problem with +c radmax being too small. It was set at 650 km. Hurricane Sandy +c exceeded this in the models, so the values returned from getradii +c were close to the default radmax value of 650 km (350 nm), instead +c of much higher as they should have been. To fix it, we now use an +c iterative technique, where we start with radmax as a small value +c (450 km). If getradii returns a value for R34 in a quadrant that +c does not exceed 0.97*radmax, then that value is ok. If it does +c exceed 0.97*radmax, then we bump up radmax by 50 km and call +c getradii again, looking to diagnose radii only in those quadrants +c where the need_to_expand_r34 flag = 'n'. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c cstormid 3-character storm ATCF ID (e.g., 03L, 11E, etc) +c ifcsthr integer value for current forecast hour +c trkrinfo derived type containing various info on the storm +c need_to_expand_r34 1-character array that specifies which of the +c 4 quadrants still need to be expanded on this time +c through getradii in order to get an R34 value that is +c not right at the outermost boundary. +c vmaxwind max wind (in m/s) that was reported from the +c get_max_wind subroutine +c radmax input max radius (km) that will be used for this +c iteration of getradii. +c first_time_thru_getradii logical flag. It is used so that any +c checking for 50- or 64-kt radii is only done on the +c first time through getradii. Only the checking for +c 34-kt radii is done on multiple iterations. +c igrct integer that indicates what iteration of getradii this +c call is. +c +c OUTPUT: +c +c igrret return code from this subroutine +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c +c LOCAL: +c +c radmax the maximum radius to look for winds for the various +c thresholds. +c quadinfo This array contains the magnitude of the near-surface +c winds and the distance from the gridpoint to the fix +c position for each point in each quadrant that is within +c the maximum allowed radius, radmax. quadinfo is +c allocated within this subroutine, and is allocated as +c (quadrant, num_pts_in_quadrant, data_type), where +c data_type is either windspeed(1) or distance(2) from +c storm center to grid point. +c quadmax This array contains the max surface wind in each +c quadrant, plus the location of it and the distance from +c the storm center. This information is critical to +c identifying when this subroutine is malfunctioning. + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE level_parms + USE trkrparms + USE verbose_output + +c + type (trackstuff) trkrinfo +c + logical(1) valid_pt(imax,jmax) + logical(1) first_time_thru_getradii +c dimension iwork(257) + real, allocatable :: quadinfo(:,:,:),iwork(:) + real quadmax(4,4) + real exactdistnm,exactdistkm,radmax,degrees,cosarg + real rlonb,rlonc,rlatb,rlatc,vmaxwind + real pt_heading_rad,pt_heading,d + integer, allocatable :: isortix(:) + integer iwindix,ipoint,ifcsthr,igrct + integer quadct(4),vradius(3,4) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: dtemp(:) + real :: windthresh(3) = (/17.5,25.7,32.9/) + character cstormid*3 + character :: need_to_expand_r34(4)*1 + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *************************************************** ' + print *,' AT BEGINNING OF GETRADII, input radmax= ',radmax + print *,' *************************************************** ' + print *,' ' + print *,'xcenlon= ',xcenlon,' xcenlat= ',xcenlat + print *,'imax= ',imax,' jmax= ',jmax,' dx= ',dx,' dy= ',dy + endif + + igrret = 0 + +c ----------------------------------------------------------- +c PART 1: Define the maximum radius for which you'll search +c for the wind values, and then get the beginning and ending +c i and j points for that sub-region to search. Define this +c maximum radius (radmax) in terms of km. +c ----------------------------------------------------------- + +c radmax = 650.0 ! This value is in units of km. With April 2013 +c ! update, this is now defined in calling routine + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-A....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine getradii' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-B....' + stop 98 + endif + + igrret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmax is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmax/(dtk*dx))/cosfac) + numjpts = ceiling(radmax/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (ibeg < 1) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-C...' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jbeg < 1) jbeg = 1 + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in getradii calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Wind radii will not be calculated for this time.' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-D....' + stop 98 + endif + + igrret = 99 + return + endif + + if (iend > imax) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-E....' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getradii, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + +c ----------------------------------------------------------- +c PART 2: Within the area of grid points defined by jbeg, +c jend, ibeg and iend, (1) calculate all the wind speeds at +c each grid point, (2) calculate all of the distances from +c each grid point to the storm center, (3) assign each grid +c point to one of the 4 quadrants (NE,NW,SE,SW), (4) in each +c quadrant, sort the points, based on windspeed. +c ----------------------------------------------------------- + + jnum = jend - jbeg + 1 + inum = iend - ibeg + 1 +c numalloc = ((jnum * inum) / 2) + inum/2 + jnum/2 + numalloc = jnum * inum + inum/2 + jnum/2 + + if ( verb .ge. 3 ) then + print *,'in getradii, numalloc= ',numalloc,' radmax= ',radmax + endif + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + allocate (quadinfo(4,numalloc,2),stat=iqa) + + if (iqa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub getradii allocating quadinfo array.' + print *,'!!! iqa = ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-F....' + stop 98 + endif + + igrret = 94 + return + endif + + quadct = 0 + +c Calculate the distances and wind speeds at each grid point. If +c the distance is < radmax, include that wind info in the +c appropriate quadinfo array location for that quadrant. + + quadmax = 0.0 + + jloop: do j=jbeg,jend + iloop: do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point in question = ',i + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-G...' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub getradii' + print *,'!!! for a non-global grid. i= ',i + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-H....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + if (dist > radmax) cycle iloop + + if (valid_pt(ip,j)) then + + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + +cc print *,'i= ',i,' j= ',j,' dist= ',dist,' vmag= ',vmag + + ! Calculate the angle from the center point to this point + ! and then assign this point to the appropriate quadrant bin + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-xcenlon) * dtr + rlatb = xcenlat * dtr + d = degrees * dtr + +c write (6,59) 360.-xcenlon,xcenlat,360.-glon(ip),glat +c +c write (6,61) d/dtr,rlatc/dtr,360.-(rlonc/dtr),rlatb/dtr +c & ,360.-(rlonb/dtr),sin(rlatc),sin(rlatb),cos(d) +c & ,sin(d),cos(rlatb) +c +c +c 59 format (1x,'+++ gr, xcenlon= ',f8.3,'W xcenlat= ' +c & ,f8.3,' glon= ',f8.3,'W glat= ',f8.3) +c +c 61 format (1x,'+++ gr, d rlatc rlonc rlatb rlonb= ',5f9.4 +c & ,' sin(rlatc)= ',f8.6,' sin(rlatb)= ',f8.6 +c & ,' cos(d)= ',f8.6,' sin(d)= ',f8.6 +c & ,' cos(rlatb)= ',f8.6) + + if (d == 0.0) then + + pt_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d)) / + & (sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_heading_rad = acos(cosarg) + else + pt_heading_rad = 2*pi - acos(cosarg) + endif + + pt_heading = pt_heading_rad / dtr + + endif + + if (pt_heading >= 0.0 .and. pt_heading < 90.) then + ! NE quadrant + iq = 1 + else if (pt_heading >= 90.0 .and. pt_heading < 180.) then + ! SE quadrant + iq = 2 + else if (pt_heading >= 180.0 .and. pt_heading < 270.) then + ! SW quadrant + iq = 3 + else if (pt_heading >= 270.0 .and. pt_heading <= 360.) then + ! NW quadrant + iq = 4 + endif + +c write (6,73) xcenlat,360.-xcenlon,j,i,ip,glat(j) +c & ,360.-glon(ip),pt_heading,iq + + 73 format (1x,'+++ getradii clat clon: ',f6.2,' ',f7.2,'W',3i4 + & ,' plat plon: ',f6.2,' ',f7.2,'W Dir: ',f7.2 + & ,' Quad: ',i2) + + quadct(iq) = quadct(iq) + 1 + quadinfo(iq,quadct(iq),1) = vmag + quadinfo(iq,quadct(iq),2) = dist + if (vmag > quadmax(iq,4)) then + quadmax(iq,1) = glon(ip) + quadmax(iq,2) = glat(j) + quadmax(iq,3) = dist + quadmax(iq,4) = vmag + endif + + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After loop, quadct(1)= ',quadct(1),' quadct(2)= ' + & ,quadct(2) + print *,' quadct(3)= ',quadct(3),' quadct(4)= ' + & ,quadct(4) + print *,' ' + + write (6,110) cstormid,ifcsthr,'NE',quadmax(1,1),quadmax(1,2) + & ,quadmax(1,3)*0.539638,quadmax(1,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SE',quadmax(2,1),quadmax(2,2) + & ,quadmax(2,3)*0.539638,quadmax(2,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SW',quadmax(3,1),quadmax(3,2) + & ,quadmax(3,3)*0.539638,quadmax(3,4)*1.9427 + write (6,110) cstormid,ifcsthr,'NW',quadmax(4,1),quadmax(4,2) + & ,quadmax(4,3)*0.539638,quadmax(4,4)*1.9427 + print *,' ' + + 110 format (' quadmax: ',a3,1x,i3.3,1x,a2,1x,' lon: ',f6.2,'E',1x + & ,' lat: ',f6.2,' radius: ',f7.2,' nm',2x,' vmag: ' + & ,f6.2,' kts') + endif + +c Now go through each quadrant and put the wind speed distance info +c into a temporary array (dtemp), sort that array, and then scan +c through that array to find the various thresholds. + + quadrantloop: do k=1,4 + + if (need_to_expand_r34(k) == 'y') then + print *,'---> R34 search underway for quadrant ',k + & ,' radmax= ',radmax + continue + else + print *,'+ R34 okay for quadrant ',k,'... skipping...' + cycle quadrantloop + endif + + if (allocated(isortix)) deallocate (isortix) + if (allocated(dtemp)) deallocate (dtemp) + if (allocated(iwork)) deallocate (iwork) + allocate (isortix(quadct(k)),stat=iisa) + allocate (dtemp(quadct(k)),stat=idta) + allocate (iwork(quadct(k)),stat=iwa) + if (iisa /= 0 .or. idta /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii allocating isortix, dtemp' + print *,'!!! or iwork array for quadrant= ',k + print *,'!!! iisa = ',iisa,' idta= ',idta,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-I....' + stop 98 + endif + + itret = 94 + return + endif + +c ------------------- + + do m=1,quadct(k) + dtemp(m) = quadinfo(k,m,2) + enddo + + imode = 2 + isortix = 0 + + call qsort (dtemp,isortix,quadct(k)) + +ccccc call orders (imode,iwork,dtemp,isortix,quadct(k),1,8,1) +cccc call orders_4byte (imode,iwork,dtemp,isortix +cccc & ,quadct(k),1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' +c ************************************************************** +c--- mf 20100609 +c CAUSE OF SEG FAULT!!!!!!!! -- not sure still an issue if dtemp +c properly allocated +c + !print *,' dtemp(isortix(1)) = ',dtemp(isortix(1)) + print *,' dtemp(isortix(quadct(k)))= ' + & ,dtemp(isortix(quadct(k))) + print *,' isortix(1) = ',isortix(1) + print *,' isortix(quadct(k)) = ',isortix(quadct(k)) + endif + +c ! Uncomment these next lines to see a listing in the output of +c ! all wind values & distances in this quadrant less than radmax +c do iqq = 1,quadct(k) +c print *,' iqq= ',iqq,' vmag= ',quadinfo(k,isortix(iqq),1) +c & ,' dist= ',quadinfo(k,isortix(iqq),2) +c enddo + +c ------------------- + + if (quadct(k) < 2) then ! not enough members in array + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GETRADII, NOT ENOUGH MEMBERS IN ARRAY FOR' + print *,'!!! QUADRANT #',k,' .... # members = quadct(k)= ' + & ,quadct(k) + print *,'!!! SETTING ALL VRADII = 0 for quadrant = ',k + endif + + vradius(1,k) = 0 + vradius(2,k) = 0 + vradius(3,k) = 0 + cycle quadrantloop + endif + +c Within this quadrant, go through the sorted array of wind +c magnitudes and compare those wind values against the set +c wind thresholds to get the wind radii. The array has +c been sorted by distance from the storm center in order of +c closest (ipoint=1) to farthest (ipoint=quadct(k)). We +c analyze these wind values by starting at the farthest +c point and moving inward until we hit a point that has a +c wind value of at least 34-knot winds (17.5 m/s). When +c we find that point, we interpolate between that point and +c the next farthest out point to get the distance that would +c be for the exact 17.5 m/s value. We then continue searching +c through the wind values down closer to the storm center to +c see if we can find values for the 50- and 64-knot winds. + + iwindix = 1 + ipoint = quadct(k) + 1 + +c print *,'drp: quad= ',k,' quadct= ',quadct(k) + + threshloop: do while (iwindix <= 3 .and. ipoint > 1) + + if (iwindix > 1) then + if (first_time_thru_getradii) then + + ! We are only doing the wind radii for 50 and 64 kts on + ! the first time through subroutine getradii (we only + ! need to do the multiple call iterations for 34 kts). + ! + ! Make sure vmax for this lead time exceeds the radii + ! threshold being diagnosed. The check below avoids, + ! for example, reporting 50-kt wind radii when the max + ! wind diagnosed was only 44 kts. This can happen since + ! the radius for searching for radii is larger than the + ! radius for searching for the max wind. + if (vmaxwind >= windthresh(iwindix)) then + if (verb >= 3) then +c print *,' ' +c print *,' +++ vmaxwind of ',vmaxwind,' m/s exceeds' +c print *,' +++ threshold of ',windthresh(iwindix) +c print *,' +++ (m/s), so radii checking will continue' +c print *,' +++ for this threshold.' +c print *,' +++ igrct= ',igrct,' ipoint= ',ipoint +c & ,' iwindix= ',iwindix + continue + endif + continue + else + if (verb >= 3) then + print *,' ' + print *,' --- vmaxwind of ',vmaxwind,' m/s does NOT' + print *,' - - exceed threshold of ' + & ,windthresh(iwindix) + print *,' - - (m/s), so radii checking will NOT be ' + print *,' - - performed for this threshold.' + endif + iwindix = iwindix + 1 + cycle threshloop + endif + else + iwindix = iwindix + 1 + cycle threshloop + endif + endif + + ipoint = ipoint - 1 + + if (quadinfo(k,isortix(ipoint),1) < windthresh(iwindix)) then + cycle threshloop + else + if (ipoint == quadct(k)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In getradii, a max wind radius was' + print *,'!!! found at the maximum radius checked, so ' + print *,'!!! you may want to make sure that you are' + print *,'!!! checking at a far enough distance from ' + print *,'!!! the fix position, that is, you may want to' + print *,'!!! increase the value of radmax in subroutine' + print *,'!!! getradii. Currently, radmax (km) = ',radmax + print *,'!!! iwindix = ',iwindix,' quadrant= ',k + endif + + vradius(iwindix,k) = int( ((quadinfo(k,isortix(ipoint),2) + & * 0.5396) / 5.0) + 0.5) * 5 + else + +c Interpolate between the 2 closest distances to each wind +c threshold to get "exact" distance to that wind threshold +c radius, convert from km to nm, and then round to the +c nearest 5 nm (since TPC uses this precision). +c 7/23/98 UPDATE: Jim Gross has asked that values not be +c rounded to the nearest 5 nm, but rather only to the +c nearest 1 nm. + + exactdistkm = quadinfo(k,isortix(ipoint),2) + + & ( (quadinfo(k,isortix(ipoint),1) - windthresh(iwindix)) / + & (quadinfo(k,isortix(ipoint),1) - + & quadinfo(k,isortix(ipoint+1),1)) * + & ( (quadinfo(k,isortix(ipoint+1),2) - + & quadinfo(k,isortix(ipoint),2)) ) ) + + exactdistnm = exactdistkm * 0.5396 ! Convert km to nm + vradius(iwindix,k) = int(exactdistnm + 0.5) + +cc vradius(iwindix,k) = int( (exactdistnm / 5.0) + 0.5) * 5 + + + if ( verb .ge. 3 ) then + print *,'iwindix= ',iwindix,' exactdistnm = ' + & ,exactdistnm + print *,'vradius(iwindix,k) =',vradius(iwindix,k) + endif + + endif + +c The possibility exists, especially for coarse output +c grids, that there could be a jump over more than 1 wind- +c thresh category when going from 1 grid point to the next, so +c we need to account for this. For example, if 1 point has +c vmag = 15 m/s and the next point closer in has vmag = 28 +c m/s, then between those 2 points you have the thresholds +c for gale force AND storm force winds, so to be safe, we +c actually need to add 1 to ipoint and re-check the current +c point, if the wind value at that point is found to be +c greater than a wind threshold value (which it has if you've +c gotten to this point in threshloop). + + ipoint = ipoint + 1 + + iwindix = iwindix + 1 + + endif + + enddo threshloop + + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (idta /= 0 .or. iisa /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating isortix or' + print *,'!!! dtemp or work for quadrant= ',k + print *,'!!! idta= ',idta,' iisa= ',iisa,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-J....' + stop 98 + endif + + itret = 94 + return + endif + + enddo quadrantloop + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating quadinfo array.' + print *,'!!! iqa= ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-K....' + stop 98 + endif + + itret = 94 + return + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_max_wind (xcenlon,xcenlat,imax,jmax,dx,dy + & ,valid_pt,levsfc,vmax,trkrinfo,rmax,igmwret) +c +c ABSTRACT: This subroutine looks for the maximum near-surface wind +c near the storm center. This subroutine is only concerned with the +c value of the max wind, NOT where it's located radially with +c respect to the center. The value that's returned in vmax is the +c max wind speed in m/s, which are the units the data are stored in. +c However, when the max wind values are output in output_atcf, they +c will be converted from m/s to knots. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c levsfc integer holding the value of the array member that holds +c the near-surface winds in the u and v arrays (at orig +c writing, it's = 4). +c +c OUTPUT: +c +c vmax value of maximum near-surface wind near the storm ctr +c rmax radius of max winds +c igmwret return code from this subroutine +c +c LOCAL: +c +c radmaxwind the maximum radius to look for a max wind near the +c storm center. You have to allow this to be bigger for +c model grids with coarse resolution (ECMWF 2.5 degree). + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real radmaxwind,degrees,dx,dy,rmax + logical(1) valid_pt(imax,jmax) +c + igmwret = 0 + rmax = -99.0 + + if ((dx+dy)/2. <= 1.25) then + if ((dx+dy)/2. <= 0.25) then + radmaxwind = 300.0 + else + radmaxwind = 300.0 + endif + else + radmaxwind = 500.0 + endif + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmaxwind is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmaxwind/(dtk*dx))/cosfac) + numjpts = ceiling(radmaxwind/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_max_wind calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Value of vmax will be set to 0 for this time.' + endif + + vmax = 0.0 + igmwret = 99 + return + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + + print *,' ' + print *,'In get_max_wind, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + + vmax = 0.0 + do j=jbeg,jend + do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point = ',i + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + + if (dist > radmaxwind) cycle + + if (valid_pt(ip,j)) then + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + if (vmag > vmax) then + vmax = vmag + rmax = dist * 0.539638 ! convert from km to nm + endif + endif + + enddo + enddo + + if ( verb .ge. 3 ) then + print *,'At end of get_max_wind, vmax= ',vmax,' rmax= ',rmax + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine fixcenter (clon,clat,ist,ifh,calcparm,geslon,geslat + & ,inp,stderr,fixlon,fixlat,xvalues,maxstorm,ifret) +c +c ABSTRACT: This subroutine loops through the different parameters +c for the input storm number (ist) and calculates the +c center position of the storm by taking an average of +c the center positions obtained for those parameters. +c First we check to see which parameters are within a +c max error range (errmax), and we discard those that are +c not within that range. Of the remaining parms, we get +c a mean position, and then we re-calculate the position +c by giving more weight to those estimates that are closer +c to this mean first-guess position estimate. +c +c INPUT: +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c geslon Initial guess longitude for this storm at this fcst hour +c geslat Initial guess latitude for this storm at this fcst hour +c inp contains the input date and model number information +c xvalues The actual max or min data values for each parameter +c maxstorm max # of storms to be handled in this run +c +c INPUT/OUTPUT: +c stderr Standard deviation of the position "error" of the parms +c relative to the guess storm position. As long as the +c distance of a parm center to the guess center is <= +c errpmax, it is included in the std dev calculation. +c +c OUTPUT: +c fixlon Best approximation of storm center's longitude +c fixlat Best approximation of storm center's latitude +c ifret Return code from this subroutine +c +c LOCAL: +c storm Contains tcvitals info for the storms (def_vitals) +c trkerr_avg Sum/avg of the track errors for all parms for this +c fcst hour, regardless of whether or not the error was +c > errmax. It's used for getting the std deviation of +c the position error for this forecast time, to be used +c as part of the errmax calculation for the next fcst +c time. +c iclose Number of parameters whose position estimates are +c found to be within a distance errmax of the guess pos +c wtpos The weight given to each position estimate. It's +c based on the distance from the average position. +c errdist The "error" of the parameter center position relative +c to the storm's guess position. +c avgerr Average "error" of the parameter center positions +c relative to the storm's guess position. +c use4next Logical; If a parm center has been calculated but its +c distance from the guess position is > errmax, we don't +c use this center in calculating the new guess position, +c however we will use this position in calculating the +c standard deviation of the current time's guess +c positions, to be used in calculating the new errmax +c for the next forecast time. So in this subroutine, +c calcparm may be set to FALSE if errdist > errmax, but +c use4next will not be set to FALSE (Actually, it is +c only set to FALSE if errdist > errpmax, which is +c defined in error_parms and is roughly 600km). +c stderr_close Standard deviation of position errors for parms that +c have center estimates that are within a distance +c errmax of the guess position. +c clon_fguess These are the first-guess mean position estimates, +c clat_fguess which are the means of the position estimates that +c are within a distance errmax. These first-guess mean +c positions will be refined by giving more weight to +c individual parameter estimates that are closer to +c this first-guess mean position. +c dist_from_mean Contains the "error" distance of each parameter +c from the first-guess mean position (clon_fguess, +c clat_fguess). NOTE: If a parameter is not within +c a distance errmax of the guess position for this +c time (geslon,geslat), then there will be NO +c dist_from_mean calculated for that parm. +c + USE error_parms; USE set_max_parms; USE inparms; USE def_vitals + USE atcf; USE gen_vitals; USE tracked_parms + USE verbose_output + + type (datecard) inp + + real clon(maxstorm,maxtime,maxtp),temp_clon(maxtp) + real clat(maxstorm,maxtime,maxtp),temp_clat(maxtp) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real trkerr(maxtp),errdist(maxtp),xvalues(maxtp) + real stderr(maxstorm,maxtime),devia(maxtp),wtpos(maxtp) + real dist_from_mean(maxtp) + real degrees,errtmp + integer gt345_ct,lt15_ct + logical(1) calcparm(maxtp,maxstorm),use4next(maxtp) + character charparm(maxtp)*8,charmaxmin(maxtp)*8 +c + data charparm /'zeta 850','zeta 700','circ 850','NOT USED' + & ,'circ 700','NOT USED',' gph 850',' gph 700',' MSLP' + & ,'circ sfc','zeta sfc',' thk 5-8',' thk 2-5',' thk 2-8'/ + data charmaxmin /' Max ',' Max ',' Min ','NOT USED' + & ,' Min ','NOT USED',' Min ',' Min ',' Min ' + & ,' Min ',' Max ',' Max ',' Max ',' Max '/ +c + ifret=0 +c +c We need to judge whether each parameter position is reasonable, +c so we'll check to make sure that the dist from each parameter's +c estimate to the guess position is less than a maximum allowable +c error. If it's the first forecast time, use the initial error max +c (defined as errinit in error_parms) as errmax. Otherwise, the +c max error criterion is that the distance error must not exceed 3 +c times the previous forecast time's standard deviation (after a +c small growth factor has been applied). +c UPDATE 3/5/98: During testing, it was found that just using the +c previous time's stdev made errmax too "jumpy" (i.e., at vt=48h, +c errmax could = 380, and then at vt=54h, errmax could jump down +c to 190, so we've changed it so that it uses an average of the +c stdev's from the 3 previous forecast times to maintain some +c continuity between successive forecast times). +c + if (ifh == 1) then + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR' ) then + errmax = err_gfs_init + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errmax = err_ecm_max + errinit = err_ecm_max + else + errmax = err_reg_init + errinit = err_reg_init + endif + else + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR') then + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errinit = err_ecm_max + else + errinit = err_reg_max + endif + + if (ifh >= 4) then + xavg_stderr = (stderr(ist,ifh-3) + stderr(ist,ifh-2) + & + stderr(ist,ifh-1)) / 3.0 + else if (ifh == 3) then + xavg_stderr = (stderr(ist,ifh-2) + stderr(ist,ifh-1)) / 2.0 + else if (ifh == 2) then + xavg_stderr = stderr(ist,ifh-1) + endif + +c The following errmax statement was replaced by the ensuing 4 +c lines due to a compiler bug on some other platforms: +c errmax = amin1(amax1(3.0*xavg_stderr*errpgro,errinit) +c & ,errpmax) + + errtmp = 3.0*xavg_stderr*errpgro + errmax = max(errtmp,errinit) + errtmp = errpmax + errmax = min(errmax,errtmp) + + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (ifh > 1) then + print '(a42,f8.2,a15,f8.2)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = ' + & ,stderr(ist,ifh-1),' xavg_stderr= ',xavg_stderr + else + print '(a45,a18)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = N/A' + & ,' xavg_stderr= N/A' + endif + print *,'At beg of fixcenter, errpgro = ',errpgro + print *,'At beg of fixcenter, errinit = ',errinit + print *,'At beg of fixcenter, errpmax = ',errpmax + print *,'At beg of fixcenter, ifh= ',ifh,' errmax= ',errmax + endif + + trkerr_avg = 0.0 + iclose = 0; itot4next = 0 + clonsum = 0.0; clatsum = 0.0 + errdist = 0.0 + use4next = .FALSE. + gt345_ct = 0 + lt15_ct = 0 + +c For each parm, check to see if the estimated center is within +c distance errmax of the guess center. If it's within errmax, +c then use that parm for locating the center. If it's NOT +c within errmax, but IS within errpmax, then we still use this +c in calculating the standard deviation of the parameters for +c helping to determine the errmax for the next forecast hour. + +c OLD NOTE: For calculating the std dev to be used for the next +c OLD forecast hour, do NOT use vmag 850, vmag 700 or vmag sfc, since +c OLD those parms are always guaranteed to be within a short range of +c OLD the guess, due to the nature of the algorithm (see subroutine +c OLD get_uv_center for further details on that). + + do ip=1,maxtp + + if (ip == 4 .or. ip == 6) then ! Parms 4 & 6 not defined. + calcparm(ip,ist) = .FALSE. + cycle + endif + if (calcparm(ip,ist)) then + call calcdist (geslon,geslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + errdist(ip) = dist + if (dist <= errpmax) then + use4next(ip) = .TRUE. + trkerr_avg = trkerr_avg + dist + itot4next = itot4next + 1 + endif + if (dist <= errmax) then + iclose = iclose + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 + endif + clonsum = clonsum + clon(ist,ifh,ip) + clatsum = clatsum + clat(ist,ifh,ip) + else + calcparm(ip,ist) = .FALSE. + endif + endif + + enddo + + if (iclose > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (clonsum) + clon_fguess = (clonsum + (360.*float(lt15_ct)))/ iclose + else + clon_fguess = clonsum / float(iclose) + endif + if (clon_fguess >= 360.0) then + clon_fguess = clon_fguess - 360. + endif + clat_fguess = clatsum / float(iclose) + endif + +c Print out a table listing of the locations of the fixes for +c the individual parameters. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'--------------------------------------------------' + write (6,95) 'Individual fixes follow..., fhr= ',ifhours(ifh) + & ,ifclockmins(ifh),' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + write (6,97) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,'Model name = ',atcfname + print *,'Values of -99.99 indicate that a fix was unable to be' + print *,'made for that paramater. Parameters 4 & 6 are not' + print *,'used. Vorticity data values are scaled by 1e5.' + print *,'Circulation data values are scaled by 1e-6.' + print *,'errdist is the distance that the position estimate is' + print *,'from the guess position for this time. MSLP value ' + print *,'here may differ from that in the atcfunix file since ' + print *,'the one here is that derived from the area-averaged ' + print *,'barnes analysis, while that in the atcfunix file is ' + print *,'from a specific gridpoint.' + write (6,21) geslon,360.-geslon,geslat + write (6,*) ' ' + write (6,23) + write (6,25) + endif + + if (geslat > 0.0) then + charmaxmin(1) = ' Max ' + charmaxmin(2) = ' Max ' + charmaxmin(3) = ' Max ' + charmaxmin(5) = ' Max ' + charmaxmin(10) = ' Max ' + charmaxmin(11) = ' Max ' + else + charmaxmin(1) = ' Min ' + charmaxmin(2) = ' Min ' + charmaxmin(3) = ' Min ' + charmaxmin(5) = ' Min ' + charmaxmin(10) = ' Min ' + charmaxmin(11) = ' Min ' + endif + + do ip=1,maxtp + if (ip == 1 .or. ip == 2 .or. ip == 11) then + ! This IF block allows vorticity values to be + ! written out and scaled up by 1e5 ... + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + elseif (ip == 3 .or. ip == 5 .or. ip == 10) then + ! This IF block allows circulation values to be + ! written out and scaled down by 1e-6 ... + + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + else + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + endif + enddo + + 21 format (' Guess location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 23 format (' parm# parm Max/Min Lon_fix(E) Lon_fix(W)' + & ,' Lat_fix Max/Min_value calcparm errdist(km)') + 25 format (' ----- ---- ------- ---------- ----------' + & ,' ------- ------------- -------- ----------') + 27 format (2x,i2,4x,a8,2x,a8,3x,f7.2,5x,f7.2,4x,f7.2,7x,f9.2 + & ,6x,L2,7x,f7.2) + 95 format (1x,a33,1x,i4,':',i2.2,a2,a4,a1,a9) + 97 format (' Gen ID (if available): ',i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3) + + +c If number of parameter centers close enough (iclose) > 0, then +c calculate the center by taking an average of all the parameter +c center positions that are within distance errmax from the guess +c position (geslon,geslat). Get a first-guess mean position, and +c then re-calculate the position estimate by giving more weight +c to those positions that are closer to the first-guess mean +c position. + + dist_from_mean = 0.0 + + if (iclose > 0) then + +c Get distances from first-guess mean position.... + + do ip=1,maxtp + if (calcparm(ip,ist)) then + call calcdist (clon_fguess,clat_fguess,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + dist_from_mean(ip) = dist + endif + enddo + +c Get the mean distance of each parameter estimate from +c the first-guess mean position + + call avgcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,iaret) + + if (iaret == 0) then + + call stdevcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,stderr_close,isret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After stdevcalc, xmn_dist_from_mean= ' + & ,xmn_dist_from_mean,' stderr_close= ' + & ,stderr_close,' isret= ',isret + endif + + endif + if (iaret /= 0 .or. isret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER -- Error occurred in either' + print *,'!!! avgcalc or stdevcalc. Storm number = ',ist + print *,'!!! RCC from avgcalc = ',iaret + print *,'!!! RCC from stdevcalc = ',isret + print *,'!!! Center fix will NOT be made, and processing' + print *,'!!! for this storm is ending. The probable cause' + print *,'!!! is that no calcparms were valid for this storm' + print *,'!!! at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + if (calcparm(1,ist) .or. calcparm(2,ist) .or. calcparm(7,ist) + & .or. calcparm(8,ist) .or. calcparm(9,ist) + & .or. calcparm(11,ist) .or. calcparm(3,ist) + & .or. calcparm(10,ist) .or. calcparm(5,ist) + & .or. calcparm(12,ist) .or. calcparm(13,ist) + & .or. calcparm(14,ist)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In fixcenter, STOPPING PROCESSING for this' + print *,'!!! storm. The reason is that none of the fix' + print *,'!!! locations for parms z850, z700, zeta 850,' + print *,'!!! zeta 700, MSLP, wcirc_850, wcirc_700, ' + print *,'!!! wcirc_sfc, sfc zeta or the various levels ' + print *,'!!! of thicknesses were within a ' + print *,'!!! reasonable distance of the guess location.' + print *,'!!! ist= ',ist,' ifh= ',ifh + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'!!! Forecast hour: ',i4,':',i2.2) + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now re-calculate the mean position by giving more weight +c to those position estimates that are closer to the first +c guess mean position. Note that if stderr_close < 5.0, we +c force it to be 5.0; we do this to avoid getting very +c large numbers for devia values, which could make the +c weights (wtpos) equal to 0. This occurred during testing +c when only 2 parameters were valid, and so, of course, the +c standard deviation from the mean of those 2 parameters +c was close to 0, which gave devia values around 6000, and +c then wtpos values of 0, leading to a divide by 0 crash +c later on in subroutine wtavrg. + + kprm=0 + + if (stderr_close > 0.0) then + if (stderr_close < 5.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Since stderr_close had a value less than' + print *,'5, stderr_close has been forced to be equal' + print *,'to 5 in order to avoid dividing by zero later' + print *,'on in subroutine wtavrg.' + endif + + stderr_close = 5.0 + endif + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + devia(kprm) = dist_from_mean(ip) / stderr_close + wtpos(kprm) = exp(-devia(kprm)/3.) + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + + if ( verb .ge. 3 ) then + write (6,113) ip,kprm,dist_from_mean(ip),devia(kprm) + & ,wtpos(kprm),temp_clon(kprm) + & ,360.-temp_clon(kprm),temp_clat(kprm) + endif + + endif + enddo + 113 format (1x,'ip= ',i2,' kprm= ',i2,' dist_from_mean= ',f7.3 + & ,' devia= ',f7.3,' wtpos= ',f8.5,2x,3(2x,f7.2)) + else +c +c This next if statement is for the case in which only 1 +c parameter is valid, for which the stderr_close will = 0 +c (obviously), but as long as we have 1 valid parameter, +c continue processing, and set the weight for that parm = 1. +c The else portion is for the case in which stderr_close +c = 0 with NO parms being close. +c + if (iclose == 1) then + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + wtpos(kprm) = 1 + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + endif + enddo + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, stderr_close not > 0' + print *,'!!! stderr_close = ',stderr_close + print *,'!!! The probable cause is that no calcparms were' + print *,'!!! valid for this storm at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + endif +c + if (kprm > 0) then + call wtavrg_lon (temp_clon,wtpos,kprm,fixlon(ist,ifh),iwtret1) + call wtavrg (temp_clat,wtpos,kprm,fixlat(ist,ifh),iwtret2) + if (iwtret1 == 0 .and. iwtret2 == 0) then + if (verb .ge. 3) then + print *,' ' + write (6,173) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + 173 format ('At end of fixcenter: ',a4,' fhr= ',i4,':',i2.2 + & ,' Fix position= ',f7.2,'E (',f6.2,'W)',2x,f7.2) + print *,' ' + endif + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER in call to wtavrg.' + print *,'!!! Return Codes from wtavrg calls follow: ' + print *,'!!! RCC from wtavrg for long fix: ',iwtret1 + print *,'!!! RCC from wtavrg for lat fix: ',iwtret2 + print *,'!!! This means a divide by zero would have ' + print *,'!!! been attempted, which means that the ' + print *,'!!! weights in wtpos are not > 0. Check in' + print *,'!!! subroutine fixcenter where devia values' + print *,'!!! are calculated to see if something is ' + print *,'!!! wrong there. Values of wtpos array follow:' + print *,'!!! ',wtpos + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + print *,' ' + endif + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, kprm NOT > 0' + print *,'!!! This means that, for whatever reason, the ' + print *,'!!! calcparm logical flag was set to .FALSE. for' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: IN FIXCENTER, No storms are within errmax ' + print *,'!!! OR the calcparm logical flag was set to .FALSE. ' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now calculate the average error of all the parms that are within +c a radius errpmax (defined in error_parms, ~600km), and the std +c dev of those errors. This standard deviation will be used in +c calculating the maximum allowable error for the next forecast +c time. + + if (itot4next > 0 .and. ifret /= 95) then + trkerr_avg = trkerr_avg / float(itot4next) + call stdevcalc (errdist,maxtp,use4next,trkerr_avg + & ,stderr(ist,ifh),isret) + if (isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in FIXCENTER calculating std deviation ' + print *,'!!! for use in next forecast hours errmax.' + print *,'!!! ist= ',ist,' ifh= ',ifh,' itot4next= ' + & ,itot4next + endif + + ifret = 95 + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine avgcalc (xdat,kmax,valid,xavg,iaret) +c +c ABSTRACT: This subroutine just calculates a straight average of +c the parameters in the input array (xdat). The logical array +c (valid) indicates whether or not to include a particular array +c member or not in the calculation. + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) +c + iaret = 0 +c + xsum = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + xsum = xsum + xdat(i) + ict = ict + 1 + endif + enddo +c + if (ict > 0) then + xavg = xsum / float(ict) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in avgcalc, ict NOT > 0' + endif + + xavg = xdat(1) + iaret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg (xdat,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xdat) using the input weights +c in the input array (wt). It is used to calculate the center lat +c and lon fix positions. +c + USE verbose_output + + real xdat(kmax),wt(kmax) +c + iwtret = 0 +c + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xdat(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg, wtot NOT > 0' + endif + + iwtret = 95 + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg_lon (xlon,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xlon) using the input weights +c in the input array (wt). This subroutine is specifically used +c to find the center lon fix positions. It contains code to +c account for wrapping around the Greenwich Meridian. +c + + USE verbose_output + + real xlon(kmax),wt(kmax) + integer gt345_ct,lt15_ct +c + iwtret = 0 + gt345_ct = 0 + lt15_ct = 0 + +c First check to see if we have lons that are both to the left +c and the right of the greenwich meridian + + do i = 1,kmax + if (xlon(i) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (xlon(i) < 15.) then + lt15_ct = lt15_ct + 1 + endif + enddo + + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some lons that are in the 300's (west of the GM), and + ! some that are in the 0's (east of the GM). We need to + ! standardize these if we want to get a meaningful average. + do i = 1,kmax + if (xlon(i) < 15.) then + xlon(i) = xlon(i) + 360.0 + endif + enddo + endif + + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xlon(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg_lon, wtot NOT > 0' + endif + + iwtret = 95 + endif + + if (xwtavg >= 360.0) then + xwtavg = xwtavg - 360.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine stdevcalc (xdat,kmax,valid,xavg,stdx,isret) + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) + + isret = 0 + + stdx = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + stdx = stdx + (xdat(i) - xavg)**2 + ict = ict + 1 + endif + enddo + + if (ict > 0) then + stdx = sqrt(stdx/float(ict)) + if (stdx == 0.0) then +c This can happen if you have just 2 points; The mean position +c will be exactly in the middle of the 2 points and so the +c standard deviation around that mean point will be 0. And +c since the calling routine will quit if the returned standard +c deviation is 0, we must force it to be 1 so the program +c continues running. Theoretically, it could also happen with +c 3 or more points, but the likelihood of the distances working +c out to exactly equidistant for 3 points is not that good. + stdx = 1.0 + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in stdevcalc, ict NOT > 0' + endif + + isret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,fxval,trkrinfo + & ,cmodel_type,maxmin,igwcret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the wind circulation near the storm center. This center fix is +c done differently than for the other parms. With this fix, +c we limit the area that is searched. This subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the +c original guess position for this lead time and the 5 other parm +c fixes that have already been made for this lead time. That +c modified guess position is passed into this subroutine as uvgeslon +c and uvgeslat, and that's where the searching for the wind +c circulation is centered. +c +c This subroutine works by converting the winds to Vt and Vr at +c each grid point evaluated, relative to each candidate center point +c that is being evaluated at the time in the loop. We then compute +c the circulation at each of 24 azimuths surrounding the storm +c center, where circulation = Vt * (length of a 1/24 arc, in meters) +c This process is repeated for 7 successive radii and the results +c are summed up over all radii, approximating a solid disk +c circulation. The point at which the circulation is maximized +c (NHEM) or minimized (SHEM) is the center of circulation. +c +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c + + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + character(*) cmodel_type,maxmin + integer, parameter :: numdist=7,numazim=24 + integer imax,jmax,ist,level,igwcret,icvpret,idist,iazim + real rdist(numdist),vr(numazim,numdist),vt(numazim,numdist) + real vt_mean(numdist),circul_band(numdist) + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + real rads,ri,uvgeslon,uvgeslat,dx,dy,ctlon,ctlat,fxval + real temp_grid_minlon,temp_guesslon,rlatt,rlont,bear + real targlon,targlat,xintrp_u,xintrp_v,vt_azim_sum,degrees + real circ_diff,circ_diff_sum,hemisphere,wind_mag_ctr,dist + real xmin_circ_diff_mean,xmax_circ_diff_mean,tlon,tlat + real dell,fmax,fmin,grid_buffer,circ_diff_mean + real circumference,arclength + real circul_disk,xmax_circul_disk,xmin_circul_disk + integer ibiret1,ibiret2,igvtret,azimuth_ct,igiret,npts + integer igibret + integer circ_diff_ct,ir,nhalf,bskip1,bskip2,iskip,nlev + integer ilonfix,jlatfix,ibeg,iend,jbeg,jend,i,j,k,iix,jix + logical(1) cflag, valid_pt(imax,jmax) + +c---------------- +c + + print *,' ' + print *,'top of get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' cmodel_type= ',cmodel_type + print *,' maxmin= ',maxmin + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' cflag= ',cflag + print *,' ctlon= ',ctlon,' ctlat= ',ctlat + print *,' fxval= ',fxval + print *,' igwcret= ',igwcret + + igwcret = 0 + + grid_maxlat = glatmax + grid_minlat = glatmin + grid_maxlon = glonmax + grid_minlon = glonmin + + rads = rads_wind_circ + ri = ri_wind_circ + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of get_wind_circulation, rads= ',rads + & ,' ri= ',ri,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+15; fmin = 1.0e+15 + ctlon = 0.0; ctlat = 0.0 + +c Distances checked and the radial intervals are a function of +c the grid resolution.... + + if (dell > 0.50) then + rdist(1) = 50. + rdist(2) = 85. + rdist(3) = 120. + rdist(4) = 155. + rdist(5) = 190. + rdist(6) = 225. + rdist(7) = 260. + else + rdist(1) = 35. + rdist(2) = 65. + rdist(3) = 95. + rdist(4) = 125. + rdist(5) = 155. + rdist(6) = 185. + rdist(7) = 215. + endif + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + print *,' in get_wind_circulation, nlev= ',nlev + + if (uvgeslat >= 0.0) then + hemisphere = 1.0 + else + hemisphere = -1.0 + endif + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend + & ,igibret) + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (uvgeslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = uvgeslon - 360. + else + temp_guesslon = uvgeslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = uvgeslon + endif + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + +c For the wind circulation analysis, we will want to speed things +c up for finer resolution grids. We can do this by skipping some +c of the points in the wind circulation analysis. + + if (dell > 0.20) then + bskip1 = 1 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 3 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 5 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 8 + bskip2 = 3 + else if (dell <= 0.03) then + bskip1 = 10 + bskip2 = 4 + endif + +c bskip1 = 1 +c bskip2 = 1 + + jix = 0 + +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to first loop, ' + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop1: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = uvgeslat + dell*float(j) + + iix = 0 + + iloop1: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop1 + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT from call in ' + print *,'!!! get_wind_circulation: icvpret= ',icvpret + endif + cycle iloop1 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist,degrees) + if (dist .gt. rads) cycle iloop1 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop1: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop1: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + ! These calls to bilin_int_uneven pass a variable "level" + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop1 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop1 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop1 +cc and radiusloop1). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,' ' +cc print *,'1st run, wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'1st run, ir= ',ir,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +c print *,'1st run, circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'1st run, circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'1st run, xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif + + enddo iloop1 + + enddo jloop1 + + if (uvgeslat >= 0.0) then + write (6,61) 360.-ctlon,ctlat,xmax_circul_disk + else + write (6,63) 360.-ctlon,ctlat,xmin_circul_disk + endif + + 61 format (' After first run, Wind Circulation (NHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmax_circul_disk = ',f15.1) + 63 format (' After first run, Wind Circulation (SHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmin_circul_disk = ',f15.1) + +c If nhalf is specified as 0, then don't go through any more +c iterations of this routine, just exit with the value that we +c already got the first time through the loop, above. + + if (dell > 0.50) then + nhalf = 4 + else if (dell > 0.20 .and. dell <= 0.50) then + nhalf = 3 + else if (dell > 0.10 .and. dell <= 0.20) then + nhalf = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + nhalf = 1 + else if (dell <= 0.05) then +c nhalf = 0 + nhalf = 1 +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'In get_wind_circulation, dell is < 0.05 deg, so ' +c print *,'nhalf is set to 0 and only the first iteration of' +c print *,'the search loop is done.' +c print *,' dell= ',dell,' nhalf= ',nhalf +c endif + endif + + if (nhalf < 1) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +c npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only do this once +c for this grid-refinement (even though the grid is redefined +c nhalf times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). Cut the value of +c rads in half (only do this once) so that any points beyond +c rads/2 are not considered as potential centers. + + rads = 0.5 * rads + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igibret) + + if (igibret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_wind_circulation from call to ' + print *,'!!! get_ij_bounds just before nhalf loop. ' + print *,'!!! Stopping processing for storm number ',ist + endif + igwcret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + kloop: do k = 1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: get_wind_circ kloop, k= ',i2,' ' + & ,i2.2,':',i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'get_wind_circ nhalf loop, k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip,' rads= ',rads + endif + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to loop k= ',k + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist + & ,degrees) + if (dist .gt. rads) cycle iloop2 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop2: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop2: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop2 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop2 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop2 +cc and radiusloop2). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,'kloop k= ',k,' wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'kloop k= ',k,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +cc print *,'kloop k= ',k,' circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'kloop k=',k,' circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0.0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'kloop k= ',k,' xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean + + enddo iloop2 + + enddo jloop2 + + if ( verb .ge. 3 ) then + if (uvgeslat >= 0.0) then + print *,'---> xmax_circul_disk= ',xmax_circul_disk + write (6,71) k,360.-ctlon,ctlat,xmax_circul_disk + else + print *,'---> xmin_circul_disk= ',xmin_circul_disk + write (6,73) k,360.-ctlon,ctlat,xmin_circul_disk + endif + endif + + enddo kloop + + 71 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (NHEM: Max) = ' + & ,f15.1) + 73 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (SHEM: Min) = ' + & ,f15.1) + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,xval,trkrinfo,igucret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the minimum in the wind speed near the storm center. This center +c fix is done differently than for the other parms. With this fix, +c we severely limit the area that is searched, because we do not +c want to confuse a wind minimum out on the periphery of a storm +c with the center wind minimum. Therefore, this subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the guess +c position for this time and the 5 other parm fixes. That modified +c guess position is passed into this subroutine as uvgeslon and +c uvgeslat, and that's where the searching for the wind minimum +c is done. To get the wind minimum, the u and v data are first +c interpolated down to a fine grid (see details below for exact +c figures), and then a single-pass barnes analysis is done on that +c fine grid. The reason that we first interpolate the data (which +c is different from how we do the other parms) is that if we just +c use the original grid resolution, we may not be able to +c accurately pick out a minimum in the wind field at the center. +c + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real, allocatable :: uold(:,:),vold(:,:),unew(:,:),vnew(:,:) + real, allocatable :: rlonold(:),rlatold(:),rlonnew(:),rlatnew(:) + real, allocatable :: vmag(:,:) + real :: dx,dy + real :: grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + character*1 :: gotlat + logical(1) cflag, valid_pt(imax,jmax) + logical(1), allocatable :: lbi(:,:) +c + gotlat = 'n' +c +c ----------------------------------------------------------------- +c INTERPOLATE INPUT GRID TO SMALLER GRID +c ----------------------------------------------------------------- +c +c Get beginning and ending j points (on the input grid) for a +c smaller array that surrounds the storm. It is this smaller array +c that we will interpolate to a finer grid. +c +c Calculate number of pts to either side of this j to search +c + npts = ceiling(rads_vmag/(dtk*((dx+dy)/2.))) +c + call get_ij_bounds (npts,0,ritrk_vmag,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij D, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij D, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center from call to ' + print *,'!!! get_ij_bounds, stopping processing for ' + print *,'!!! storm number ',ist + endif + + igucret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, and our gridtype is NOT' + print *,'!!! global, so we are going to redefine ibeg to 1.' + print *,' ' + endif + + ibeg = 1 + endif + endif + + if (jbeg < 1) jbeg = 1 + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center calculating ibeg, iend, jbeg' + print *,'or jend. ibeg= ',ibeg,' iend= ',iend,' jbeg= ',jbeg + print *,'jend= ',jend + print *,'uv center will not be calculated for this time.' + endif + + igrret = 99 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, and our gridtype is' + print *,'!!! NOT global, so we will redefine iend to imax.' + print *,' ' + endif + + iend = imax + endif + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif +c + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the grid sizes for +c some of the typical grids that will be used: +c +c Original grid size # of interps Final grid size +c -------------------- ------------ --------------------- +c 1.00 deg (111.19 km) 3 0.125 deg (13.9 km) +c 1.25 deg (138.99 km) 3 0.156 deg (17.4 km) +c 2.50 deg (277.99 km) 4 0.156 deg (17.4 km) + + if ((dx+dy)/2. > 1.2) then + numinterp = 4 + else if ((dx+dy)/2. > 0.50 .and. (dx+dy)/2. <= 1.2) then + numinterp = 3 + else if ((dx+dy)/2. > 0.25 .and. (dx+dy)/2. <= 0.50) then + numinterp = 2 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.25) then + numinterp = 1 + else if ((dx+dy)/2. <= 0.10) then + numinterp = 0 + endif + + dell = (dx+dy)/2. + imxold = iend - ibeg + 1 + jmxold = jend - jbeg + 1 + +c -------------------------------------------------------------- +c Before interpolating, make sure that all the original +c points have valid data. If they don't then exit the +c subroutine. NOTE: This is NOT checking to see if ALL the pts +c on the complete & full input grid have valid data; it only +c checks those points that are within the box returned from +c get_ij_bounds. + + do i=ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_uv_center, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! PROCESSING WILL STOP. ' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + stop 94 + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_uv_center' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + do j=jbeg,jend + if (.not. valid_pt(ip,j)) goto 975 + enddo + + enddo + +c ------------------------------------ +c Now begin the interpolation process + + allocate (uold(imxold,jmxold),stat=iuo) + allocate (vold(imxold,jmxold),stat=ivo) + allocate (rlonold(imxold),stat=iloo) + allocate (rlatold(jmxold),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. iloo /= 0 .or. ilao /= 0) goto 970 + + do intnum = 1,numinterp + + if (intnum == 1) then + + do i=ibeg,iend + + ik = i + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ik = i + imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i < 1' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i + endif + + igucret = 92 + return + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ik = i - imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i > imax' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i,' imax= ',imax + endif + + igucret = 92 + return + endif + endif + + rlonold(i-ibeg+1) = glon(ik) + do j=jbeg,jend + uold(i-ibeg+1,j-jbeg+1) = u(ik,j,nlev) + vold(i-ibeg+1,j-jbeg+1) = v(ik,j,nlev) + if (gotlat == 'n') then + rlatold(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatold once + enddo + + else + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate (rlatold) + allocate (uold(imxnew,jmxnew),stat=iuo) + allocate (vold(imxnew,jmxnew),stat=ivo) + allocate (rlonold(imxnew),stat=iloo) + allocate (rlatold(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 970 + + gotlat = 'n' + do i=1,imxnew + rlonold(i) = rlonnew(i) + do j=1,jmxnew + uold(i,j) = unew(i,j) + vold(i,j) = vnew(i,j) + if (gotlat == 'n') then + rlatold(j) = rlatnew(j) + endif + enddo + gotlat = 'y' + enddo + + imxold = imxnew + jmxold = jmxnew + deallocate (unew); deallocate (vnew) + deallocate (rlonnew); deallocate (rlatnew) + + endif + + dell = 0.5 * dell + imxnew = 2 * imxold - 1 + jmxnew = 2 * jmxold - 1 + + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + + call bilin_int_even (imxold,jmxold,uold + & ,imxnew,jmxnew,unew,ibiret) + call bilin_int_even (imxold,jmxold,vold + & ,imxnew,jmxnew,vnew,ibiret) +c call lin_int (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int_lon (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int (jmxold,jmxnew,rlatold,rlatnew,iliret) + + chk_lonspc_old = rlonold(imxold) - rlonold(imxold - 1) + chk_latspc_old = rlatold(jmxold) - rlatold(jmxold - 1) + chk_lonspc_new = rlonnew(imxnew) - rlonnew(imxnew - 1) + chk_latspc_new = rlatnew(jmxnew) - rlatnew(jmxnew - 1) + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, intnum= ',intnum + print *,'imxold= ',imxold,' imxnew= ',imxnew + print *,'jmxold= ',jmxold,' jmxnew= ',jmxnew + print *,'Grid boundaries of modified uv grid: ' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ' + & ,grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ' + & ,grid_minlon + endif + + enddo + +c ------------------ + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate(rlatold) + + if (numinterp == 0) then + + ! No interpolations were done for this fine mesh grid, but we + ! need to fill some of these arrays and define variables for + ! subsequent subroutine calls just below here that require + ! the variables imxnew, jmxnew, and the arrays unew and vnew. + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional ' + print *,'grid; iend should not > imax here !!!' + endif + + igucret = 99 + return + endif + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional' + print *,'grid; ibeg should not < 1 here !!!' + endif + + igucret = 99 + return + endif + endif + + imxnew = iend - ibeg + 1 + jmxnew = jend - jbeg + 1 + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + gotlat = 'n' + + do i=ibeg,iend + + ip = i + + if (i > imax) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i - imax ! Wrapping past GM + endif + + if (i < 1) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i + imax ! Wrapping past GM + endif + + rlonnew(i-ibeg+1) = glon(ip) + do j=jbeg,jend + unew(i-ibeg+1,j-jbeg+1) = u(i,j,nlev) + vnew(i-ibeg+1,j-jbeg+1) = v(i,j,nlev) + if (gotlat == 'n') then + rlatnew(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatnew once + enddo + + endif + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,'Grid boundaries of modified uv grid in get_uv_center:' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ',grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ',grid_minlon + endif + + allocate (vmag(imxnew,jmxnew),stat=ivm) + allocate (lbi(imxnew,jmxnew),stat=ilb) + if (ivm /= 0 .or. ilb /= 0) goto 972 + call calc_vmag (unew,vnew,imxnew,jmxnew,vmag,icvret) + deallocate (unew); deallocate (vnew) + + lbi = .TRUE. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to find_maxmin, imxnew= ',imxnew + & ,'jmxnew= ',jmxnew,' ist= ',ist + write (6,171) dell,uvgeslon,360.-uvgeslon,uvgeslat + 171 format (' dell= ',f7.3,' uvgeslon= ',f8.3,'E (',f8.3,'W)' + & ,' uvgeslat= ',f8.3) + endif + +c Note that in the next call, I pass the 'global' argument to +c find_maxmin. This defines what type of grid it is, so that the +c proper grid_buffer can be chosen. This grid_buffer is designed +c to avoid having a center be chosen too close to the grid +c boundary. However, in the case of vmag here, we are only using +c a small subgrid, and we want to make sure we use *all* points +c in that subgrid for searching, and that will occur if we set that +c calling argument to 'global' as opposed to 'regional'. + + call find_maxmin (imxnew,jmxnew,dell,dell,'vmag' + & ,vmag,'min',ist,uvgeslon,uvgeslat,rlonnew,rlatnew,lbi + & ,trkrinfo,cflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,'global',ifmret) + deallocate (vmag); deallocate (lbi) + deallocate (rlonnew); deallocate (rlatnew) +c + if (ifmret == 0) then + goto 995 + else + igucret = ifmret + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center in call to find_maxmin' + print *,'!!! storm num = ',ist,' igucret = ',igucret + endif + + goto 998 + endif +c + 970 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either uold, vold,' + print *,'!!! rlonold or rlatold in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 971 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either unew, vnew,' + print *,'!!! rlonnew or rlatnew in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 972 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either vmag or lbi in ' + print *,'!!! subroutine get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! ivm= ',ivm,' ilb= ',ilb + endif + + igucret = 97 + goto 998 + + 975 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Inside get_uv_center, at least one of the points' + print *,'!!! is not a valid data point. This point may be ' + print *,'!!! outside the valid data bounds of a regional grid' + print *,'!!! i= ',i,' j= ',j + print *,'!!! Storm number = ',ist + endif + + igucret = 98 + goto 998 +c + 995 continue + igucret = 0 +c + 998 continue + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_guess (guesslon,guesslat,clon,clat + & ,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) +c +c ABSTRACT: The purpose of this subroutine is to get a modified +c first guess lat/lon position before searching for the +c minimum in the wind field. The reason for doing this is +c to better refine the guess and avoid picking up a wind +c wind minimum far away from the center. So, use the +c first guess position (and give it strong weighting), and +c then also use the fix positions for the current time +c (give the vorticity centers stronger weighting as well), +c and then take the average of these positions. +c +c INPUT: +c guesslon guess longitude for this forecast time +c guesslat guess latitude for this forecast time +c clon array with center longitude fixes for the various parms +c clat array with center latitude fixes for the various parms +c calcparm logical; tells whether or not a parm has a valid fix +c at this forecast hour +c ist index for current storm +c ifh index for current forecast hour +c maxstorm max # of storms that can be handled +c +c OUTPUT: +c uvgeslon contains modified guess longitude position at which to +c look for the wind minimum +c uvgeslat contains modified guess latitude position at which to +c look for the wind minimum +c igugret return code for this subroutine (0=normal) +c---- +c + USE set_max_parms; USE level_parms; USE error_parms + USE verbose_output + + logical(1) calcparm(maxtp,maxstorm) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real uvgeslon, uvgeslat + real guesslon,guesslat,degrees + integer gt345_ct,lt15_ct + + sumlon = 0.0 + sumlat = 0.0 + ict = 0 + gt345_ct = 0 + lt15_ct = 0 + +c NOTE: We need to be careful in this routine when averaging +c the longitudes together, in case we cross the greenwich +c meridian, because then we may be averaging 345+ lons with +c lons that are less than 15, giving incorrect results. +c Therefore, check for this, and if it occurs, add 360 onto +c any of the <15 lons (add it twice for those lons being +c counted twice (guesslon and the vorticity centers)). + +c Weight the uv guess position by counting the storm's guess +c position twice. + + sumlon = sumlon + 2.*guesslon + sumlat = sumlat + 2.*guesslat + ict = ict + 2 + + if (guesslon > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (guesslon < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct.... + endif + + do ip = 1,maxtp + if ((ip > 2 .and. ip < 7) .or. ip == 10) then + cycle ! because 3-6 are for 850 & 700 u & v and 10 is + ! for surface wind magnitude. + else + if (calcparm(ip,ist)) then + call calcdist (guesslon,guesslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + + if (dist < uverrmax) then +c +c Give the vorticity centers 2x weighting as well +c + if (ip == 1 .or. ip == 2 .or. ip == 11) then + sumlon = sumlon + 2.*clon(ist,ifh,ip) + sumlat = sumlat + 2.*clat(ist,ifh,ip) + ict = ict + 2 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct... + endif + else + sumlon = sumlon + clon(ist,ifh,ip) + sumlat = sumlat + clat(ist,ifh,ip) + ict = ict + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 ! Only 1 for non-zeta parms + endif + endif + + endif + + endif + endif + enddo +c + if (ict > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (sumlon) + uvgeslon = (sumlon + (360.*float(lt15_ct)))/ ict + else + uvgeslon = sumlon / ict + endif + if (uvgeslon >= 360.0) then + uvgeslon = uvgeslon - 360. + endif + uvgeslat = sumlat / ict + igugret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_guess, ict not > 0, ict= ',ict + print *,'!!! vmag center will not be calculated for this' + print *,'!!! storm -- at least not at this level' + print *,'!!! Storm number = ',ist + endif + + igugret = 91 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calc_vmag (xu,xv,imx,jmx,wspeed,icvret) +c +c ABSTRACT: This subroutine calculates the magnitude of the wind +c speed for an array of points, given real u and real v arrays. +c + real xu(imx,jmx),xv(imx,jmx),wspeed(imx,jmx) +c + do i=1,imx + do j=1,jmx + wspeed(i,j) = sqrt( xu(i,j)*xu(i,j) + xv(i,j)*xv(i,j) ) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_even (imxold,jmxold,xold + & ,imxnew,jmxnew,xnew,ibiret) +c +c ABSTRACT: This subroutine does a bilinear interpolation on a +c grid of evenly spaced data. Do NOT attempt to use this subroutine +c with data that are not evenly spaced or you will get unpredictable +c results. +c + real xold(imxold,jmxold), xnew(imxnew,jmxnew) +c +c +c --------------------------------------------------------------------- +c Latitude ----> | +c | +c L O e O e O e O e O | O: original point from input array +c o | +c n e 1 2 1 2 1 2 1 e | 1: interpolated, primary inter. pt +c g | +c i O 2 O 2 O 2 O 2 O | e: interpolated edge point +c t | +c u e 1 2 1 2 1 2 1 e | 2: interpolated, secondary inter. pt +c d | +c e O 2 O 2 O 2 O 2 O | Interpolations are done in the order +c | as indicated above; First, the input +c | e 1 2 1 2 1 2 1 e | 'O' pts are placed onto the new, +c | | larger grid. From that, the '1' pts +c | O 2 O 2 O 2 O 2 O | can be interpolated. Next, the edge +c | | (e) pts are interpolated using an +c v e 1 2 1 2 1 2 1 e | interpolation of two 'O' pts and one +c | '1' pt. Finally, the '2' pts are +c O e O e O e O e O | done using the 2 surrounding '0' and +c | '1' pts. Bilinear interpolation is +c | made incredibly easier by the fact +c | that the grid is evenly spaced. +c --------------------------------------------------------------------- +c NOTE: Remember that the arrays that are read in are indexed as +c (lon,lat), so that in the diagram above, pt (1,1) is at the upper +c left and pt (imax,jmax) is at the lower right, and each column is +c a new latitude and each row is a new longitude. +c +c ----------------------------------------------------------------- +c Put original (O) values from input array into new, expanded array +c ----------------------------------------------------------------- +c + do i=1,imxold + do j=1,jmxold + xnew(2*i-1,2*j-1) = xold(i,j) + enddo + enddo +c +c ---------------------------------------------- +c Interpolate to get primary interior (1) points +c ---------------------------------------------- +c + do i=1,imxold-1 + do j=1,jmxold-1 + xnew(2*i,2*j) = 0.25 * (xnew(2*i-1,2*j-1) + xnew(2*i+1,2*j-1) + & + xnew(2*i+1,2*j+1) + xnew(2*i-1,2*j+1)) + enddo + enddo +c +c --------------------------- +c Interpolate edge (e) points +c --------------------------- +c +c ... Northernmost 'e' points ... +c + j=1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,2)) + enddo +c +c ... Southernmost 'e' points ... +c + j = 2*jmxold - 1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,j-1)) + enddo +c +c ... Westernmost 'e' points ... +c + i=1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(2,2*j)) + enddo +c +c ... Easternmost 'e' points ... +c + i = 2*imxold - 1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(i-1,2*j)) + enddo +c +c ------------------------------------------------ +c Interpolate to get secondary interior (2) points +c ------------------------------------------------ +c + do j=2,2*jmxold-2 + istep = mod(j+1,2) + do i=istep+2,2*imxold-2,2 + xnew(i,j) = 0.25 * (xnew(i-1,j) + xnew(i,j-1) + xnew(i+1,j) + & + xnew(i,j+1)) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points +c + do i=1,ioldmax-1 + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int_lon (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. This particular +c routine is specifically used for interpolating +c longitudes, and it factors in the possibility of +c interpolating across the greenwich meridian. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points, and make the +c necessary adjustment when interpolating a longitude between, +c for example, 359.5 and 0.0. +c + do i=1,ioldmax-1 + if (xnew(2*i-1) > 350. .and. xnew(2*i+1) < 10.) then + xnew(2*i) = 0.5 * (xnew(2*i-1) + (360. + xnew(2*i+1))) + else + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + endif + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) +c +c ABSTRACT: This subroutine finds the maximum and mean zeta values +c at 850 & 700 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms + USE trkrparms; USE level_parms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + + logical(1) readflag(14),valid_pt(imax,jmax),compflag + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanzeta,dx,dy,re,ri,parmlon,parmlat + integer igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) + integer n,ix1,ix2,ilev,npts,imax,jmax,igzvret,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_zeta_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_zeta_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max zeta values at 850 and 700 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_zeta_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_zeta_loop: do n=1,2 + + gridpoint_maxmin = -99.0 + xmeanzeta = -99.0 + compflag = .true. + + select case (n) + case (1); ilev=850 ! For 850 mb + case (2); ilev=700 ! For 700 mb + end select + + if (zeta(ilonfix,jlatfix,n) > -9990.0) then + + ! ------------------------------------------- + ! We have valid zeta data for this level, so + ! we first call barnes now to get the mean zeta + ! surrounding our found center position. + ! ------------------------------------------- + + if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,n),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanzeta + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds + imeanzeta(n) = int ((xmeanzeta * 1e6) + 0.5) + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_zeta_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for zeta values will not be done.') + exit report_zeta_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + exit report_zeta_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) n,ilev,xmeanzeta,imeanzeta(n) + 621 format (1x,'+++ RPT_MEAN_ZETA: n= ',i2,' lev= ',i4 + & ,' xmeanzeta= ',f9.6,' imeanzeta (*1e6)= ',i8) + write (6,*) ' --- mean zeta raw = ',xmeanzeta + endif + + ! ----------------------------------------------- + ! Call fix_latlon_to_ij to get the nearest actual + ! raw (grid) zeta data value, not the mean value. + ! ----------------------------------------------- + + call fix_latlon_to_ij (imax,jmax,dx,dy + & ,zeta(1,1,n),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanzeta,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + igridzeta(n) = int ((gridpoint_maxmin * 1e6) + 0.5) + else + igridzeta(n) = -99 + endif + + if ( verb .ge. 3 ) then + write (6,623) n,ilev,gridpoint_maxmin,igridzeta(n),ifilret + 623 format (1x,'+++ RPT_GRID_ZETA: n= ',i2,' lev= ',i4 + & ,' grid zeta= ',f9.6,' igrid zeta (*1e6)= ',i8 + & ,' ifilret= ',i3) + write (6,*) ' --- grid zeta raw= ',gridpoint_maxmin + endif + + enddo report_zeta_loop + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get 850 & 700 zeta for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine find_maxmin (imax,jmax,dx,dy,cparm,fxy,maxmin,ist + & ,guesslon,guesslat,rlonv,rlatv,valid_pt,trkrinfo + & ,compflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,cmodel_type,ifmret) +c +c This routine finds the location (clon,clat) of and value of the +c the max or min of fxy in the vicinity of slon,slat. The value of +c the input flag maxmin determines whether to look for a max or a +c min value. The max/min is determined by finding the point which +c gives the max/min value of a single point barnes analysis of fxy +c with e-folding radius re (km) and influence radius ri (km). The +c initial search is restricted to a radius rads around the point +c (slon,slat) on a grid with lon,lat spacing dx and dy. The location +c is refined by reducing the spacing of the search grid by a factor +c of two, nhalf times. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c maxmin Char string indicating whether to search for a max or min +c ist Number of the storm being processed +c guesslon Guess longitude of the storm +c guesslat Guess latitude of the storm +c rlonv Array containing longitude values of input grid points +c rlatv Array containing latitude values of input grid points +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c trkrinfo derived type detailing user-specified grid info +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c +c INPUT/OUTPUT: +c compflag Logical; continue processing this storm or not (would be +c set to FALSE if, for example, the guess position is +c outside the domain of a regional grid) +c +c OUTPUT: +c ctlon Center longitude of storm found for this parameter +c ctlat Center latitude of storm found for this parameter +c xval Max or Min value found at the (ctlon,ctlat) +c ifmret Return code from this subroutine +c +c UPDATE DEC 2009: For the HFIP HRH testing, it was found that +c due to the very limited domain size of some of the models, the +c barnes scheme was allowing points close to the grid boundaries +c to erroneously be selected as the center point. We add in a +c buffer (grid_buffer) here to prevent this from occurring. + + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + character(*) maxmin,cparm,cmodel_type + logical(1) compflag, valid_pt(imax,jmax) + real fxy(imax,jmax),rlonv(imax),rlatv(jmax) + real ctlon,ctlat,degrees,dx,dy,guesslon,guesslat,xval + real rads,re,ri,dell,fmax,fmin,rlatt,rlont,dist,ftemp,ritmp + real vmag_latmax,vmag_latmin,vmag_lonmax,vmag_lonmin,retmp + real tlon,tlat,grid_buffer,temp_grid_minlon,temp_guesslon + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + integer imax,jmax,ist,bskip1,bskip2,iskip,ifmret,npts,maxvgrid + integer ibeg,iend,jbeg,jend,ilonfix,jlatfix,igiret,icount,iret + integer ibct,ibarnes_loopct,i,j,k,iix,jix,jvlatfix,ivlonfix + integer nhalf,icvpret + integer date_time(8) + character (len=10) big_ben(3) +c + ifmret = 0 + nhalf = 5 +c +c ----------------------------------------------------------- +c Set initial parms for use in find_maxmin. +c Different radii used for V magnitude than for other parms, +c see discussion in module radii for more details. +c + if (cparm == 'vmag') then + +c NOTE: The maxvgrid variable determines what size grid to send +c to subroutine barnes. e.g., maxvgrid = 8 means send an +c 8x8 grid; maxvgrid = 12 means send a 12x12 grid. For +c ultra-fine mesh grids (finer than 0.04 deg, or 1/25 deg), +c we expand to 12 in order to sample a few more points +c around each grid point. + + if ((dx+dy)/2. > 0.04) then + maxvgrid = 8 + else + maxvgrid = 12 + endif + + rads = rads_vmag; re = retrk_vmag; ri = ritrk_vmag + re = (float(maxvgrid)/4) * ((dx+dy)/2. * dtk) ! Basically, this +c sets re equal to half the distance from the gridpoint +c in question to the farthest point that will be +c sampled when the (maxvgrid x maxvgrid) grid is passed +c on to subroutine barnes. Thus, just ignore the +c parameter retrk_vmag, and use this instead. + else if ((dx+dy)/2. < 1.26 .and. (dx+dy)/2. >= 0.40) then + rads = rads_most; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.40 .and. (dx+dy)/2. >= 0.10) then + rads = rads_fine; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.10) then + rads = rads_hres; re = retrk_hres; ri = ritrk_most + else + rads = rads_coarse; re = retrk_coarse; ri = ritrk_coarse + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of find_maxmin, rads= ',rads,' re= ',re + & ,' ri= ',ri,' cparm= ',cparm,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+12; fmin = 1.0e+12 + ctlon = 0.0; ctlat = 0.0 + + if (npts == 0) npts = 1 + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if (dell > 0.20) then + bskip1 = 2 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 4 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 6 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 10 + bskip2 = 5 + else if (dell <= 0.03) then + bskip1 = 15 + bskip2 = 5 + endif + + if (cparm == 'vmag') then + bskip1 = 1 + bskip2 = 1 + endif + +c If input parm is vmag, we've already done the minimizing by +c interpolating to the fine mesh grid, so we'll simply send the +c bounds that were input to this subroutine to barnes +c as boundaries for the array to search. For all other parms, +c however, no minimizing has been done yet, so we need to call +c get_ij_bounds to set the boundaries for a much smaller grid that +c surrounds the storm (as opposed to having subroutine barnes +c search the entire global grid). + + if (cparm == 'vmag') then + + if ( verb .ge. 3 ) then + print *,'In find_maxmin, jmax= ',jmax,' imax= ',imax + endif + + ibeg=1; jbeg=1; iend=imax; jend=jmax + vmag_latmax = rlatv(1) ! N-most lat of vmag subgrid + vmag_latmin = rlatv(jmax) ! S-most lat of vmag subgrid + vmag_lonmin = rlonv(1) ! W-most lon of vmag subgrid + vmag_lonmax = rlonv(imax) ! E-most lon of vmag subgrid + + if ( verb .ge. 3 ) then + write (6,44) vmag_latmax,vmag_lonmin,360.-vmag_lonmin + & ,imax,jmax + write (6,46) vmag_latmin,vmag_lonmax,360.-vmag_lonmax + endif + + 44 format (' vmag_latmax= ',f8.3,' vmag_lonmin= ',f8.3 + & ,'E (',f8.3,'W) imax= ',i4,' jmax= ',i4) + 46 format (' vmag_latmin= ',f8.3,' vmag_lonmax= ',f8.3 + & ,'E (',f8.3,'W)') + + if (vmag_lonmin > 330. .and. vmag_lonmax < 30.) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: For a case of find_maxmin, our vmag' + print *,'!!! subgrid is straddling the GM. The code should' + print *,'!!! be able to handle this, but if strange errors' + print *,'!!! are occurring, check into the code either here' + print *,'!!! in find_maxmin or get_uv_ctr.' + print *,' ' + endif + endif + + npts = ceiling(rads/(dtk*dell)) + + else + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,guesslon,guesslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to ' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + ifmret = 92 + return + endif + + endif + +c +c --------------------------------------------------------------- +c + if ( verb .ge. 3 ) then + print *,' ' + write (6,39) guesslon,360.-guesslon,guesslat + 39 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + if (cparm == 'vmag') then + print *,'ilonfix= (unused) jlatfix= (unused)' + & ,' npts= ',npts + print *,'ilonfix and jlatfix are meaningless for computing' + print *,'vmag, so ignore the large values you see for them.' + else + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + endif + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + + if ( verb .ge. 3 ) then + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: find_maxmin 1 ',i2.2,':',i2.2,':',i2.2) + endif + + ibct=0 + ibarnes_loopct = 0 + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (guesslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = guesslon - 360. + else + temp_guesslon = guesslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = guesslon + endif + + jix = 0 + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + jloop: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = guesslat + dell*float(j) + + iix = 0 + +c vlat(jix) = rlatt + + iloop: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c if (cparm == 'vmag') then +c print *,' ' +c print '(a16,i6,a4,i6,2(a8,f8.3),a12,f8.3)' +c & ,'in find_max, i= ',i +c & ,' j= ',j,' rlatt= ',rlatt,' rlont= ',rlont +c & ,' 360-rlont= ',360.-rlont +c endif + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT: icvpret= ',icvpret + endif + + cycle iloop + endif + + call calcdist(rlont,rlatt,temp_guesslon,guesslat,dist,degrees) + if (dist .gt. rads) cycle iloop + + if (cparm == 'vmag') then + +c This next bit of code gets the ij coordinates for an 8x8 +c box around the current point under consideration. These ij +c coordinates are sent to barnes so that barnes only loops +c 64 times, as opposed to nearly 10,000 if the whole 97x97 +c array were sent. So, fix rlatt to the grid point just +c northward of rlatt and fix rlont to the grid point just +c eastward of rlont. Note that this makes for a modified +c barnes analysis in that we're sort of specifying ahead of +c time exactly which grid points will be included and we'll +c be excluding some points that would be near the periphery +c of each (rlont,rlatt)'s range, but as long as we're consis- +c tent and do it this way for each point, it's well worth the +c trade-off in cpu time. Parameter maxvgrid determines what +c size array to send to barnes (maxvgrid=8 means 8x8) + + jvlatfix = int((vmag_latmax - rlatt)/dy + 1.) + ivlonfix = int((rlont - temp_grid_minlon)/dx + 2.) +c ivlonfix = int((rlont - vmag_lonmin)/dx + 2.) + + ibeg = ivlonfix - (maxvgrid/2) + iend = ivlonfix + (maxvgrid/2 - 1) + jbeg = jvlatfix - (maxvgrid/2 - 1) + jend = jvlatfix + (maxvgrid/2) + + if (ibeg < 1 .or. jbeg < 1 .or. + & iend > imax .or. jend > jmax) then + + ! DO NOT quit if we find a boundary outside the grid + ! bounds. Rather, just set the J violating bound(s) to + ! the min or max limit, and for I bounds, allow the + ! program to continue down to subsequent code below, + ! provided it's a global grid. + +c print *,'!!! ' +c print *,'!!! Before vmag adjustments, boundaries are: ' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt,' dx= ',dx +c print *,'!!! temp_grid_minlon= ',temp_grid_minlon +c print *,'!!! vmag_latmax= ',vmag_latmax +c print *,'!!! ivlonfix = ',ivlonfix,' jvlatfix = ',jvlatfix +c print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax +c print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Vmag will not be computed for' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Vmag will not be computed for ' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,'!!! ' + print *,'!!! *AFTER* vmag adjustments, boundaries are: ' + print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax + print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + endif + + endif + + endif + + if (cparm == 'vmag') then + ri = re * 3 +c print '(a36,f10.4,a6,f10.4)' +c & ,' + before call to vmag barnes, re= ',re,' ri= ',ri + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,bskip1,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes...' + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After 1st findmax loop, # calls to barnes = ',ibct + print *,'Total # of barnes loop iterations = ',ibarnes_loopct + endif + +c + 55 format ('i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ',f7.3 + & ,' barnval= ',f11.5) + 56 format ('k= ',i3,' i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ' + & ,f7.3,' barnval= ',f11.5) + + if (ctlon < 0.) then + ! We have grid-wrapped to find the ctlon, which was found to be + ! < 0, so for reporting purposes and for the start of the next + ! loop, set ctlon to positive degress east. + ctlon = ctlon + 360. + endif + + if (cparm == 'zeta') then + + if ( verb .ge. 3 ) then + print *,'!!! Zeta check, fmax= ',fmax,' fmin= ',fmin + write (6,61) 360.-ctlon,ctlat,fmax*100000.,fmin*100000. + endif + + else + + if ( verb .ge. 3 ) then + write (6,63) 360.-ctlon,ctlat,fmax,fmin + endif + + endif + 61 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 63 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax = ',e16.3,' fmin = ',e16.3) + 111 format (i2,' rlont= ',f7.2,'W rlatt= ',f7.2,' zeta= ',f13.8) + +c Through interpolation, the grid for vmag has already been +c minimized considerably, we don't need to go through the 2nd part +c of this subroutine, which halves the grid spacing. + + if (nhalf < 1 .or. cparm == 'vmag') then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c ------------------------------------------------------------- +c If the grid spacing is +c fine enough (I've chosen 0.2-deg as a min threshold), there is +c no need to halve the grid more than 3 times, as halving a +c 0.2-deg grid 3 times gives a resolution of 0.025-deg (2.7 km), +c or a max error in the position estimate of 2.7/2 = 1.35 km. + + if ((dx+dy)/2. <= 0.2) then + if ((dx+dy)/2. <= 0.05) then + nhalf = 1 + else + nhalf = 2 + endif + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +ctpm npts = 3 + npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only +c do this once for this grid-refinement (even though the grid is +c redefined 3 times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to get_ij_bounds' + print *,'!!! just before nhalf loop. Stopping processing' + print *,'!!! for storm number ',ist + endif + + ifmret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + + if ( verb .ge. 3 ) then + print *,' ' + endif + + if ((dx+dy)/2. <= 1.25 .and. ri >= 300 .and. re >= 150) then + retmp = re + ritmp = ri + re = re * 0.5 + ri = ri * 0.5 + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re has been reduced' + print *,'from ',retmp,' to ',re,', and ri has been reduced ' + print *,'from ',ritmp,' to ',ri + endif + + else + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re and ri have NOT ' + print *,'been changed. re = ',re,' ri = ',ri + endif + + endif + + ibct=0 + ibarnes_loopct = 0 + do k=1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: find_maxmin kloop, k= ',i2,' ',i2.2,':' + & ,i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat + fmax = -1.0e+15; fmin = 1.0e+15 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'find_maxmin nhalf loop, cparm= ',cparm,' k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,iskip,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes, k= ',k + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop2 + enddo jloop2 + + if ( verb .ge. 3 ) then + if (cparm == 'zeta') then + write (6,71) k,360.-ctlon,ctlat,fmax*100000.,fmin*100000. + else + write (6,73) k,360.-ctlon,ctlat,fmax,fmin + endif + endif + + enddo + + 71 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 73 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax = ',e16.3,' fmin = ',e16.3) + + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ppp after 2nd findmax loop, # calls to barnes = ' + & ,ibct + print *,'ppp Total # of barnes loop iterations = ' + & ,ibarnes_loopct + endif + + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine barnes(flon,flat,rlon,rlat,iimax,jjmax,iibeg,jjbeg + & ,iiend,jjend,fxy,defined_pt,bskip,re,ri,favg,icount,ctype + & ,trkrinfo,iret) +c +c ABSTRACT: This routine performs a single-pass barnes anaylsis +c of fxy at the point (flon,flat). The e-folding radius (km) +c and influence radius (km) are re and ri, respectively. +c +c NOTE: The input grid that is searched in this subroutine is most +c likely NOT the model's full, original grid. Instead, a smaller +c subgrid of the original grid is searched. The upper left and +c lower right grid point indices are passed into this subroutine +c (iibeg, jjbeg, iiend, jjend) for this subgrid. These indices are +c determined in the subroutine get_ij_bounds, and the purpose of +c doing it this way is to limit the number of points for which the +c subroutine has to calculate distances (for a global 1 deg grid, +c the number of loop iterations is reduced from 65160 to somewhere +c around 600). +c +c NOTE: This subroutine will immediately exit with a non-zero +c return code if it tries to access a grid point that does not have +c valid data. This would happen in the case of a regional grid, if +c you try to access a point near the edge of the grid (remember that +c because of the interpolation for the regional grids, there will be +c areas around the edges that have no valid data). +c +c INPUT: +c flon Lon value for center point about which barnes anl is done +c flat Lat value for center point about which barnes anl is done +c rlon Array of lon values for each grid point +c rlat Array of lat values for each grid point +c iimax Max number of pts in x-direction on input grid +c jjmax Max number of pts in y-direction on input grid +c iibeg i index for grid point to start barnes anlysis (upp left) +c jjbeg j index for grid point to start barnes anlysis (upp left) +c iiend i index for last grid point in barnes anlysis (low right) +c jjend j index for last grid point in barnes anlysis (low right) +c fxy Real array of data on which to perform barnes analysis +c defined_pt Logical; bitmap array used for regional grids +c bskip integer to indicate number of grid points to skip during +c a barnes loop, in order to speed processing +c re input e-folding radius for barnes analysis +c ri input influence radius for searching for min/max +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, in +c this barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c favg Average value about the point (flon,flat) +c iret Return code from this subroutine +c + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real fxy(iimax,jjmax), rlon(iimax), rlat(jjmax) + real degrees + integer bskip + logical(1) defined_pt(iimax,jjmax) + character(*) ctype + +c -------------------------- + + res = re*re + wts = 0.0 + favg = 0.0 + + icount = 0 + + do jix=jjbeg,jjend,bskip + do iix=iibeg,iiend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > iimax) then + if (trkrinfo%gridtype == 'global') then + i = iix - iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i,' imax= ',iimax + print *,' ' + endif + + stop 97 + endif + endif + + icount = icount + 1 + + call calcdist(flon,flat,rlon(i),rlat(j),dist,degrees) + + if (dist .gt. ri) cycle + + if (defined_pt(i,j)) then + if (fxy(i,j) >-999.01 .and. fxy(i,j) <-998.99) then + ! This is a patch. Even though this (i,j) is a valid + ! point, its zeta value has been set to -999 because a + ! neighboring point in subroutine rvcal was found + ! to be out of the grid boundaries. + cycle + endif + wt = exp(-1.0*dist*dist/res) + wts = wts + wt + favg = favg + wt*fxy(i,j) + else + if (ctype == 'vitals') then + continue + else +carw print *,' ' +carw print *,'!!! UNDEFINED PT OUTSIDE OF GRID IN BARNES....' +carw print *,'!!! i= ',i,' j= ',j +carw print *,'!!! flon= ',flon,' flat= ',flat +carw print *,'!!! rlon= ',rlon(i),' rlat= ',rlat(j) +carw print *,'!!! re= ',re,' ri= ',ri +carw print *,'!!! EXITING BARNES....' +carw print *,' ' +carw iret = 95 +carw return + endif + endif + + enddo + enddo + + if (wts > 1.0E-5) then + favg = favg/wts + else + favg = 0.0 + endif + iret = 0 +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,rglatmax,rglatmin,rglonmax,rglonmin,geslon,geslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) +c +c ----------------------------------------------------------- +c ABSTRACT: This subroutine figures out, based on ri, dx and dy and +c the guess latitude and longitude positions, the farthest reaching +c grid points that are searchable by an analysis subroutine. The +c purpose is to return indices for a subgrid that is much smaller +c than the original, full grid. This smaller subgrid can then be +c passed to a subsequent analysis or interpolation subroutine, and +c work can be done on this smaller array. This can help save time, +c especially in the barnes analysis subroutine, as work will only +c be done on, say, a 20 x 20 array (400 pts) instead of on a +c 360 x 181 array (65160 pts). It's crucial, however, to make sure +c that the ibeg, jbeg, iend and jend are extended far enough out to +c fully encompass any search that would be done. Below is a +c diagram showing the different grids.... +c +c Full Global or Regional Model Grid (Grid F) -----------> +c ---------------------------------------------------------------- +c | | (ibeg,jbeg) | +c | | x = ij position that the | (Grid R) | +c | | geslat/geslon is fixed to. ._______________. | +c | | | | | +c | | Even though only the points | (Grid B) | | +c | | within Grid B will be checked | . . . . k | | +c v | later on for a max/min (in the | . . . . . | | +c | case of a subsequent call to | . . x . e | | +c | find_maxmin), the barnes anal- | . . . . . | | +c | ysis will include all pts sur- | . . . . . | | +c | rounding these Grid B points | | | +c | that are within a radius of ri. ._______________. | +c | So in the case of pt. k, that ri | +c | radius may extend all the way to the Grid R | | +c | boundary, thus we need to include those (iend,jend) | +c | points within our ibeg-jbeg-iend-jend bounds. | +c | | +c ---------------------------------------------------------------- +c +c Remember that the grids we deal with start north and increase +c south, so the northernmost latitude on the input grid will have +c a j index of 1. +c +c INPUT: +c npts Num pts from x to edge of max/min search grid (Grid B) +c (i.e., You define the size of Grid B by the value of +c npts that you pass into this subroutine). +c nhalf Number of times the grid spacing will be halved +c ri Radius of influence (for use in barnes analysis) +c imax Number of points in x-direction on original grid +c jmax Number of points in y-direction on original grid +c dx Input grid spacing in i-direction on original grid +c dy Input grid spacing in j-direction on original grid +c rglatmax Value of northern-most latitude on original grid +c rglatmin Value of southern-most latitude on original grid +c rglonmax Value of eastern-most longitude on original grid +c rglonmin Value of western-most longitude on original grid +c geslat Value of latitude of guess position of storm +c geslon Value of longitude of guess position of storm +c +c OUTPUT: +c ilonfix i index on full, input grid that storm is fixed to +c jlatfix j index on full, input grid that storm is fixed to +c ibeg i index for top left of sub-array (Grid R) of input grid +c iend i index for bot right of sub-array (Grid R) of input grid +c jbeg j index for top left of sub-array (Grid R) of input grid +c jend j index for bot right of sub-array (Grid R) of input grid +c igiret Return code from this subroutine +c + USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + real tmpangle +c + igiret = 0 +c +c -------------------------------------- +c GET BEGINNING AND ENDING J POINTS.... +c +c (1) Calculate number of searchable, max/min pts, that is, the pts +c from x to the edge of Grid B. +c (2) Calculate number of pts beyond the last search point in Grid +c B, but are within the bounds of Grid R and thus can be +c included in the barnes analysis. +c (3) Add (1) and (2) to get the max number of pts to subtract/add +c to x to get jbeg and jend. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Beginning of get_ij_bounds...' + print *,' geslat= ',geslat,' geslon= ',geslon + print *,' ' + endif + + +c If nhalf > 0: This occurs in the case of a call from fmax, when +c the grid spacing is halved nhalf times. In this case, we have to +c do extra work to figure out the maximum possible grid point. For +c this case: +c jhlatpts = # of grid pts to last possible search pt (from x to +c edge of Grid B in above diagram), plus a cushion. +c jripts = # of searchable grid points within radius ri of last +c possible search pt (num pts between edge of Grid B +c and edge of Grid R in above diagram), plus a cushion +c jbmaxlatpts = # of pts (in j direction) from x to the edge of +c Grid R to include in this subgrid. +c +c If nhalf = 0: In this case, the grid spacing will not be reduced, +c so the number of pts in j direction from x to the edge of Grid +c B will be the input parameter npts, and just multiply it by 2, +c and add 2 for a cushion to get jmaxlatpts. Typically, this sub +c is called from find_maxmin, and in that sub, the first time that +c this sub is called, nhalf will = 0. Then, after a first-shot +c center is found, the grid spacing will be cut in order to rerun +c barnes on a smaller grid, and that's when nhalf will be sent +c here as 3. +c + if (nhalf > 0) then + rdeg = 0.0 + do i = 1,nhalf + rdeg = rdeg + float(npts) * (1./(float(i)*2)) * (dx+dy)/2 + enddo + jhlatpts = ceiling(rdeg/dy) + 1 + jripts = ceiling((ri + 1.)/(dtk*dx)) + 1 + jbmaxlatpts = jhlatpts + jripts + else + jbmaxlatpts = npts * 2 + 2 + endif +c +c +c Roughly fix geslat to the grid point just poleward of geslat. +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' +++ Near top of get_ij_bounds, ' + print *,' +++ geslat= ',geslat,' geslon= ',geslon + print *,' +++ rglatmax= ',rglatmax,' rglatmin= ',rglatmin + print *,' +++ rglonmax= ',rglonmax,' rglonmin= ',rglonmin + print *,' +++ imax= ',imax,' jmax= ',jmax + print *,' +++ dx= ',dx,' dy= ',dy,' nhalf= ',nhalf + print *,' +++ npts= ',npts + if(nhalf>0) then + print *,' +++ jhlatpts= ',jhlatpts,' jripts= ',jripts + else + print *,' +++ nhalf<=0 so jhlatpts and jripts unused' + endif + print *,' +++ jbmaxlatpts= ',jbmaxlatpts + endif + + if (geslat >= 0.0) then + jlatfix = int((rglatmax - geslat)/dy + 1.) + else + jlatfix = ceiling((rglatmax - geslat)/dy + 1.) + endif + + if ( verb .ge. 3 ) then + print *,' +++ jlatfix= ',jlatfix + endif + + jbeg = jlatfix - jbmaxlatpts + jend = jlatfix + jbmaxlatpts + if (jbeg > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jbeg > jmax' + print *,'!!! jbeg = ',jbeg,' jmax= ',jmax + endif + + igiret = igiret + 1 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jend < 1, jend = ',jend + endif + + igiret = igiret + 1 + return + endif + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' +++ jbeg= ',jbeg,' jend= ',jend + endif + + ! If using a global grid, avoid using the pole points, or else + ! you'll get a cosfac = 0 and then a divide by zero!!! + + if (jend == jmax .and. rglatmin == -90.0) then + jend = jmax - 2 + endif + if (jbeg == 1 .and. rglatmax == 90.0) then + jbeg = 3 + endif + +c ----------------------------------------- +c NOW GET BEGINNING AND ENDING I POINTS.... +c +c Using the map factor (cos lat), figure out, based on ri, the +c max distance beyond the last search point in x-direction (in +c degrees) that could be searched at this guess latitude (geslat) +c (i.e., in the diagram above, the max num pts from pt. e eastward +c to the edge of Grid R). Calculate how many grid points that is, +c add 2 to it for a cushion, & add the number of points (npts) +c within the defined search grid (Grid B) to get ibmaxlonpts. +c +c April, 2007: A min statement was put on the calculation to +c derive dlon, since with that cosine in there, the values of +c of dlon could get pretty ridiculous as you approach the poles. +c Also, the cosine factor (cosfac) used to be computed at the +c most poleward latitude possible given the jend here. For +c similar concerns with cosines near the poles, I've scrapped +c this to instead compute the cosine factor at the input +c guess latitude. - tpm + + cosfac = cos (geslat * dtr) + tmpangle = cosfac * dtk + dlon = min((ri /tmpangle ),20.0) +c dlon = min((ri / (cosfac * dtk)),20.0) +c + if (nhalf > 0) then + ihlonpts = ceiling(rdeg/dx) + 1 + ibmaxlonpts = ihlonpts + ceiling(dlon/dx) + 2 + else + ibmaxlonpts = npts + ceiling(dlon/dx) + 2 + endif + + if ( verb .ge. 3 ) then + if(nhalf>0) then + print *,' +++ rdeg= ',rdeg,' ri= ',ri,' cosfac= ',cosfac + print *,' +++ dtr= ',dtr,' dtk= ',dtk,' dlon= ',dlon + else + print*,' +++ nhalf<=0 so rdeg,ri,cosfac,dtr,dtk,dlon unused' + endif + print *,' +++ ibmaxlonpts= ',ibmaxlonpts,' dx= ',dx,' dy= ',dy + endif + +c Roughly fix geslon to the grid point just EASTward of geslon. + + ilonfix = int((geslon - rglonmin)/dx + 2.) + + ibeg = ilonfix - ibmaxlonpts + iend = ilonfix + ibmaxlonpts + + if ( verb .ge. 3 ) then + print *,' +++ (orig) ilonfix= ',ilonfix + print *,' +++ (orig) ibeg= ',ibeg,' iend= ',iend + print *,' +++ ' + endif + + if (ibeg > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 1 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, ibeg > imax' + print *,'!!! for a non-global grid' + print *,'!!! ibeg = ',ibeg,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + ! For a regional grid, just set iend to be imax + iend = imax + endif + endif + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + ! For a regional grid, just set ibeg to be 1 + ibeg = 1 + endif + endif + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + + if ( verb .ge. 3 ) then + print *,'!!! ERROR in get_ij_bounds, iend < 1' + print *,'!!! for a non-global grid' + print *,'!!! iend = ',iend,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine check_bounds (guesslon,guesslat,ist,ifh,trkrinfo + & ,icbret) +c +c ABSTRACT: This subroutine checks to make sure that the requested +c storm is in fact within the model's grid boundaries; +c this is only a concern for the regional models. +c + USE def_vitals; USE grid_bounds; USE set_max_parms + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + if (trkrinfo%gridtype == 'regional') then + if (guesslon > glonmax .or. guesslon < glonmin .or. + & guesslat > glatmax .or. guesslat < glatmin) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is outside of grid' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + goto 125 + else + icbret = 0 + endif + endif + + ! We have encountered problems with global grids where we + ! continue tracking almost the whole way to the pole. While + ! that's nice to do that, it creates problems for array + ! indices, especially in subroutine getradii. So we will cut + ! our losses and eliminate tracking of storms within + ! 5 degrees of the pole for global grids. + + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'global')then + if (guesslat > 85.0 .or. guesslat < -85.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is too close to the' + print *,'!!! N or S Pole for global tracking.' + print *,'!!! STOPPING TRACKING FOR THIS STORM DUE TO POLE' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + else + icbret = 0 + endif + endif + + 125 continue +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calcdist(rlonb,rlatb,rlonc,rlatc,xdist,degrees) +c +c ABSTRACT: This subroutine computes the distance between two +c lat/lon points by using spherical coordinates to +c calculate the great circle distance between the points. +c Figure out the angle (a) between pt.B and pt.C, +c N. Pole then figure out how much of a % of a great +c x circle distance that angle represents. +c / \ +c b/ \ cos(a) = (cos b)(cos c) + (sin b)(sin c)(cos A) +c / \ +c pt./<--A-->\c NOTE: The latitude arguments passed to the +c B / \ subr are the actual lat vals, but in +c \ the calculation we use 90-lat. +c a \ +c \pt. NOTE: You may get strange results if you: +c C (1) use positive values for SH lats AND +c you try computing distances across the +c equator, or (2) use lon values of 0 to +c -180 for WH lons AND you try computing +c distances across the 180E meridian. +c +c NOTE: In the diagram above, (a) is the angle between pt. B and +c pt. C (with pt. x as the vertex), and (A) is the difference in +c longitude (in degrees, absolute value) between pt. B and pt. C. +c +c !!! NOTE !!! -- THE PARAMETER ecircum IS DEFINED (AS OF THE +c ORIGINAL WRITING OF THIS SYSTEM) IN KM, NOT M, SO BE AWARE THAT +c THE DISTANCE RETURNED FROM THIS SUBROUTINE IS ALSO IN KM. +c + USE trig_vals + + real degrees +c + if (rlatb < 0.0 .or. rlatc < 0.0) then + pole = -90. + else + pole = 90. + endif +c + distlatb = (pole - rlatb) * dtr + distlatc = (pole - rlatc) * dtr + difflon = abs( (rlonb - rlonc)*dtr ) +c + cosanga = ( cos(distlatb) * cos(distlatc) + + & sin(distlatb) * sin(distlatc) * cos(difflon)) + +c This next check of cosanga is needed since I have had ACOS crash +c when calculating the distance between 2 identical points (should +c = 0), but the input for ACOS was just slightly over 1 +c (e.g., 1.00000000007), due to (I'm guessing) rounding errors. + + if (cosanga > 1.0) then + cosanga = 1.0 + endif +cPENG added bug fixed on 04/28/2016-------------------------- + if (cosanga < -1.0) then + cosanga = -1.0 + endif +cPENG added on 04/28/2016-------------------------- + + degrees = acos(cosanga) / dtr + circ_fract = degrees / 360. + xdist = circ_fract * ecircum +c +c NOTE: whether this subroutine returns the value of the distance +c in km or m depends on the scale of the parameter ecircum. +c At the original writing of this subroutine (7/97), ecircum +c was given in km. +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine subtract_cor (imax,jmax,dy,level) +c +c ABSTRACT: This subroutine subtracts out the coriolis parameter +c from the vorticity values. It is needed because at the original +c writing of this system, all of the forecast centers who included +c vorticity included only absolute vorticity. +c + USE tracked_parms; USE trig_vals; USE grid_bounds + + implicit none + + integer :: i,j,imax,jmax,level + real :: dy,coriolis,rlat +c + do j=1,jmax + rlat = glatmax - ((j-1) * dy) + coriolis = 2. * omega * sin(rlat*dtr) + do i=1,imax + zeta(i,j,level) = zeta(i,j,level) - coriolis + enddo + enddo +c + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_grib_file_name (ifh,gfilename,ifilename) + +c ABSTRACT: This subroutine uses various input regarding the model +c and forecast hour and generates the name of the input grib file +c for this particular forecast hour. Remember that the lead time +c is in minutes and that 5 spaces must be reserved for the lead +c time (e.g., f00360). File name should be something that looks +c like either, e.g., "gfdl.6thdeg.katrina12l.2005082818.f00720", +c or "gfdl.6thdeg.2005082818.f00720" (the part in there with the +c storm name & ID is optional). The grib index file name should +c be exactly the same as the grib data file itself, but with the +c character string ".ix" added onto the end of the name. +c +c NOTE: Array iftotalmins is brought in via module tracked_parms. +c +C INPUT: +c ifh integer array index for current lead time +c +c OUTPUT: +c gfilename GRIB file name +c ifilename GRIB index file name + + USE gfilename_info; USE tracked_parms; USE atcf + USE verbose_output + + implicit none + + character(*) gfilename,ifilename + character cfmin*5,cymdh*10 + integer ifh,nlen1,nlen2,nlen3,nlen4,nlen5 + +c Convert integer minutes to 5-position character, with +c leading zeroes, and convert 10-digit integer date into +c 10-position character. Then trim the various input variables +c and combine all into the file name. + + write (cfmin,'(i5.5)') iftotalmins(ifh) + write (cymdh,'(i10.10)') atcfymdh + + nlen1 = len_trim(gmodname) + gfilename = trim(gmodname(1:nlen1)) + + nlen2 = len_trim(rundescr) + + gfilename = trim(gfilename(1:nlen1))//'.'//trim(rundescr(1:nlen2)) + + nlen3 = len_trim(atcfdescr) + nlen4 = len_trim(gfilename) + +c If an extension to the name with the ATCF or storm name descriptor +c was included, then add it to the name now. Otherwise, just add +c the starting date and the lead time in minutes. + + if (nlen3 > 0) then + gfilename = trim(gfilename(1:nlen4))//'.' + & //trim(atcfdescr(1:nlen3))//'.'//cymdh//'.f'//cfmin + else + gfilename = trim(gfilename(1:nlen4))//'.'//cymdh//'.f'//cfmin + endif + +c Create the name for the grib index file, which is just the name of +c the grib file, with "ix" added to the end of it. + + nlen5 = len_trim(gfilename) + ifilename = trim(gfilename(1:nlen5))//'.ix' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,72) 'gfilename',gfilename + write (6,72) 'ifilename',ifilename + endif + + 72 format (1x,'In get_grib_file_name, file name for ',a9 + & ,' is ',a120) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) +c +c ABSTRACT: This subroutine reads the input GRIB file for the +c tracked parameters. It then calls subroutines to convert the +c data from a 1-d array into a 2-d array if the read was successful. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature (I jerry-rigged this by storing +c the data as being at the 401 mb level.) +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +c +c INPUT: +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c inp of a derived type, contains user-input info +c lugb integer unit number of input grib file +c lugi integer unit number of input grib index file +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE verbose_output; USE params; USE grib_mod; USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + type (datecard) inp + type (gribfield) :: gfld,prevfld,holdgfld +c + integer, parameter :: jf=40000000 + integer, parameter :: nreadparms=17 + real, allocatable :: f(:) + real :: dmin,dmax,firstval,lastval + logical(1), allocatable :: lb(:) + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1) file_open + logical :: unpack=.true. + logical :: open_grb=.false. + character*1 :: lbrdflag + character*8 :: chparm(nreadparms) + CHARACTER(len=8) :: pabbrev + character (len=10) big_ben(3) + integer date_time(8) + integer,dimension(200) :: jids,jpdt,jgdt + integer :: listsec1(13), enable_timing + integer, intent(in) :: imax,jmax + integer igparm(nreadparms),iglev(nreadparms) + integer iglevtyp(nreadparms) + integer ig2_parm_cat(nreadparms),ig2_parm_num(nreadparms) + integer ig2_lev_val(nreadparms),ig2_lev_typ(nreadparms) +cPENG 04/18/2018 for CMC Det. and CMC ensemble data + integer ig2_lev_11_cmc(nreadparms),ig2_lev_val_cmc(nreadparms) + integer ig2_lev_11_cmcd(nreadparms),ig2_lev_val_cmcd(nreadparms) + + integer cpsig2_parm_cat(nlevs_cps),cpsig2_parm_num(nlevs_cps) +c integer cpsig2_lev_typ(nlevs_cps),cpsig2_lev_val(nlevs_cps) +cPENG------- + integer cpsig2_lev_10(nlevs_cps) + integer cpsig2_lev_11(nlevs_cps),cpsig2_lev_12(nlevs_cps) + + integer ec_igparm(nreadparms),ec_iglev(nreadparms) + integer ec_iglevtyp(nreadparms) + integer cpsgparm(nlevs_cps),cpsglev(nlevs_cps) + integer cpsglevtyp(nlevs_cps) + integer ec_cpsgparm(nlevs_cps) + integer jpds(200),jgds(200),kpds(200),kgds(200) + integer igvret,ifa,ila,ip,ifh,i,j,k,kj,iret,kf,lugb,lugi + integer jskp,jdisc,np + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer pdt_4p0_vert_level,pdt_4p0_vtime + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 +c + lbrdflag = 'n' + enable_timing=trkrinfo%enable_timing +c The following data statements contain the parameters that will be +c used to search the grib files. The first 9 parameters will all be +c used to locate the storm position. The last 4 parameters (500 mb +c u- and v-components and 10 m u- and v- components) will not be +c used for tracking, but only for helping to estimate the next first +c guess position (500 mb winds) and for estimating the max near- +c surface wind speeds in the vicinity of the storm (10 m winds). +c +c ** NOTE: iglevtyp(12 & 13) and iglev(12 & 13) are initialized to +c 0 just to satisfy the IBM xlf compiler, which barks about +c there being too few initial values in the list when I +c only had 11 values there -- even though the real +c initialization for these variables is done just about +c 10 lines below. +c +c ** NOTE: The new ECMWF hi-res data uses the ECMWF GRIB parameter +c ID table, which has different values than the NCEP +c table. Therefore, we needed to add the variables and +c data values for ec_igparm, ec_iglevtyp and ec_iglev. +c +c July 2007: Read statements added for GP height for cyclone +c phase space (CPS) algorithm. + +c data igparm /41,41,33,34,33,34,7,7,1,33,34,33,34,11,7,7,81/ + data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7,81/ + data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 + & ,100,100,100,1/ + data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500,401 + & ,500,200,0/ + + data cpsgparm /13*7/ + data ec_cpsgparm /13*156/ + data cpsglevtyp /13*100/ + data cpsglev /900,850,800,750,700,650,600,550,500,450,400 + & ,350,300/ + + data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 + & ,131,132,130,156,156,999/ + data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 + & ,100,100,100,999/ + data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 + & ,401,500,200,999/ + + data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' + & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' + & ,'vgrid','temp','gphgt','gphgt','lmask'/ + + data ig2_parm_cat /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2/ + data ig2_parm_num /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8/ + data ig2_lev_typ /100,100,100,100,100,100,100,100,101,103,103 + & ,100,100,100,100,100,-9999/ + data ig2_lev_val /850,700,850,850,700,700,850,700,0,10,10,500,500 + & ,401,500,200,-9999/ +cPENG 04/18/2018 for CMC Det. and CMC ensemble data + data ig2_lev_11_cmc /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0/ + data ig2_lev_val_cmc/85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999/ + + data ig2_lev_11_cmcd /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0/ + data ig2_lev_val_cmcd /85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999/ + + data cpsig2_parm_cat /13*3/ + data cpsig2_parm_num /13*5/ +c data cpsig2_lev_typ /13*100/ +c data cpsig2_lev_val /900,850,800,750,700,650,600,550,500,450,400 +c & ,350,300/ +cPENG---------------------------------- + data cpsig2_lev_10 /13*100/ + data cpsig2_lev_11 /13*0/ + data cpsig2_lev_12 /90000,85000,80000,75000,70000,65000 + & ,60000,55000,50000,45000,40000 + & ,35000,30000/ + +c Model numbers used: (1) AVN, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) Early Eta, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble, +c (13) SREF Ensemble, +c (14) NCEP Ensemble (from ensstat mean fields), +c (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) NCEP Ensemble RELOCATION +c (21) UKMET hi-res (from NHC) +c (23) FNMOC Ensemble +c (24) HWRF Basin-scale + + if (trkrinfo%gribver == 2) then + +c For GRIB2, we will check to see if the MSLP being searched for +c is the standard MSLP (MSLP parm ID = 1) or if it is the +c so-called "Eta" or "Membrane" MSLP reduction that is included +c in the output for some models (like GFS and GDAS). Note that +c for 10m winds, with GRIB2, so far with all of the GRIB2 model +c data we've seen to this point, they all have the same IDs for +c 10m winds for all models, so no need to break out by model +c like we do for GRIB v1 in the else portion of this if statement. + + ig2_parm_num(9) = trkrinfo%g2_mslp_parm_id ! 1 = standard MSLP + ! reduction, 192 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB2 read, MSLP ID = ig2_parm_num(9) = ' + & ,ig2_parm_num(9) + + endif + + else + +c For GRIB1, do the same check as done just above in the IF part +c of this IF statement, but note that we need to also check to +c see what the GRIB1 parm IDs are for the sfc wind level type +c and value. Most models list the level type as 105 (which means +c height above the ground) and then a level value of 10. But +c ECMWF and UKMET use a level type of 1 (which means ground or +c water surface) and a level value of 0. + + igparm(9) = trkrinfo%g1_mslp_parm_id ! 102 = standard MSLP + ! reduction, 130 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglev(10) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + iglev(11) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + + ec_iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglev(10) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + ec_iglev(11) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB1 read, MSLP ID = igparm(9) = ' + & ,igparm(9) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev type = ' + & ,iglevtyp(10) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev value = ' + & ,iglev(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev type = ' + & ,ec_iglevtyp(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev value = ' + & ,ec_iglev(10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata. A return' + print *,'code (iret) not equal to zero indicates that ' + print *,'subroutine getgb was unable to find the requested ' + print *,'parameter. This could be simply because the parm is ' + print *,'not included in the grib file (this is likely for ' + print *,'ECMWF data, as they limit what they send us), or it ' + print *,'could indicate a problem with the grib index file.' + endif + + + if (allocated(f)) deallocate(f) + if (allocated(lb)) deallocate(lb) + allocate (f(imax*jmax),stat=ifa) + allocate (lb(imax*jmax),stat=ila) + if (ifa /= 0 .or. ila /= 0) then + print *,' ' + print *,'!!! ERROR in getdata allocating f or lb array.' + print *,'!!! ifa = ',ifa,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + if (trkrinfo%gribver == 2) then + + ! Reading from a GRIB v2 file.... + + grib2_standard_parm_read_loop: do ip = 1,nreadparms + + if (ip == 17) then + if (trkrinfo%use_land_mask == 'y' .or. + & trkrinfo%use_land_mask == 'Y') then + continue + else + if (verb .ge. 3) then + print *,' ' + print *,'The use_land_mask flag has not been set to ' + print *,'y or Y, so we will not try to read it in... ' + print *,' ' + cycle grib2_standard_parm_read_loop + endif + endif + endif + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + if (ip == 17) then + jdisc=1 ! hydrological products. At this point, used only + ! for the land-sea mask. + else + jdisc=0 ! meteorological products + endif + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for input parameter by production template 4.0. This +c tave program is used primarily for temperature, but still we +c will leave that as a variable and not-hard wire it in case we +c choose to average something else in the future. + + ! We are looking for Temperature or GP Height here. This + ! block of code, or even the smaller subset block of code that + ! contains the JPDT(1) and JPDT(2) assignments, can of course + ! be modified if this program is to be used for interpolating + ! other variables.... + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = ig2_parm_cat(ip) + JPDT(2) = ig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + + JPDT(10) = ig2_lev_typ(ip) +cPENG 04/18/2018 CMC Det. and CMC ensemble data + if (inp%model == 16 ) then + JPDT(11) = ig2_lev_11_cmc(ip) + JPDT(12) = ig2_lev_val_cmc(ip) + elseif (inp%model == 15 ) then + JPDT(11) = ig2_lev_11_cmcd(ip) + JPDT(12) = ig2_lev_val_cmcd(ip) + else + JPDT(11) = 0 + if (JPDT(10) == 100) then ! isobaric surface + JPDT(12) = ig2_lev_val(ip) * 100 ! GRIB2 levels are in Pa + else + JPDT(12) = ig2_lev_val(ip) ! This is going to be either mslp + & ! or 10m winds. + endif + endif + + if ( verb_g2 .ge. 1 ) then + print *,'before getgb2 call, value of unpack = ',unpack + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is CLOSED' + endif + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is CLOSED' + endif + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,531) date_time(5),date_time(6),date_time(7) + 531 format (1x,'TIMING: before getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,532) date_time(5),date_time(6),date_time(7) + 532 format (1x,'TIMING: after getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call, value of unpacked = ' + & ,gfld%unpacked + print *,'after getgb2 call, gfld%ngrdpts = ',gfld%ngrdpts + print *,'after getgb2 call, gfld%ibmap = ',gfld%ibmap + endif + + if ( iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb2 found parm: ',chparm(ip) + print *,'+++ at level = ',ig2_lev_val(ip) + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + select case (chparm(ip)) + case ('absv') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpdt(12) == 20000 .or. jpdt(12) == 2) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb2 could not find parm: ' + & ,chparm(ip) + print *,'!!! at level = ',ig2_lev_val(ip) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib2_standard_parm_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c This is the GRIB2 reading section. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + grib2_cps_parm_lev_loop: do ip = 1,nlevs_cps + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + + jpds = -1 + jgds = -1 + j=0 + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = cpsig2_parm_cat(ip) + JPDT(2) = cpsig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + +c JPDT(10) = cpsig2_lev_typ(ip) + JPDT(10) = cpsig2_lev_10(ip) +cPENG-------------------------------------------- + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + JPDT(11) = cpsig2_lev_11(ip) + JPDT(12) = cpsig2_lev_12(ip) + endif + +c if (JPDT(10) == 100) then ! isobaric surface +c JPDT(12) = cpsig2_lev_val(ip) * 100 ! GRIB2 levels +c ! are in Pa +c else +c if (verb .ge. 3) then +c print *,' ' +c print *,'ERROR in getdata: JPDT(10) array value' +c print *,'should only be 100 in this CPS section' +c print *,'for GRIB2 data.' +c endif +c endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,731) date_time(5),date_time(6),date_time(7) + 731 format (1x,'TIMING: before getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn + & ,jgdt,unpack,krec,gfld,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,732) date_time(5),date_time(6),date_time(7) + 732 format (1x,'TIMING: after getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 (PHASE) in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call(PHASE),' + & ,' value of unpacked = ',gfld%unpacked + print *,'after getgb2 (PHASE) call, gfld%ngrdpts = ' + & ,gfld%ngrdpts + print *,'after getgb2 (PHASE) call, gfld%ibmap = ' + & ,gfld%ibmap + endif + + if (verb .ge. 3) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + +c Determine packing information from GRIB2 file. +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) + & then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ' + & ,gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ' + & ,gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned + ! from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do +c this once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) + & then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.' + & ,gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ' + & ,gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ' + & ,gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.' + & ,gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline + & ,gfld%ipdtmpl(1),gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,231) + 231 format (' rec# param level byy bmm bdd ' + & ,'bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin + & ,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo grib2_cps_parm_lev_loop + + endif + + endif + + else + + !---------------------------------- + ! Reading from a GRIB v1 file.... + !---------------------------------- + + grib1_read_loop: do ip = 1,nreadparms + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then ! ECMWF hi-res data uses ECMWF table + print *,' ' + print *,'WARNING: From the namelist, inp%model is set to a' + print *,' value of 4, which is for ECMWF, so in routine' + print *,' getdata_grib, the input jpds(5,6,7) parms are' + print *,' going to have values that are specific for' + print *,' ECMWF GRIB1 data.' + print *,' ' + jpds(5) = ec_igparm(ip) + jpds(6) = ec_iglevtyp(ip) + jpds(7) = ec_iglev(ip) + else ! All other models use NCEP-standard GRIB table + jpds(5) = igparm(ip) + jpds(6) = iglevtyp(ip) + jpds(7) = iglev(ip) + endif + + print *,' ' + print *,' --- Before getgb, jpds(5)= ',jpds(5) + print *,' --- , jpds(6)= ',jpds(6) + print *,' --- , jpds(7)= ',jpds(7) + + if (jpds(5) == 999) then + cycle + endif + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,831) date_time(5),date_time(6),date_time(7) + 831 format (1x,'TIMING: before getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + + if (enable_timing /= 0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,832) date_time(5),date_time(6),date_time(7) + 832 format (1x,'TIMING: after getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb call, j= ',j,' k= ',k + & ,' iftotalmins= ' + & ,iftotalmins(ifh),' parm # (ip) = ',ip,' iret= ',iret + else + print *,'After getgb call, j= ',j,' k= ',k,' ifhours= ' + & ,ifhours(ifh),' parm # (ip) = ',ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb found parm: ',chparm(ip) + print *,'+++ at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,29) + else + write (6,31) + endif + 29 format (' rec# parm# levt lev byy bmm bdd bhh fmin' + & ,' npts minval maxval') + 31 format (' rec# parm# levt lev byy bmm bdd bhh fhr ' + & ,' npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + + select case (chparm(ip)) + case ('absv') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpds(7) == 200) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb could not find parm: ',chparm(ip) + print *,'!!! at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib1_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + cps_grib1_lev_loop: do ip = 1,nlevs_cps + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then + ! Use different grib parm id for ECMWF GP height + jpds(5) = ec_cpsgparm(ip) + else + jpds(5) = cpsgparm(ip) + endif + jpds(6) = cpsglevtyp(ip) + jpds(7) = cpsglev(ip) + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,841) date_time(5),date_time(6),date_time(7) + 841 format (1x,'TIMING: before getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,842) date_time(5),date_time(6),date_time(7) + 842 format (1x,'TIMING: after getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,39) + else + write (6,41) + endif + 39 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fmin npts minval maxval') + 41 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fhr npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo cps_grib1_lev_loop + + endif + + endif + + endif +c + deallocate (f) + deallocate (lb) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) +c +c ABSTRACT: This subroutine reads the input NetCDF file for the +c tracked parameters for one lead time. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +c +c If the user has requested to check the cyclone phase space for +c this run (phaseflag set to 'y' and phasescheme set to 'cps'), +c then we need to have gp height data for 900-300 mb at every 50 +c mb. Some of those levels for gp height data were already read +c in with the read of the initial 17 parameters, but we will be +c sure to read in the others, if requested. +c +c INPUT: +c ncfile_id integer ID associated with the NetCDF file +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file +c itself in subroutine read_netcdf_fhours. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE netcdf_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo + integer, parameter :: nreadparms=17,nreadparms_cps=13 + real, allocatable :: f(:) + real :: dmin,dmax,xmissing_value,xfill_value + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + character*1 :: lbrdflag,match_check + character*30 :: chparm(nreadparms) + character*30 :: chparm_cps(nreadparms_cps) + integer, intent(in) :: ncfile_id,imax,jmax + integer :: igvret,ifa,ip,ifh,i,j,k,m,n,ncfile_tmax,nf_get_att_real + integer :: nf_get_att_double,nf_inq_attlen,imvlen,ifvlen + integer :: usertime,ncix,missing_val_length,nf_status + integer :: nf_inq_varid,varid +c + lbrdflag = 'n' + +cnc data cpsgparm /13*7/ +cnc data cpsglevtyp /13*100/ +cnc data cpsglev /900,850,800,750,700,650,600,550,500,450,400 +cnc & ,350,300/ + +c data chparm /'vort850','vort700' +c & ,'u850','v850','u700','v700' +c & ,'h850','h700','slp','u_ref','v_ref' +c & ,'u500','v500','tm'/ + +c Load the names of the NetCDF variables for the standard +c variables into the chparm array... + + chparm(1) = netcdfinfo%rv850name + chparm(2) = netcdfinfo%rv700name + chparm(3) = netcdfinfo%u850name + chparm(4) = netcdfinfo%v850name + chparm(5) = netcdfinfo%u700name + chparm(6) = netcdfinfo%v700name + chparm(7) = netcdfinfo%z850name + chparm(8) = netcdfinfo%z700name + chparm(9) = netcdfinfo%mslpname + chparm(10) = netcdfinfo%usfcname + chparm(11) = netcdfinfo%vsfcname + chparm(12) = netcdfinfo%u500name + chparm(13) = netcdfinfo%v500name + chparm(14) = netcdfinfo%tmean_300_500_name + chparm(15) = netcdfinfo%z500name + chparm(16) = netcdfinfo%z200name + chparm(17) = netcdfinfo%lmaskname + + + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata_netcdf.' + endif + + if (allocated(f)) deallocate(f) + allocate (f(imax*jmax),stat=ifa) + if (ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getdata_netcdf allocating f data array.' + print *,'!!! ifa = ',ifa + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + !--------------------------------------------------------------- + ! First go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match up + ! the lead times that were read in with the lead times that + ! we read in directly from the NetCDF file. Get the index from + ! the NetCDF file for that lead time and use that in the call to + ! the read routine (get_var3_tlev_double). + !--------------------------------------------------------------- + + usertime = iftotalmins(ifh) + + match_check = 'n' + + find_index_loop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + ncix = m + if (verb .ge. 1) then + print *,'+++ Time match in getdata_netcdf for usertime= ' + & ,usertime,' netcdf file index= ncix= ',ncix + endif + match_check = 'y' + exit find_index_loop + endif + + enddo find_index_loop + + if (match_check == 'n') then + print *,' ' + print *,'!!! ERROR in getdata_netcdf: ' + print *,' For a NetCDF file, the user has ' + print *,' requested to process a lead time, and that lead' + print *,' time does not exist in the NetCDF list of time' + print *,' values. ' + print *,' ifh= ',ifh + print *,' usertime= iftotalmins(ifh)= ',iftotalmins(ifh) + print *,' STOPPING....' + stop 99 + endif + + !--------------------------------------------------------------- + ! Now go through the read loop for the list of parameters + !--------------------------------------------------------------- + + netcdf_standard_parm_read_loop: do ip = 1,nreadparms + + if (chparm(ip) == 'X' .or. chparm(ip) == 'x') then + if (verb .ge. 3) then + print *,' ' + print *,'!!! NetCDF read NOT requested for parm # ',ip + endif + cycle netcdf_standard_parm_read_loop + else + if (verb .ge. 3) then + print *,' ' + print *,'+++ NetCDF read requested for parm # ',ip + & ,' ... parm= ',chparm(ip) + endif + endif + + ! Note that I am sending a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which we + ! want), depending on the model & grid, we may need to flip the + ! grid in the north-south direction. I already have a routine + ! for converting data from a 1-d to a 2-d array, and it has + ! the functionality for flipping a grid, so I programmed it as + ! getting a 1-d array from the netcdf read routine and send that + ! 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm(ip),imax,jmax,ncix + & ,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + + ! Need to get the value of the "missing_value" attribute for + ! this variable from the list of attributes in the NetCDF + ! file. Only do this for the first lead time, since the + ! value of the "missing_value" obviously will not change + ! with lead time. + +c nf_status = nf_inq_attlen (ncfile_id,varid,"missing_value" +c & ,imvlen) +c nf_status = nf_inq_attlen (ncfile_id,varid,"_FillValue" +c & ,ifvlen) + + ! These next two nf function calls retrieve the value of the + ! "missing_value" attribute from the list of attributes for + ! the given variable being read in. This is needed in order + ! to know if a non-valid point is being accessed, as for a + ! regional grid, like the nested fvGFS. In GRIB1/GRIB2 files, + ! such regions would be bitmapped out, but in a NetCDF file, + ! no such bitmap exists, so we have to check for missing + ! values. In case it's a moving grid, we need to do this + ! for every lead time, since the "map of missing values" + ! will shift with lead time. Once we have those missing + ! values, we can loop through them and fill the valid_pt + ! logical array so that, in the end, we will have the same + ! logical bitmap for masking out missing data that we have + ! with GRIB1/GRIB2 data. + + nf_status = nf_inq_varid (ncfile_id,chparm(ip),varid) + + print *,'nf_status from nf_inq_varid call = ',nf_status + + nf_status = nf_get_att_real (ncfile_id,varid,"missing_value" + & ,xmissing_value) + + print *,'nf_status from nf_get_att_real call = ',nf_status + +c nf_status = nf_get_att_real (ncfile_id,varid,"_FillValue" +c & ,xfill_value) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"missing_value",len=imvlen) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"_FillValue",len=ifvlen) +c +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + if (verb .ge. 3) then + write (6,31) + 31 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,33) ifhours(ifh),ifclockmins(ifh),ip,chparm(ip) + & ,dmin,dmax + 33 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,35) chparm(ip),xmissing_value + 35 format (' --- ',a30,' missing value = ',g12.4) + endif + + ! This call to conv1d2d_logic_netcdf creates + ! a logical bitmap, so that in case we have + ! regional (non-global) data and an irregular grid (e.g., + ! the FV3 nested grid), we can mask out grid points that + ! have missing values as their data values. There is not + ! actually a native logical bitmap in NetCDF, so we will + ! create one by examining the real data values and masking + ! out grid points that have missing values. + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic_netcdf (imax,jmax,f,valid_pt + & ,xmissing_value,need_to_flip_lats) + lbrdflag = 'y' + endif + + if (ip == 1) then ! 850 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else if (ip == 2) then ! 700 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + else if (ip == 3) then ! 850 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (ip == 4) then ! 850 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (ip == 5) then ! 700 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (ip == 6) then ! 700 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (ip == 7) then ! 850 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (ip == 8) then ! 700 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (ip == 9) then ! MSLP + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + else if (ip == 10) then ! Near-sfc (10m) u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + else if (ip == 11) then ! Near-sfc (10m) v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + else if (ip == 12) then ! 500 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else if (ip == 13) then ! 500 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else if (ip == 14) then ! 300-500 mb mean Temp + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + else if (ip == 15) then ! 500 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (ip == 16) then ! 200 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + else if (ip == 17) then ! Land-sea mask + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + else + + print *,'!!! NOTE: Parm not recognized. ' + print *,'!!! ip is > 17.... ip= ',ip + print *,'!!! Forecast time level = ',ifh + + endif + + endif + + enddo netcdf_standard_parm_read_loop + +c *--------------------------------------------------------------* +c If we are attempting to determine the cyclone structure using +c Hart's cyclone phase space, then read in data now that will +c allow us to do that. If we are instead just using the +c mid-level (300-500 mb) mean temperature to do that with a +c simple warm-core check, then that mean temperature field was +c already read in above in the read loop for the standard +c variables. The variables needed here for CPS are pretty +c straightforward: gp height every 50 mb from 300 to 900 mb. +c keep in mind that we have already read in a few of these +c gp height records for selected levels above. +c *--------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + chparm_cps(1) = netcdfinfo%z900name + chparm_cps(2) = netcdfinfo%z850name + chparm_cps(3) = netcdfinfo%z800name + chparm_cps(4) = netcdfinfo%z750name + chparm_cps(5) = netcdfinfo%z700name + chparm_cps(6) = netcdfinfo%z650name + chparm_cps(7) = netcdfinfo%z600name + chparm_cps(8) = netcdfinfo%z550name + chparm_cps(9) = netcdfinfo%z500name + chparm_cps(10) = netcdfinfo%z450name + chparm_cps(11) = netcdfinfo%z400name + chparm_cps(12) = netcdfinfo%z350name + chparm_cps(13) = netcdfinfo%z300name + + ! Read in GP Height levels for cyclone phase space... + + if (verb .ge. 3) then + print *,' ' + print *,'--- Reads for CPS parms follow...' + print *,' ' + endif + + netcdf_cps_parm_read_loop: do ip = 1,nreadparms_cps + + if (chparm_cps(ip) == 'X' .or. chparm_cps(ip) == 'x') then + if (verb .ge. 3) then + print *,'!!! ERROR: NetCDF read NOT requested for' + print *,'!!! CPS parm # ',ip + print *,'!!! You must have an error in your namelist.' + print *,'!!! You have requested to do cyclone phase' + print *,'!!! checking, so you need to include the ' + print *,'!!! NetCDF names for ALL requested gp height' + print *,'!!! variables from 900 to 300 mb, every 50 ' + print *,'!!! mb,in the namelist.' + print *,'!!! phaseflag is being set to NO (n), and ' + print *,'!!! phase-checking will NOT take place.' + print *,'!!! If you want to run again and just do ' + print *,'!!! phase-checking with a simple warm-core' + print *,'!!! check, then in the namelist set phaseflag' + print *,'!!! to y and set phasescheme to vtt.' + phaseflag = 'n' + endif + exit netcdf_cps_parm_read_loop + else + if (verb .ge. 3) then + print *,'+++ NetCDF read requested for cps parm # ',ip + & ,' ... parm= ',chparm_cps(ip) + endif + endif + + ! As above, we send a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which + ! we want), depending on the model & grid, we may need to + ! flip the grid in the north-south direction. I already + ! have a routine for converting data from a 1-d to a 2-d + ! array, and it has the functionality for flipping a grid, + ! so I programmed it as getting a 1-d array from the netcdf + ! read routine and send that 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm_cps(ip),imax + & ,jmax,ncix,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm_cps(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + nf_status = nf_inq_varid (ncfile_id,chparm_cps(ip),varid) + nf_status = nf_get_att_real (ncfile_id,varid + & ,"missing_value",xmissing_value) + + if (verb .ge. 3) then + write (6,231) + 231 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,233) ifhours(ifh),ifclockmins(ifh),ip + & ,chparm_cps(ip),dmin,dmax + 233 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,235) chparm_cps(ip),xmissing_value + 235 format (' --- ',a30,' missing value = ',g12.4) + endif + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo netcdf_cps_parm_read_loop + + endif + + endif + + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_ncdim1 (ncid,var1_name,nmax) +c +c ABSTRACT: This routine queries a netcdf file to get the +c value of a requested file dimension (e.g., imax, jmax) +c + implicit none + + include "netcdf.inc" + + integer, intent(in) :: ncid + character*(*), intent(in) :: var1_name + integer, intent(out) :: nmax + integer :: status, var1id + + status = nf_inq_dimid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + status = nf_inq_dimlen (ncid,var1id,nmax) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_ncdim1 +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_var1_double (ncid,var1_name,nmax,var1) +c +c ABSTRACT: This routine reads a netcdf file in order to return +c a 1-dimensional array of data. + + implicit none + + include "netcdf.inc" + + integer, intent(in):: ncid + character*(*), intent(in):: var1_name + integer, intent(in):: nmax + real, intent(out):: var1(nmax) + + integer :: status, var1id + + status = nf_inq_varid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) +! write(*,*) 'Got var1id', var1id + + status = nf_get_var_real (ncid,var1id,var1) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var1_double +c +c--------------------------------------------------------- +c +c--------------------------------------------------------- + subroutine get_var3_tlev_double (ncid,var3_name,imax,jmax,ncix + & ,var3,igvret) +c +c ABSTRACT: This routine reads a netcdf file and returns a +c 2-dimensional synoptic variable at a particular lead time. +c The lead time is specified by the ltix array, which is +c included in module tracked_parms and defined in subroutine +c read_fhours. +c +c PARAMETERS +c +c INPUT: +c ncid integer that contains the NetCDF file ID +c var3_name character name of NetCDF input file +c imax integer x-dimension of input data +c jmax integer y-dimension of input data +c ncix integer index of time level for where this time level +c actually is inside the NetCDF data. Do NOT confuse this +c with the index of where this forecast hour is in the +c user's list of input forecast hours, as they may be +c different. For example, the user may request times that +c are every 6 hours, but the NetCDF file might have times +c that are every hour, so the indices for those two arrays +c will be different. Be sure to use the one (ncix) that +c indicates where the data actually starts in the +c NetCDF file. +c +c OUTPUT: +c var3 real array with real values returned from NetCDF read +c igvret integer return code from this routine + + USE tracked_parms; USE verbose_output; USE netcdf_parms + + implicit none + + include "netcdf.inc" +c + integer, intent(in) :: ncid,ncix + character*(*), intent(in) :: var3_name + integer, intent(in) :: imax,jmax + real, intent(out) :: var3(imax,jmax) + integer :: istart(3),ilength(3) + integer :: status,var3id,igvret + + if (verb .ge. 3) then + print *,' ' + print *,'In get_var3_tlev_double, ncix= ',ncix + print *,' nctotalmins(ncix)= ',nctotalmins(ncix) + endif + + istart(1) = 1 + istart(2) = 1 + istart(3) = ncix + + ilength(1) = imax + ilength(2) = jmax + ilength(3) = 1 + + igvret = 0 + + status = nf_inq_varid (ncid,var3_name,var3id) + + if (status /= NF_NOERR) then + print *,' ' + print *,'NOTE: Could not find variable ',var3_name,' at time' + & ,' index ncix= ',ncix + & ,' nctotalmins(ncix)= ',nctotalmins(ncix) + + igvret = 92 + return + endif + + status = nf_get_vara_real (ncid,var3id,istart,ilength,var3) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var3_tlev_double +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine handle_netcdf_err (status) +c +c ABSTRACT: This subroutine is an error handling routine for NetCDF- +c related functions. + + implicit none + + include "netcdf.inc" + + integer status +c + if (status /= nf_noerr) then + print *,' ' + print *,'Tracker NetCDF error: ' + print *, nf_strerror(status) + stop 'Stopped' + endif + + end subroutine handle_netcdf_err +c +c------------------------------------------------------------------- +c +c------------------------------------------------------------------- + subroutine bitmapchk (n,ld,d,dmin,dmax) +c +c This subroutine checks the bitmap for non-existent data values. +c Since the data from the regional models have been interpolated +c from either a polar stereographic or lambert conformal grid +c onto a lat/lon grid, there will be some gridpoints around the +c edges of this lat/lon grid that have no data; these grid +c points have been bitmapped out by Mark Iredell's interpolater. +c To provide another means of checking for invalid data points +c later in the program, set these bitmapped data values to a +c value of -999.0. The min and max of this array are also +c returned if a user wants to check for reasonable values. +c + logical(1) ld + dimension ld(n),d(n) +c + dmin=1.E15 + dmax=-1.E15 +c + do i=1,n + if (ld(i)) then + dmin=min(dmin,d(i)) + dmax=max(dmax,d(i)) + else + d(i) = -999.0 + endif + enddo +c + return + end +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic (imax,jmax,lb1d,lb2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of logical data (lb1d) into a 2-dimensional output +c array (dimension imax,jmax) of logical data (lb2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c lb1d 1-d array containing logical bitmap values +c iscanflag This is kgds(11), an integer value in the GDS, +c which holds the scanning mode for the data values +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + logical(1) lb1d(imax*jmax),lb2d(imax,jmax) + logical(1) :: need_to_flip_lats + integer :: ilat,ilatix,ilon,imax,jmax +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + lb2d(ilon,ilatix) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + lb2d(ilon,ilat) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic_netcdf (imax,jmax,dat1d,lb2d + & ,xmissing_val,need_to_flip_lats) +c +c ABSTRACT: The purpose of this routine is to create a 2-d logical +c bitmap to be used for masking out regions with missing data, +c such as for a regional grid with irregular boundaries (such as +c we've seen for the regional / nested FV3). This bitmap will +c have the same functionality as a GRIB1/GRIB2 bitmap. The trick +c is that NetCDF does not have a logical bitmap within its +c definition, so we need to make one. We do this by reading in +c the "missing_value" attribute for any variable, then here we +c scan through all the data values retrieved from the NetCDF read, +c and then for all grid points with missing values we set the +c valid_pt flag to .false. +c +c Note the use of the need_to_flip_lats flag. This is in order to +c handle grids that are flipped. Most grids -- NCEP, UKMET, ECMWF +c -- have point (1,1) as the uppermost left point on the grid, and +c the data goes from north to south. Some grids -- GFDL and the +c new NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the need_to_flip_lats flag was set to TRUE in getgridinfo, meaning +c that we have northward scanning data, we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d array containing floating point data values +c xmissing_val real value of missing value for the given variable +c that was read in for the calling routine +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + USE verbose_output + + implicit none + + logical(1) lb2d(imax,jmax) + logical(1) need_to_flip_lats + integer ilat,ilatix,ilon,imax,jmax,tct,fct,mct + real dat1d(imax*jmax) + real xmissing_val +c + tct = 0 + fct = 0 + mct = 0 + + if (verb >= 3) then + print *,' ' + print *,'TOP of conv1d2d_logic_netcdf, xmissing_val= ' + & ,xmissing_val + print *,' ' + endif +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then + lb2d(ilon,ilatix) = .false. +c print *,'LBSF FLIP: ilon= ',ilon,' ilatix= ',ilatix +c fct = fct + 1 + else + lb2d(ilon,ilatix) = .true. +c tct = tct + 1 + endif + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then +c print *,'LBSF no-flip: ilon= ',ilon,' ilat= ',ilat + lb2d(ilon,ilat) = .false. +c fct = fct + 1 + else + lb2d(ilon,ilat) = .true. +c tct = tct + 1 + endif + enddo + enddo + + endif + +c print *,' ' +c print *,' LB STATS: tct= ',tct,' fct= ',fct,' mct= ',mct +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine conv1d2d_real (imax,jmax,dat1d,dat2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of real data (dat1d) into a 2-dimensional output +c array (dimension imax,jmax) of real data (dat2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d real array of data +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c dat2d 2-d real array of data +c + logical(1) :: need_to_flip_lats + real dat1d(imax*jmax),dat2d(imax,jmax) +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + dat2d(ilon,ilatix) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + dat2d(ilon,ilat) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_nlists (inp,trkrinfo,netcdfinfo) +c +c ABSTRACT: This subroutine simply reads in the namelists that are +c created in the shell script. Namelist datein contains the +c starting date information, plus the model identifier. Namelist +c stswitch contains the flags for processing for each storm. +c + USE inparms; USE set_max_parms; USE atcf; USE trkrparms; USE phase + USE structure; USE gfilename_info + USE verbose_output; USE waitfor_parms; USE netcdf_parms + USE tracking_parm_prefs + + implicit none + + integer ifh + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo +c + namelist/datein/inp + namelist/atcfinfo/atcfnum,atcfname,atcfymdh,atcffreq + namelist/trackerinfo/trkrinfo + namelist/phaseinfo/phaseflag,phasescheme,wcore_depth + namelist/structinfo/structflag,ikeflag + namelist/fnameinfo/gmodname,rundescr,atcfdescr + namelist/verbose/verb,verb_g2 + namelist/waitinfo/use_waitfor,wait_min_age,wait_min_size + & ,wait_max_wait,wait_sleeptime + & ,use_per_fcst_command,per_fcst_command + namelist/netcdflist/netcdfinfo + namelist/parmpreflist/user_wants_to_track_zeta850 + & ,user_wants_to_track_zeta700,user_wants_to_track_wcirc850 + & ,user_wants_to_track_wcirc700,user_wants_to_track_gph850 + & ,user_wants_to_track_gph700,user_wants_to_track_mslp + & ,user_wants_to_track_wcircsfc,user_wants_to_track_zetasfc + & ,user_wants_to_track_thick500850 + & ,user_wants_to_track_thick200500 + & ,user_wants_to_track_thick200850 + +c Set namelist default values: + use_per_fcst_command='t' + per_fcst_command=' ' + atcffreq=600 + trkrinfo%enable_timing=1 + trkrinfo%want_oci=.false. + trkrinfo%gribver=1 ! Set to GRIB1 as default, can be set to + ! something else in the namelist input. + + read (5,NML=datein,END=801) + 801 continue + read (5,NML=atcfinfo,END=807) + 807 continue + print *,'just before trackerinfo read namelist' + read (5,NML=trackerinfo,END=809) + 809 continue + print *,'just after trackerinfo read namelist' + read (5,NML=phaseinfo,END=811) + 811 continue + read (5,NML=structinfo,END=815) + 815 continue + read (5,NML=fnameinfo,END=817) + 817 continue + read (5,NML=waitinfo,END=821) + 821 continue + read (5,NML=netcdflist,END=823) + 823 continue + read (5,NML=parmpreflist,END=825) + 825 continue + read (5,NML=verbose,END=819,ERR=833) + 819 continue + goto 837 + 833 continue + verb = 1 + 837 continue + + print *,'in read_nlists, verb= ',verb + + if ( verb .ge. 0 ) then + print *,' ' + print *,'After datein namelist in trak.f, namelist ' + & ,'parms follow:' + print *,'Forecast initial year = byy = ',inp%byy + print *,'Forecast initial month = bmm = ',inp%bmm + print *,'Forecast initial day = bdd = ',inp%bdd + print *,'Forecast initial hour = bhh = ',inp%bhh + print *,'Forecast model identifier = model= ',inp%model + print *,'Forecast model type = modtyp= ',inp%modtyp + print *,'Forecast model data lead time units= lt_units= ' + & ,inp%lt_units + print *,'Forecast model data sequencing setup= file_seq= ' + & ,inp%file_seq + print *,'Forecast model nest type = ',inp%nesttyp +c + print *,' ' + print *,'Values read in from atcfinfo namelist: ' + write (6,89) atcfnum,atcfname + write (6,90) atcfymdh + write (6,92) atcffreq + 89 format ('ATCF ID = ',i2,' ATCF Name = ',a4) + 90 format ('ATCF date (initial date on output atcf records) = ' + & ,i10) + 92 format ('ATCF output frequency (in hours*100) = atcffreq = ',i6) +c + print *,' ' + print *,'Values read in from trackerinfo namelist follow: ' + write (6,101) ' western boundary = westbd = ',trkrinfo%westbd + write (6,101) ' eastern boundary = eastbd = ',trkrinfo%eastbd + write (6,101) ' northern boundary = northbd = ',trkrinfo%northbd + write (6,101) ' southern boundary = southbd = ',trkrinfo%southbd + write (6,102) ' tracker type = ',trkrinfo%type + write (6,103) ' mslp threshold = mslpthresh = ' + & ,trkrinfo%mslpthresh + write (6,120) ' Flag for using backup mslp gradient check= ' + & ,'use_backup_mslp_grad_check = ' + & ,trkrinfo%use_backup_mslp_grad_check + write (6,103) ' v850 threshold = v850thresh = ' + & ,trkrinfo%v850thresh + write (6,122) ' Flag for using backup 850 mb Vt check= ' + & ,'use_backup_850_vt_check = ' + & ,trkrinfo%use_backup_850_vt_check + write (6,123) ' Max allowable distance between the ' + & ,'tracker-found fixes for mslp and 850 zeta = ' + & ,trkrinfo%max_mslp_850 + write (6,104) ' model grid type = ',trkrinfo%gridtype + write (6,101) ' Contour interval to be used = ',trkrinfo%contint + write (6,106) ' Flag for whether or not roci will be computed' + & ,' and written out for tracker-type case = ' + & ,trkrinfo%want_oci + write (6,105) ' Flag for whether or not vitals will be written ' + & ,'out = ',trkrinfo%out_vit + write (6,109) ' Flag for whether or not a land mask will be ' + & ,'used for tcgen candidate low filtering = ' + & ,trkrinfo%use_land_mask + write (6,110) ' Flag for input data type (grib or netcdf) = ' + & ,trkrinfo%inp_data_type + write (6,107) ' Flag for which GRIB version (1 or 2) the input' + & ,' data will be in = ',trkrinfo%gribver + write (6,108) ' Flag for input GRIB2 JPDTN (0 or 1) = ' + & ,trkrinfo%g2_jpdtn + write (6,112) ' Flag for input GRIB2 MSLP ID (1 or 192) = ' + & ,trkrinfo%g2_mslp_parm_id + write (6,114) ' Flag for input GRIB1 MSLP ID (102 or 130) = ' + & ,trkrinfo%g1_mslp_parm_id + write (6,116) ' Flag for input GRIB1 sfcwind level type ' + & ,'(PDS Octet 10... should be 1 or 105) = ' + & ,trkrinfo%g1_sfcwind_lev_typ + write (6,118) ' Flag for input GRIB1 sfcwind level value ' + & ,'(PDS Octets 11 & 12... usually 0 or 10) = ' + & ,trkrinfo%g1_sfcwind_lev_val + + 101 format (a31,f7.2) + 102 format (a16,a7) + 103 format (a31,f7.4) + 104 format (a19,a8) + 106 format (a46,a41,L1) + 105 format (a48,a6,a1) + 109 format (a45,a41,a1) + 110 format (a45,a6) + 107 format (a47,a19,i1) + 108 format (a39,i2) + 112 format (a43,i4) + 114 format (a45,i4) + 116 format (a41,a39,i4) + 118 format (a42,a43,i4) + 120 format (a44,a29,a1) + 122 format (a40,a26,a1) + 123 format (a36,a44,f7.1) + + print *,' ' + print *,' ' + print *,'Values read in from netcdflist namelist: ' + print *,' ' + write (6,300) netcdfinfo%num_netcdf_vars ! Total *possible* + ! number of input NetCDF variables, + ! including those that are included + ! in the input file and those that + ! are not. + write (6,370) netcdfinfo%netcdf_filename ! full path filename + write (6,301) + write (6,302) netcdfinfo%rv850name ! 850 mb rel vort + write (6,304) netcdfinfo%rv700name ! 700 mb rel vort + write (6,306) netcdfinfo%u850name ! 850 mb u-comp + write (6,308) netcdfinfo%v850name ! 850 mb v-comp + write (6,310) netcdfinfo%u700name ! 700 mb u-comp + write (6,312) netcdfinfo%v700name ! 700 mb v-comp + write (6,314) netcdfinfo%z850name ! 850 mb gp height + write (6,316) netcdfinfo%z700name ! 700 mb gp height + write (6,318) netcdfinfo%mslpname ! mslp + write (6,320) netcdfinfo%usfcname ! near-sfc u-comp + write (6,322) netcdfinfo%vsfcname ! near-sfc v-comp + write (6,324) netcdfinfo%u500name ! 500 mb u-comp + write (6,326) netcdfinfo%v500name ! 500 mb v-comp + write (6,328) netcdfinfo%tmean_300_500_name !Mean T @ 300-500 mb + write (6,330) netcdfinfo%z500name ! 500 mb gp height + write (6,332) netcdfinfo%z200name ! 200 mb gp height + write (6,334) netcdfinfo%lmaskname ! Land mask + write (6,336) netcdfinfo%z900name ! 900 mb gp height + write (6,338) netcdfinfo%z800name ! 800 mb gp height + write (6,340) netcdfinfo%z750name ! 750 mb gp height + write (6,342) netcdfinfo%z650name ! 650 mb gp height + write (6,344) netcdfinfo%z600name ! 600 mb gp height + write (6,346) netcdfinfo%z550name ! 550 mb gp height + write (6,348) netcdfinfo%z450name ! 450 mb gp height + write (6,350) netcdfinfo%z400name ! 400 mb gp height + write (6,352) netcdfinfo%z350name ! 350 mb gp height + write (6,354) netcdfinfo%z300name ! 300 mb gp height + write (6,355) netcdfinfo%time_name ! Name of time variable + ! (usually it is "time") + write (6,356) netcdfinfo%lon_name ! longitudes + write (6,358) netcdfinfo%lat_name ! latitudes + write (6,359) netcdfinfo%time_units ! This will be either "days" + ! or "hours". If it's "hours", + ! then all the time data values + ! are for hours since the initial + ! time. Same thing for "days", + ! however if it is "days", then + ! know that a value of 0.25 will + ! be the same as a 6-hour lead + ! time. + + 300 format ('Total *possible* number of input NetCDF variables,' + & ,/,' including those that are included in the input' + & ,/,' NetCDF file and those that are not = ',i4) + 370 format ('Input NetCDF filename = ',a180) + 301 format (' ',/ + & ,'List of NetCDF variables follows. A value of X ',/ + & ,'indicates the variable is not included in the ',/ + & ,'input file and no attempt will be made to read in ',/ + & ,'that variable: ',/,' ') + 302 format ('NetCDF variable name for 850 mb vort = ',a30) + 304 format ('NetCDF variable name for 700 mb vort = ',a30) + 306 format ('NetCDF variable name for 850 mb u-comp = ',a30) + 308 format ('NetCDF variable name for 850 mb v-comp = ',a30) + 310 format ('NetCDF variable name for 700 mb u-comp = ',a30) + 312 format ('NetCDF variable name for 700 mb v-comp = ',a30) + 314 format ('NetCDF variable name for 850 mb gp height = ',a30) + 316 format ('NetCDF variable name for 700 mb gp height = ',a30) + 318 format ('NetCDF variable name for MSLP = ',a30) + 320 format ('NetCDF variable name for near-sfc u-comp = ',a30) + 322 format ('NetCDF variable name for near-sfc v-comp = ',a30) + 324 format ('NetCDF variable name for 500 mb u-comp = ',a30) + 326 format ('NetCDF variable name for 500 mb v-comp = ',a30) + 328 format ('NetCDF variable name for 300-500 mb Mean T = ',a30) + 330 format ('NetCDF variable name for 500 mb gp height = ',a30) + 332 format ('NetCDF variable name for 200 mb gp height = ',a30) + 334 format ('NetCDF variable name for land-sea mask = ',a30) + 336 format ('NetCDF variable name for 900 mb gp height = ',a30) + 338 format ('NetCDF variable name for 800 mb gp height = ',a30) + 340 format ('NetCDF variable name for 750 mb gp height = ',a30) + 342 format ('NetCDF variable name for 650 mb gp height = ',a30) + 344 format ('NetCDF variable name for 600 mb gp height = ',a30) + 346 format ('NetCDF variable name for 550 mb gp height = ',a30) + 348 format ('NetCDF variable name for 450 mb gp height = ',a30) + 350 format ('NetCDF variable name for 400 mb gp height = ',a30) + 352 format ('NetCDF variable name for 350 mb gp height = ',a30) + 354 format ('NetCDF variable name for 300 mb gp height = ',a30) + 355 format ('NetCDF variable name for time = ',a30) + 356 format ('NetCDF variable name for longitudes = ',a30) + 358 format ('NetCDF variable name for latitudes = ',a30) + 359 format ('NetCDF time value (hours|days) = ',a30) + + print *,' ' + print *,' ' + print *,'Values read in from parmpreflist namelist: ' + print *,' ' + write (6,402) user_wants_to_track_zeta850 + write (6,404) user_wants_to_track_zeta700 + write (6,406) user_wants_to_track_wcirc850 + write (6,408) user_wants_to_track_wcirc700 + write (6,410) user_wants_to_track_gph850 + write (6,412) user_wants_to_track_gph700 + write (6,414) user_wants_to_track_mslp + write (6,416) user_wants_to_track_wcircsfc + write (6,418) user_wants_to_track_zetasfc + write (6,420) user_wants_to_track_thick500850 + write (6,422) user_wants_to_track_thick200500 + write (6,424) user_wants_to_track_thick200850 + + 402 format ('user_wants_to_track_zeta850= ',a2) + 404 format ('user_wants_to_track_zeta700= ',a2) + 406 format ('user_wants_to_track_wcirc850= ',a2) + 408 format ('user_wants_to_track_wcirc700= ',a2) + 410 format ('user_wants_to_track_gph850= ',a2) + 412 format ('user_wants_to_track_gph700= ',a2) + 414 format ('user_wants_to_track_mslp= ',a2) + 416 format ('user_wants_to_track_wcircsfc= ',a2) + 418 format ('user_wants_to_track_zetasfc= ',a2) + 420 format ('user_wants_to_track_thick500850= ',a2) + 422 format ('user_wants_to_track_thick200500= ',a2) + 424 format ('user_wants_to_track_thick200850= ',a2) + + print *,' ' + print *,'Values read in from phaseinfo namelist: ' + write (6,211) phaseflag,phasescheme + write (6,212) wcore_depth + 211 format ('Storm phase flag = ',a1,' Phase scheme = ',a4) + 212 format ('Storm phase, warm core depth (wcore_depth) = ',f7.2) + + print *,' ' + print *,'Values read in from structinfo namelist: ' + write (6,93) structflag + write (6,95) ikeflag + 93 format ('Structure flag = ',a1) + 95 format ('IKE flag = ',a1) + + print *,' ' + print *,'Values read in for grib file name from fnameinfo' + & ,' namelist: ' + write (6,131) gmodname + write (6,133) rundescr + write (6,135) atcfdescr + 131 format ('Model name description = gmodname = ',a4) + 133 format ('Forecast run description = rundescr = ',a40) + 135 format ('Optional ATCF / Storm name description = atcfdescr = ' + & ,a40) + + print *,' ' + print *,'Value read in for verbose output for most output:' + write (6,141) verb + 141 format ('Value read in for verbose flag = verb = ',i2) + + print *,' ' + print *,'Value read in for verbose output for grib2 output:' + write (6,142) verb_g2 + 142 format ('Value read in for GRIB2 verbose flag = verb_g2 = ',i2) + + print *,' ' + print *,'Values read in from waitinfo namelist:' + write (6,151) use_waitfor + write (6,152) wait_min_age + write (6,153) wait_min_size + write (6,154) wait_max_wait + write (6,155) wait_sleeptime + if(len_trim(per_fcst_command)>0) then + write (6,156) trim(per_fcst_command) + else +c No command specified, so disable the feature + use_per_fcst_command='n' + endif + 151 format ('Flag for input file waiting = use_waitfor = ',a1) + 152 format ('min age (time in seconds since last mod) = ' + & ,'wait_min_age = ',i8) + 153 format ('min file size in bytes = wait_min_size = ',i12) + 154 format ('max number of seconds to wait for each file = ' + & ,'wait_max_wait = ',i6) + 155 format ('number of seconds to sleep between checks = ' + & ,'wait_sleeptime = ',i6) + 156 format ('command to run after every forecast time = "',A,'"') +c + if (use_waitfor == 'y') then + if (inp%file_seq == 'multi') then + continue + else + print *,' ' + print *,'!!! ERROR: The use_waitfor flag is set to "y".' + print *,' This requires that the inp%file_seq flag be' + print *,' set to "multi", but you have specified ' + print *,' something else. ' + print *,' inp%file_seq = ',inp%file_seq + print *,' STOPPING....' + print *,' ' + STOP 95 + endif + endif +c + endif + return + end +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_fhours (ifhmax) +c +c ABSTRACT: This subroutine reads in a text file that contains the +c forecast times that will be read in. The format of the file is +c in "MMMMM", i.e., minutes, for example, for a forecast going out +c to 120h, the file would look like this: +c +c For reference, here +c are the times that +c match up with the +c minutes on the left: +c +c 1 0 0:00 +c 2 240 4:00 +c 3 270 4:30 +c 4 300 5:00 +c 5 330 5:30 +c 6 360 6:00 +c 7 600 10:00 +c 8 630 10:30 +c 9 660 11:00 +c 10 690 11:30 +c 11 720 12:00 +c 12 960 16:00 +c 13 990 16:30 +c . . . +c . . . +c . . . +c 87 7200 120:00 +c +c Note that we are now allowing for sub-hourly time intervals. +c + USE tracked_parms + USE verbose_output + + implicit none +c + integer, parameter :: iunit_fh=15 + integer itmphrs(750),itmpmins(750),input_mins(750),itmpltix(750) + integer ifhmax,inphr,inpmin,ict,i,ifa,ifma,icma,ira,inpltix,ila + real xminfract + + itmphrs = -99 + itmpmins = -99 + + if (allocated(ifhours)) deallocate (ifhours) + if (allocated(iftotalmins)) deallocate (iftotalmins) + if (allocated(ifclockmins)) deallocate (ifclockmins) + if (allocated(fhreal)) deallocate (fhreal) + if (allocated(ltix)) deallocate (ltix) + + ict = 0 + do while (.true.) + + if ( verb .ge. 3 ) then + print *,'Top of while loop in read_fhours' + endif + + read (iunit_fh,85,end=130) inpltix,inpmin + write (6,85) inpltix,inpmin + + if (inpmin >= 0 .and. inpmin < 150000) then + ict = ict + 1 + itmpltix(ict) = inpltix + itmphrs(ict) = inpmin / 60 + itmpmins(ict) = mod(inpmin,60) + input_mins(ict) = inpmin + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Input minutes not between 0 and 150000' + print *,'!!! inpmin= ',inpmin + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + if ( verb .ge. 3 ) then + print *,'readloop, ict= ',ict,' inpmin= ',inpmin + endif + + enddo + + 130 continue + + ifhmax = ict + + 85 format (i4,1x,i5) + + if ( verb .ge. 3 ) then + print *,' ' + endif + + allocate (ifhours(ifhmax),stat=ifa) + allocate (iftotalmins(ifhmax),stat=ifma) + allocate (ifclockmins(ifhmax),stat=icma) + allocate (fhreal(ifhmax),stat=ira) + allocate (ltix(ifhmax),stat=ila) + if (ifa /= 0 .or. ifma /= 0 .or. icma /= 0 .or. ira /= 0 .or. + & ila /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_fhours allocating either ifhours,' + print *,'!!! iftotalmins, ifclockmins or fhreal.' + print *,'!!! ifa = ',ifa,' ifma= ',ifma,' ira= ',ira + print *,'!!! icma= ',icma,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + do i = 1,ifhmax + + ltix(i) = itmpltix(i) + xminfract = float(itmpmins(i)) / 60. + fhreal(i) = float(itmphrs(i)) + xminfract + ifhours(i) = itmphrs(i) + ifclockmins(i) = itmpmins(i) + iftotalmins(i) = input_mins(i) + + if (i > 1) then + if (fhreal(i) > fhreal(i-1)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: In read_fhours, the time read in ' + print *,'!!! is not greater than the previous time.' + print *,'!!! i= ',i + print *,'!!! fhreal(i)= ',fhreal(i) + print *,'!!! fhreal(i-1)= ',fhreal(i-1) + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + endif + + if ( verb .ge. 3 ) then + write (6,87) i,ltix(i),iftotalmins(i),fhreal(i),ifhours(i) + & ,ifclockmins(i) + endif + + enddo + + 87 format (1x,'i= ',i3,' input lead time index= ',i4,' minutes= ' + & ,i5,' real_lead_time= ',f6.2,' clock_lead_time= ',i3,':' + & ,i2) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_tcv_card1 (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + logical(1) :: vit_file_exists + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + + ! Check to see if the TC Vitals file exists. If so, then open it + ! using the unit specified in lucard. + + inquire (file="tcvit_rsmc_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for existing, RSMC-numbered' + & ,' storms exists and will be opened with ' + & ,' unit= lucard= ',lucard + endif + + open (unit=lucard,file="tcvit_rsmc_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_rsmc_storms.txt has ' + print *,' been opened with unit= lucard= ',lucard + endif + + else + + if (trkrinfo%type == 'tracker') then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card. The fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. This TC Vitals file is needed for' + print *,'!!! a tracker case. Check to see that the ' + print *,'!!! TC Vitals file exists in this directory and' + print *,'!!! is named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING....' + print *,'!!! ' + iret=99 + return + endif + + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! NOTE: In read_tcv_card, the fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. While this TC Vitals file is ' + print *,'!!! needed for tracker cases, you are running' + print *,'!!! either a midlat or tcgen case here, and so ' + print *,'!!! that file is not needed... although you can ' + print *,'!!! run with using tc vitals for those genesis' + print *,'!!! cases if you want to. You may want to check' + print *,'!!! and make sure this is what you intend. If ' + print *,'!!! you do want to use it, the TC Vitals file ' + print *,'!!! should be in this directory and it should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! ' + endif + + endif + + endif + + ii=1 + + if (vit_file_exists) then + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + 801 continue + endif + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + + maxstorm = numtcv + + if (maxstorm > 0) then + continue + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING...' + print *,'!!! ' + iret=99 + return + endif + endif + + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! Stopping....' + print *,' ' + endif + + iret = 99 + return + + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card opening rsmc TC vitals' + print *,'!!! file named tcvit_rsmc_storms.txt. A file with' + print *,'!!! that name needs to be in your working directory.' + print *,'!!! It should contain the input TC vitals for ' + print *,'!!! already-existing storms that have rsmc-issued' + print *,'!!! storm IDs.' + endif + + iret = 97 + return + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine read_gen_vitals1(lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + logical(1) :: vit_file_exists + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + + ! Check to see if the genesis TC Vitals file exists. If so, then + ! open it using the unit specified in lgvcard. + + inquire (file="tcvit_genesis_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for genesis' + & ,' storms exists and will be opened with ' + & ,' unit= lgvcard= ',lgvcard + endif + + open (unit=lgvcard,file="tcvit_genesis_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_genesis_storms.txt has ' + print *,' been opened with unit= lgvcard= ',lgvcard + endif + + endif + + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + + ii = numtcv + 1 + + if (vit_file_exists) then + + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1 + & ,1x,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + endif + + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals opening genesis vitals' + print *,'!!! file named tcvit_genesis_storms.txt' + endif + + iret = 97 + return + + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just read the +c grib file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE tracked_parms; USE inparms + USE verbose_output; USE params; USE grib_mod + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + + type(gribfield) :: gfld,prevfld,holdgfld + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1), allocatable :: lb(:) + logical :: unpack=.true. + logical :: open_grb=.false. + CHARACTER(len=8) :: pabbrev + integer,dimension(200) :: jids,jpdt,jgdt + integer, parameter :: jf=40000000 + integer :: listsec1(13) + integer pdt_4p0_vert_level,pdt_4p0_vtime + real xhold,xlondiff,xlatdiff,temp,firstval,lastval + real, allocatable :: f(:) + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer jpds(200),jgds(200),igetpds(200),igetgds(200) + integer, intent(in) :: ifh + integer, intent(out) :: imax,jmax + integer iia,ija,ila,midi,midj,i,j,iix,jix,ifa,iret + integer iscanflag,iggret,kf,k,lugb,lugi,jskp,jdisc + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 + + iggret = 0 + + allocate (lb(jf),stat=ila); allocate (f(jf),stat=ifa) + if (ila /= 0 .or. ifa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating either lb or f' + print *,'!!! ila = ',ila,' ifa= ',ifa + endif + iggret = 97 + return + endif + + if (trkrinfo%gribver == 2) then + + ! Search for a record from a GRIB2 file + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 ! meteorological products + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for Temperature or GP Height by production template.... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + ! Request a record on a lat/lon grid. + + jgdtn = 0 + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpdt(8) = 0 + jpdt(9) = iftotalmins(ifh) + else + jpdt(8) = 1 + jpdt(9) = ifhours(ifh) + endif + + if (verb >= 3) then + print *,'before getgb2 call, lugb= ',lugb,' lugi= ',lugi + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + if ( iret.ne.0) then + print *,' ' + print *,' ERROR: getgb2 error in getgridinfo = ',iret + print *,' FATAL ERROR: cannot proceed without info ' + print *,' from getgridinfo. STOPPING....' + stop 95 + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if ( verb_g2 .ge. 1 ) then + print *,' ' + print *,' -- BEGIN getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'PDT num= gfld%ipdtnum= ',gfld%ipdtnum + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + imax = gfld%igdtmpl(8) + jmax = gfld%igdtmpl(9) + dx = float(gfld%igdtmpl(17))/1.e6 + dy = float(gfld%igdtmpl(17))/1.e6 + kf = gfld%ngrdpts + + holdgfld = gfld + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + + endif + + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' -- END getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' ' + print *,' ' + + endif + + need_to_flip_lons = .false. + + iscanflag = gfld%igdtmpl(19) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(gfld%igdtmpl(12))/1.e6 + glatmax = float(gfld%igdtmpl(15))/1.e6 + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(gfld%igdtmpl(15))/1.e6 + glatmax = float(gfld%igdtmpl(12))/1.e6 + need_to_flip_lats = .false. + endif + + glonmin = float(gfld%igdtmpl(13))/1.e6 + glonmax = float(gfld%igdtmpl(16))/1.e6 + + if (verb .ge. 3) then + print *,'In getgridinfo: glatmin= ',glatmin + print *,' glatmax= ',glatmax + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + + else + + !------------------------------------------ + ! Search for a record from a GRIB1 file + !------------------------------------------ + + jpds = -1 + jgds = -1 + + jgds(1) = 0 ! Request a record that's on a lat/lon grid + + if ( verb .ge. 3 ) then + print *,'before getgb in getgridinfo, ifh= ',ifh + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* Forecast hour: ',i4,':',i2.2) + print *,' ifhours(ifh)= ',ifhours(ifh) + print *,' iftotalmins(ifh)= ',iftotalmins(ifh) + endif + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + j=0 + +c jpds(14) = 0 ! test +c + write(*,980) jpds(1),jpds(2) + write(*,981) jpds(3),jpds(4) + write(*,982) jpds(5),jpds(6) + write(*,983) jpds(7),jpds(8) + write(*,984) jpds(9),jpds(10) + write(*,985) jpds(11),jpds(12) + write(*,986) jpds(13),jpds(14) + write(*,987) jpds(15),jpds(16) + write(*,988) jpds(17),jpds(18) + write(*,989) jpds(19),jpds(20) + write(*,990) jpds(21),jpds(22) + write(*,991) jpds(23),jpds(24) + write(*,992) jpds(25) + write(*,880) jgds(1),jgds(2) + write(*,881) jgds(3),jgds(4) + write(*,882) jgds(5),jgds(6) + write(*,883) jgds(7),jgds(8) + write(*,884) jgds(9),jgds(10) + write(*,885) jgds(11),jgds(12) + write(*,886) jgds(13),jgds(14) + write(*,887) jgds(15),jgds(16) + write(*,888) jgds(17),jgds(18) + write(*,889) jgds(19),jgds(20) + write(*,890) jgds(21),jgds(22) + + 980 format(' jpds(1) = ',i7,' jpds(2) = ',i7) + 981 format(' jpds(3) = ',i7,' jpds(4) = ',i7) + 982 format(' jpds(5) = ',i7,' jpds(6) = ',i7) + 983 format(' jpds(7) = ',i7,' jpds(8) = ',i7) + 984 format(' jpds(9) = ',i7,' jpds(10) = ',i7) + 985 format(' jpds(11) = ',i7,' jpds(12) = ',i7) + 986 format(' jpds(13) = ',i7,' jpds(14) = ',i7) + 987 format(' jpds(15) = ',i7,' jpds(16) = ',i7) + 988 format(' jpds(17) = ',i7,' jpds(18) = ',i7) + 989 format(' jpds(19) = ',i7,' jpds(20) = ',i7) + 990 format(' jpds(21) = ',i7,' jpds(22) = ',i7) + 991 format(' jpds(23) = ',i7,' jpds(24) = ',i7) + 992 format(' jpds(25) = ',i7) + 880 format(' jgds(1) = ',i7,' jgds(2) = ',i7) + 881 format(' jgds(3) = ',i7,' jgds(4) = ',i7) + 882 format(' jgds(5) = ',i7,' jgds(6) = ',i7) + 883 format(' jgds(7) = ',i7,' jgds(8) = ',i7) + 884 format(' jgds(9) = ',i7,' jgds(10) = ',i7) + 885 format(' jgds(11) = ',i7,' jgds(12) = ',i7) + 886 format(' jgds(13) = ',i7,' jgds(14) = ',i7) + 887 format(' jgds(15) = ',i7,' jgds(16) = ',i7) + 888 format(' jgds(17) = ',i7,' jgds(18) = ',i7) + 889 format(' jgds(19) = ',i7,' jgds(20) = ',i7) + 890 format(' jgds(20) = ',i7,' jgds(22) = ',i7) + + print *,'lugb= ',lugb,' lugi= ',lugi + print *,'before ggi getgb jpds(14) = ',jpds(14) + print *,'before ggi getgb jgds(1) = ',jgds(1) + + call getgb(lugb,lugi,jf,j,jpds,jgds, + & kf,k,igetpds,igetgds,lb,f,iret) + + if (iret.ne.0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo calling getgb' + print *,'!!! Return code from getgb = iret = ',iret + endif + + iggret = iret + else + iggret=0 + imax = igetgds(2) + jmax = igetgds(3) + dx = float(igetgds(9))/1000. + dy = float(igetgds(10))/1000. + endif + +c write(*,780) igetpds(1),igetpds(2) +c write(*,781) igetpds(3),igetpds(4) +c write(*,782) igetpds(5),igetpds(6) +c write(*,783) igetpds(7),igetpds(8) +c write(*,784) igetpds(9),igetpds(10) +c write(*,785) igetpds(11),igetpds(12) +c write(*,786) igetpds(13),igetpds(14) +c write(*,787) igetpds(15),igetpds(16) +c write(*,788) igetpds(17),igetpds(18) +c write(*,789) igetpds(19),igetpds(20) +c write(*,790) igetpds(21),igetpds(22) +c write(*,791) igetpds(23),igetpds(24) +c write(*,792) igetpds(25) +c write(*,680) igetgds(1),igetgds(2) +c write(*,681) igetgds(3),igetgds(4) +c write(*,682) igetgds(5),igetgds(6) +c write(*,683) igetgds(7),igetgds(8) +c write(*,684) igetgds(9),igetgds(10) +c write(*,685) igetgds(11),igetgds(12) +c write(*,686) igetgds(13),igetgds(14) +c write(*,687) igetgds(15),igetgds(16) +c write(*,688) igetgds(17),igetgds(18) +c write(*,689) igetgds(19),igetgds(20) +c write(*,690) igetgds(21),igetgds(22) +c +c 780 format(' kpds(1) = ',i7,' kpds(2) = ',i7) +c 781 format(' kpds(3) = ',i7,' kpds(4) = ',i7) +c 782 format(' kpds(5) = ',i7,' kpds(6) = ',i7) +c 783 format(' kpds(7) = ',i7,' kpds(8) = ',i7) +c 784 format(' kpds(9) = ',i7,' kpds(10) = ',i7) +c 785 format(' kpds(11) = ',i7,' kpds(12) = ',i7) +c 786 format(' kpds(13) = ',i7,' kpds(14) = ',i7) +c 787 format(' kpds(15) = ',i7,' kpds(16) = ',i7) +c 788 format(' kpds(17) = ',i7,' kpds(18) = ',i7) +c 789 format(' kpds(19) = ',i7,' kpds(20) = ',i7) +c 790 format(' kpds(21) = ',i7,' kpds(22) = ',i7) +c 791 format(' kpds(23) = ',i7,' kpds(24) = ',i7) +c 792 format(' kpds(25) = ',i7) +c 680 format(' kgds(1) = ',i7,' kgds(2) = ',i7) +c 681 format(' kgds(3) = ',i7,' kgds(4) = ',i7) +c 682 format(' kgds(5) = ',i7,' kgds(6) = ',i7) +c 683 format(' kgds(7) = ',i7,' kgds(8) = ',i7) +c 684 format(' kgds(9) = ',i7,' kgds(10) = ',i7) +c 685 format(' kgds(11) = ',i7,' kgds(12) = ',i7) +c 686 format(' kgds(13) = ',i7,' kgds(14) = ',i7) +c 687 format(' kgds(15) = ',i7,' kgds(16) = ',i7) +c 688 format(' kgds(17) = ',i7,' kgds(18) = ',i7) +c 689 format(' kgds(19) = ',i7,' kgds(20) = ',i7) +c 690 format(' kgds(20) = ',i7,' kgds(22) = ',i7) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,' dx= ',dx,' dy= ',dy + endif + + +c ---------------------------------------------------------------- +c Get boundaries of the data grid. NOTE: gds(4) is referred to in +c GRIB documenatation as the "Latitude of origin", which might +c imply "minimum Latitude". However, for the grids that we'll be +c using in this program, the "Latitude of origin" will be listed +c under gds(4) as the northernmost point (eg., in MRF, +c gds(4) = 90), so for this program, use gds(4) as your max lat, +c and gds(7) as your min lat. However, in case NCEP, UKMET or +c ECMWF change their convention and begin flipping their grids, a +c check is made to make sure that the max lat is not less than the +c min lat. +c +c BUGFIX (August, 2001): It is possible to have an input grid +c which goes from south to north (such as NAVGEM). In this case, +c we flip the data in subroutine conv1d2d_real. However, the max +c and min latitudes listed in the GRIB GDS will be confused, so we +c need to check the value of the GRIB scanning mode flag here. + + need_to_flip_lons = .false. + + iscanflag = igetgds(11) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(igetgds(4))/1000. + glatmax = float(igetgds(7))/1000. + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(igetgds(7))/1000. + glatmax = float(igetgds(4))/1000. + need_to_flip_lats = .false. + endif + + glonmin = float(igetgds(5))/1000. + glonmax = float(igetgds(8))/1000. + + endif + +c After this point in this subroutine, nothing is GRIB1 / GRIB2 +c specific, so it does not need to be within the if/then +c statement above that differentiated between GRIB / GRIB2. + +c17Jul2014 if (glonmin < 0.0) glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax < 0.0) glonmax = 360. - abs(glonmax) + + if (glonmin >= 0.0 .and. glonmax >= 0.0) then + if (glonmin > glonmax) then + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Badly notated longitude boundaries in ' + print *,' GRIB PDS, because the min longitude ' + print *,' (glonmin) is greater than the max ' + print *,' longitude (glonmax) where both longitudes' + print *,' are greater than 0.' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + print *,' !!! STOPPING....' + stop 98 + endif + endif + elseif (glonmin < 0.0 .and. glonmax >= 0.0) then + ! An example of this is the MPAS data, which starts and ends + ! at the dateline and is specified as glonmin=-179.875, + ! glonmax=179.875. Convert to be positive and go from + ! 180.125 to 539.875. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0, glonmax > 0, so glonmin' + print *,' will be converted to be > 0 and 360 will' + print *,' be added to glonmax.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. + abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin < 0.0 .and. glonmax < 0.0) then + ! Examples of this are GFDL and HWRF. In this case, make + ! both glonmin and glonmax positive. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0 and glonmax < 0, so both' + print *,' will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin >= 0.0 .and. glonmax < 0.0) then + ! An example of this is the GFS data, which goes from + ! glonmin=0.0 to glonmax=-0.5. Convert it here to go + ! from glonmin=0.0 to glonmax=359.5 + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is >= 0 and glonmax < 0, so' + print *,' glonmax will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + endif + +c17Jul2014 if (glonmin < 0.0) then +c17Jul2014 glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax <= 0.0) then +c17Jul2014 glonmax = 360. - abs(glonmax) +c17Jul2014 else +c17Jul2014 glonmax = 360 + abs(glonmax) +c17Jul2014 endif +c17Jul2014 endif + + if (glatmax < glatmin) then + temp = glatmax + glatmax = glatmin + glatmin = temp + endif + + if (glonmin > 200.0 .and. glonmin <= 360.) then + if (glonmax < 50.) then + ! Likely GM-wrapping in current record + glonmax = glonmax + 360. + endif + endif +c + if ( verb .ge. 3 ) then + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + print *,' ' + print *,'NOTE: For regional grids, valid data points might' + print *,'NOT extend all the way to the gds-defined grid ' + print *,'boundary, due to the fact that data have been ' + print *,'interpolated from a NPS or Lamb-Conf grid onto a ' + print *,'lat/lon grid. This program checks the logical ' + print *,'bitmap for valid data points, but just keep this in' + print *,'mind if trying to debug errors that occur near the' + print *,'grid boundaries for regional models.' + endif + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glat)) deallocate(glat) + if (allocated(glon)) deallocate(glon) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + endif + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + + iggret = 96 + return + endif + + do j=1,jmax + glat(j) = glatmax - (j-1)*dy + enddo + do i=1,imax + glon(i) = glonmin + (i-1)*dx + enddo + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + +c -------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have +c forgotten to change the input grid bounds from a global grid +c run). Modify the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just query the +c netcdf file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE inparms + USE verbose_output; USE netcdf_parms + + implicit none +c + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (datecard) inp + + logical(1) :: need_to_flip_lats,need_to_flip_lons + real xhold,xlondiff,xlatdiff + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer iscanflag,iggret + integer, intent(in) :: ncfile_id + integer, intent(out) :: imax,jmax + integer :: iia,ija,midi,midj,i,j,iix,jix +c + + iggret = 0 + + call get_ncdim1(ncfile_id,netcdfinfo%lon_name,imax) + call get_ncdim1(ncfile_id,netcdfinfo%lat_name,jmax) + + if (allocated(tmplon)) deallocate (tmplon) + if (allocated(tmplat)) deallocate (tmplat) + allocate (tmplon(imax),stat=iia) + allocate (tmplat(jmax),stat=ija) + if (iia /= 0 .or. ija /= 0) then + print *,' ' + print *,'!!! ERROR in sub getgridinfo_netcdf allocating arrays.' + print *,'!!! iia = ',iia,' ija= ',ija + iggret = 94 + return + endif + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + endif + + call get_var1_double (ncfile_id,netcdfinfo%lon_name,imax,tmplon) + call get_var1_double (ncfile_id,netcdfinfo%lat_name,jmax,tmplat) + +c Compute the dx and dy by picking values out of the middle of +c the lat and lon arrays.... + + midi = imax/2 + midj = jmax/2 + + dx = abs(tmplon(midi) - tmplon(midi-1)) + dy = abs(tmplat(midj) - tmplat(midj-1)) + + if (verb .ge. 1) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,'dx= ',dx,' dy= ',dy + print *,' ' + write (6,112) midi,dx + write (6,113) midj,dy + + 112 format(1x,' DX: midi= ',i4,' dx= ',f8.4) + 113 format(1x,' DY: midj= ',i4,' dy= ',f8.4) + endif + + +c ------------------------------------------------------------------ +c Get boundaries of the data grid. Note that it is possible to have +c an input grid which goes from south to north (in fact, it appears +c that many NetCDF files are constructed this way). Keep in mind, +c however, that the tracker has been written such that point (1,1) +c should be the upper-leftmost point on the grid, while point +c (imax,jmax) should be the lower-rightmost point. If we check and +c find that we're dealing with data that instead starts from the +c south and increases northward, we flip the data in subroutine +c conv1d2d_real. Similarly here, we make sure to test so that when +c we are done in this routine, glatmax refers to the northernmost +c latitude and glatmin the southernmost latitude. + + if (tmplon(imax) > tmplon(1)) then + glonmin = tmplon(1) + glonmax = tmplon(imax) + else + glonmin = tmplon(imax) + glonmax = tmplon(1) + endif + + if (tmplat(1) > tmplon(jmax)) then + glatmax = tmplat(1) + glatmin = tmplat(jmax) + else + glatmax = tmplat(jmax) + glatmin = tmplat(1) + endif + + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glon)) deallocate (glon) + if (allocated(glat)) deallocate (glat) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + iggret = 96 + return + endif + + ! If the lat or lon grids are flipped (i.e., the lats increase + ! from south to north, or the lons increase westward), then we + ! will need to flip both the data arrays as well as the arrays + ! that are holding the values of the lats and lons.... + + need_to_flip_lats = .false. + need_to_flip_lons = .false. + + if (tmplat(1) > tmplon(jmax)) then + do j=1,jmax + glat(j) = tmplat(j) + enddo + else + do j=1,jmax + jix = jmax - j + 1 + glat(jix) = tmplat(j) + enddo + need_to_flip_lats = .true. + endif + + if (tmplon(imax) > tmplon(1)) then + do i=1,imax + glon(i) = tmplon(i) + enddo + else + do i=1,imax + iix = imax - i + 1 + glon(iix) = tmplon(i) + enddo + need_to_flip_lons = .true. + endif + +c do i = 1,imax +c print *,'i= ',i,' glon(i)= ',glon(i) +c enddo +c do j = 1,jmax +c print *,'j= ',j,' glat(j)= ',glat(j) +c enddo + +c --------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have forgotten +c to change the input grid bounds from a global grid run). Modify +c the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) +c +c ABSTRACT: The purpose of this subroutine is to read the "time" +c dimension and "time data" from the NetCDF file so that we know +c how many time levels there are and what those time levels are. +c One reason for doing this is that some models, like the GFDL +c FV3, do not output hour 0 data, so we need to check this first +c before running through the tracking processing for the various +c hours. We will take the list of hours read in here directly from +c the NetCDF file and compare that against the *requested* list of +c forecast hours that the user has entered. The user might not be +c aware that there is no hour 0 data for a given model. We compare +c these two lists of forecast hours and then write a message if +c there is a lead time that is not in the NetCDF file. +c +c INPUT: +c ncfile character name of NetCDF file +c ncfile_id integer id associated with NetCDF file after open +c ifhmax integer max number of lead times that the user has +c requested on the input lead times data file. This +c value was set in subroutine read_fhours. +c netcdfinfo variable of user-defined type netcdfstuff (from +c module netcdf_parms). +c +c OUTPUT: +c ncfile_tmax integer max number of lead times that are in the +c NetCDF file, as read in from this subroutine +c ncfile_has_hour0 character flag (y|n) that tells whether or not +c the input NetCDF data file actually has an hour0 +c record in it or not. +c + USE netcdf_parms; USE tracked_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo + + character :: ncfile*180,ncfile_has_hour0*1,match_check*1 + integer, intent(in) :: ncfile_id + integer, intent(out) :: ncfile_tmax + integer :: infta,k,m,n,ifhmax,irnhret,usertime +c + + irnhret = 0 + ncfile_has_hour0 = 'n' + + !----------------------------------------------------------- + ! First read the NetCDF file to get the number of time levels, + ! which will be returned in "ncfile_tmax".... + !----------------------------------------------------------- + + print *,' ' + print *,'in read_netcdf_hours...' + print *,'ncfile_id= ',ncfile_id + print *,'netcdfinfo%time_name= ',netcdfinfo%time_name + print *,'ncfile_tmax= ',ncfile_tmax + + call get_ncdim1(ncfile_id,netcdfinfo%time_name,ncfile_tmax) + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + print *,'Num netcdf time levs= ncfile_tmax= ',ncfile_tmax + endif + + if (allocated(netcdf_file_time_values)) then + deallocate (netcdf_file_time_values) + endif + + allocate (netcdf_file_time_values(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating' + print *,'!!! netcdf_file_time_values array. infta = ',infta + irnhret = 94 + return + endif + + + !----------------------------------------------------------- + ! Now read in the actual time values that are stored in the + ! NetCDF file.... + !----------------------------------------------------------- + + call get_var1_double (ncfile_id,netcdfinfo%time_name,ncfile_tmax + & ,netcdf_file_time_values) + + if (verb .ge. 1) then + do k = 1,ncfile_tmax + print *,'k= ',k,' netcdf_file_time_values(k)= ' + & ,netcdf_file_time_values(k) + enddo + endif + + !------------------------------------------------------------ + ! Now convert the NetCDF time values into minutes in order to + ! be able to compare with the user-requested list of lead + ! times. Remember that the NetCDF lead times will be listed + ! either as hours or as fractions of days. + !------------------------------------------------------------ + + if (allocated(nctotalmins)) then + deallocate (nctotalmins) + endif + + allocate (nctotalmins(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating ' + print *,'!!! nctotalmins array. infta = ',infta + irnhret = 94 + return + endif + + do k = 1,ncfile_tmax + + if (netcdfinfo%time_units == 'hours') then + nctotalmins(k) = int(netcdf_file_time_values(k)) * 60 + elseif (netcdfinfo%time_units == 'days') then + nctotalmins(k) = int(netcdf_file_time_values(k) * 60. * 24.) + else + print *,' ' + print *,'!!! ERROR: In read_netcdf_hours, the value of' + print *,' netcdfinfo%time_units is neither hours nor days.' + print *,' netcdfinfo%time_units= ',netcdfinfo%time_units + print *,' STOPPING....' + print *,' ' + stop 99 + endif + + if (verb .ge. 1) then + write (6,71) k,netcdf_file_time_values(k),nctotalmins(k) + endif + + enddo + + 71 format (1x,i5,' netcdf_file_time_values(k)= ',f8.4 + & ,' nctotalmins(k)= ',i10) + + !------------------------------------------------------------ + ! Now go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match + ! the two lists up. The big one to watch out for is whether + ! or not the NetCDF file actually has an hour 0 lead time. + !------------------------------------------------------------ + + userloop: do n = 1,ifhmax + + usertime = iftotalmins(n) + + match_check = 'n' + + netcdfloop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + if (verb .ge. 1) then + print *,'+++ Time match for usertime= ',usertime + endif + match_check = 'y' + endif + + enddo netcdfloop + + if (match_check == 'n') then + + if (usertime == 0) then + print *,' ' + print *,'Warning: For a NetCDF file, the user has requested' + print *,'to read in an hour 0 file, however a scan of the' + print *,'time data values in the NetCDF file indicates' + print *,'that there is no hour 0 data in this file. ' + print *,'We will substitute either missing values or ' + print *,'the values from the TC Vitals data in the ' + print *,'hour 0 record and then start searching at the ' + print *,'next lead time.' + ncfile_has_hour0 = 'n' + else + print *,' ' + print *,'!!! ERROR: For a NetCDF file, the user has' + print *,' requested to process a particular lead time that' + print *,' does not exist in the NetCDF list of time ' + print *,' values.' + print *,' n= ',n + print *,' usertime= iftotalmins(n)= ',iftotalmins(n) + print *,' STOPPING....' + stop 99 + endif + + elseif (match_check == 'y') then + + if (usertime == 0) then + if (verb .ge. 1) then + print *,' ' + print *,'+++ For the input NetCDF file, an hour0 data ' + print *,' record exists in the data file.' + endif + ncfile_has_hour0 = 'y' + endif + + endif + + enddo userloop +c + return + end +c +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_valid_point (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) +c +c ABSTRACT: This subroutine checks to see if the input lat/lon +c point is associated with four surrounding (i,j) locations that +c have valid data. The writing of this routine was prompted by the +c HFIP project in February, 2009. Some of their high resolution +c data for their inner nests contained grids that had been rotated +c from native map projections to regular lat/lon grids, but that +c rotation left "empty" spots on the lat/lon grid where there is +c no data. Then when searching in find_maxmin, we were running +c barnes iterations from these lat/lon locations where there was +c no data, which would give artificially low values at those +c lat/lon locations (because the barnes scheme would only include +c points that were relatively far away where there was valid data). +c So in this routine, we call subroutine fix_latlon_to_ij in order +c to get the nearest (i,j) coordinates, and then we check all of +c these points to make sure that valid data exist. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing in i-direction +c dy grid spacing in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c rlatt,rlont input lat/lon about which we will check the +c surrounding (i,j) locations for valid data. +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c icvpret return code from this routine. A value of 0 means that +c all is okay and the input point is surrounded by valid +c data. + + USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,ifix,jfix + integer ifilret,icvpret + character(*) cmaxmin + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real rlont,rlatt,xdum,gridpoint_maxmin + real dx,dy,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +c + call fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt + & ,xdum,ifix,jfix,gridpoint_maxmin,'checker' + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) + + if (ifilret /= 0) then + icvpret = 99 + return + endif + + if (valid_pt(ifix,jfix)) then + icvpret = 0 + else + icvpret = 99 + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.025) then + grfact = 20 + else if (grdspc > 0.025 .and. grdspc <= 0.05) then + grfact = 12 + else if (grdspc > 0.05 .and. grdspc <= 0.10) then + grfact = 6 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif + + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (1*grfact) + iend = ipfix + (2*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (2*grfact) + iend = ipfix + (1*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (1*grfact) + iend = ipfix + (1*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (2*grfact) + jend = jpfix + (1*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (1*grfact) + jend = jpfix + (2*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (1*grfact) + jend = jpfix + (1*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +c print *,' End of fix_latlon_to_ij, gridpoint_maxmin = ' +c & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine rvcal (imax,jmax,dlon,dlat,z,vp) +c +c ABSTRACT: This routine calculates the relative vorticity (zeta) +c from u,v on an evenly-spaced lat/lon grid. Centered finite +c differences are used on the interior points and one-sided +c differences are used on the boundaries. +c +c NOTE: There are 3 critical arrays in this subroutine, the first +c being zeta and the 2nd and 3rd being u and v. There is a +c critical difference in the array indexing for the levels. For +c zeta, the array is dimensioned with levels from 1 to 3, with +c 1 = 850, 2 = 700, 3 = sfc. However, there is an extra level +c for the winds, such that the level dimension goes 1 = 850, +c 2 = 700, 3 = 500, 4 = sfc. So we need to adjust for that in +c this routine. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE trig_vals; USE grid_bounds + USE verbose_output + + implicit none + + dimension cosfac(jmax),tanfac(jmax) + real tmpzeta(imax,jmax) + real xlondiff,xlatdiff,dlon,dlat,dfix + real dlat_edge,dlat_inter,dlon_edge,dlon_inter + real rlat(jmax),cosfac,tanfac + integer z,iscanflag,nlat,nlon,i,j,imax,jmax,w + integer ii,jj + logical(1) vp(imax,jmax) + +c -------------------------- + +c Figure out what level of data we have and what the array +c indices should be. + + if (z == 1) then + ! z = 1 for 850 mb zeta, w = 1 for 850 mb winds + w = 1 + else if (z == 2) then + ! z = 2 for 700 mb zeta, w = 2 for 700 mb winds + w = 2 + else if (z == 3) then + ! z = 3 for sfc zeta, w = 4 for sfc (10m) winds + w = 4 + endif + +c Calculate grid increments for interior and edge points. + +c IMPORTANT: If dtk is defined in module trig_vals in km, then +c we need to multiply by 1000 here to get meters. If it's defined +c as meters, just let it be. Since the wind values are given in +c meters, that's why we need the dlon values to be in meters. + + if (dtk < 750.) then ! chances are, dtk was defined as km + dfix = 1000.0 + else ! dtk was already defined as meters + dfix = 1.0 + endif + + dlon_edge = dtk * dfix * dlon ! Di dist over 1 grid pt + dlat_edge = dtk * dfix * dlat ! Dj dist over 1 grid pt + dlon_inter = dtk * dfix * 2.0 * dlon ! Di dist over 2 grid pts + dlat_inter = dtk * dfix * 2.0 * dlat ! Dj dist over 2 grid pts + + +c Calculate required trig functions. These are functions of +c latitude. Remember that the grid must go from north to south. +c This north-to-south requirement has +c already been checked in subroutine getgridinfo. If necessary, +c any flipping of the latitudes was done there, and flipping of +c the data, again if necessary, was done in subroutine getdata. + + do j=2,jmax-1 + rlat(j) = glatmax - ((j-1) * dlat) + cosfac(j) = cos(dtr*rlat(j)) + tanfac(j) = (tan(dtr*rlat(j)))/erad + enddo + +c Set trig factors at end points to closest interior point +c to avoid a singularity if the domain includes the poles, +c which it will for the global grids (MRF, GDAS, GFS, UKMET,NCE) + + cosfac(1) = cosfac(2) + tanfac(1) = tanfac(2) + cosfac(jmax) = cosfac(jmax-1) + tanfac(jmax) = tanfac(jmax-1) + +c NOTE: These next bits of vorticity calculation code assume that +c the input grid is oriented so that point (1,1) is the upper +c left-most (NW) and point (imax,jmax) is the lower right- +c most point. Any other grids will probably crash the +c program due to array out of bounds errors. +c NOTE: Before each calculation is done, the logical array is +c checked to make sure that all the data points in this +c calculation have valid data (ie., that the points are not +c outside a regional model's boundaries). +c +c !!! IMPORTANT NOTE: While testing this, I uncovered a bug, which was +c that I had the "j+1" and "j-1" reversed. Just from a physical +c understanding, the du/dy term at a point is calculated by taking +c the u value north of the point minus the u value south of the +c point. Intuitively, this is u(j+1) - u(j-1). However, we have +c designed this program to have the northernmost point as +c the beginning of the grid (i.e., for the global grids, j=1 at 90N, +c and j increases southward). Thus, if you would do u(j+1) - +c u(j-1), you would actually be taking the u value south of the +c point minus the u value north of the point, EXACTLY THE OPPOSITE +c OF WHAT YOU WANT. Therefore, the vorticity calculations have +c been changed so that we now have u(j-1) - u(j+1). +c +c UPDATE FEB 2009: With limited domain grids that have missing +c data on them (such as you would have for a grid that has been +c converted from a non-lat/lon grid to a lat/lon grid), we were +c running into problems below with the setting of zeta values to +c a missing value of -999. In place of this, the easiest thing to +c do is to simply assign a value of the background coriolis value +c to that point. No, this is not correct, but it is the easiest +c workaround for this right now. Setting it to zero would be too +c far off. Setting it to the coriolis component has a net effect +c of not having much impact on the barnes scheme result. +c +c --------------- +c Interior points +c --------------- + + if ( verb .ge. 3 ) then + print *,'Just before inter rvcalc, dlon_inter = ',dlon_inter + & ,' dlat_inter = ',dlat_inter + endif + + do j=2,jmax-1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1) .and. vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo + enddo +c +c ----------------------------- +c Bottom (Southernmost) points +c ----------------------------- +c + j=jmax + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------- +c Top (Northernmost) points +c -------------------------- +c + j=1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c ------------------------------- +c Left edge (Westernmost) points +c ------------------------------- +c + i=1 + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------------- +c Right edge (Easternmost) points +c -------------------------------- +c + i=imax + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c --------- +c SW corner +c --------- + i=1 + j=jmax + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i+1,j,w)-v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NW corner +c --------- + i=1 + j=1 + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NE corner +c --------- + i=imax + j=1 + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c SE corner +c --------- + i=imax + j=jmax + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i,j,w)-v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + do ii=1,imax + do jj=1,jmax + tmpzeta(ii,jj) = zeta(ii,jj,z) * 1.e5 + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine thickness_calc (imax,jmax,vp) +c +c ABSTRACT: This routine calculates the thicknesses for three +c different layers: 200-500, 500-850 and 200-850 mb. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE verbose_output + + implicit none + + integer i,j,layer,upper,lower,imax,jmax + logical(1) vp(imax,jmax) + +c -------------------------- + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 +c +c The array indices for the levels for the 4 different GP height +c arrays (as assigned in subroutine getdata) are as follows: +c 1: 850 mb +c 2: 700 mb +c 3: 500 mb +c 4: 200 mb + + + do layer = 1,3 + + select case (layer) + case (1); upper=3; lower=1; + case (2); upper=4; lower=3; + case (3); upper=4; lower=1; + end select + + do j = 1,jmax + do i = 1,imax + + if (vp(i,j)) then + thick(i,j,layer) = hgt(i,j,upper) - hgt(i,j,lower) + else + thick(i,j,layer) = -999.0 + endif + + enddo + enddo + + enddo +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine first_ges_center (imax,jmax,dx,dy,cparm,fxy + & ,cmaxmin,trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) +c +c ABSTRACT: This subroutine scans an array and picks out areas of +c max or min, then loads those center positions into the first- +c guess lat & lon arrays to be used by subroutine tracker for +c locating the very specific low center positions. +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c dx Grid spacing in i-direction for the input grid +c dy Grid spacing in j-direction for the input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c finf Logical. Field of influence. Dimension same as fxy +c cmaxmin Char string to indicate if search is for a max or a min +c trkrinfo Derived type that holds/describes various tracker parms, +c including the contour interval to be used +c ifh Index for the forecast hour +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c contour_info Type cint_stuff from module contours. Contains +c contour information +c +c OUTPUT: +c maxmini Integer array containing i-indeces of max/min locations +c maxminj Integer array containing j-indeces of max/min locations +c ifgcret return code from this subroutine +c +c OTHER: +c storm Contains the tcvitals for the storms (module def_vitals) + + USE trkrparms; USE grid_bounds; USE set_max_parms; USE def_vitals + USE contours; USE tracked_parms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,n,isstart,ifamret,ibeg,jbeg,iend,jend + integer ifh,maxstorm,imax,jmax,itemp,ifgcret + integer stormct,oldstormct,mm + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + character(*) cparm,cmaxmin + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real dmax,dmin,dx,dy,dbuffer,tmp + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of first_ges_center *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for new lows at hour ',i4,':',i2.2) + print *,'*-------------------------------------------------*' + endif + + +c First check the user-supplied grid boundaries to see if we will +c scan the entire array or just a portion of it. + + if (trkrinfo%northbd < -998.0 .or. trkrinfo%southbd < -998.0 .or. + & trkrinfo%westbd < -998.0 .or. trkrinfo%eastbd < -998.0) then + ! User did not specify a subgrid, so scan the whole domain + ibeg = 1 + iend = imax + jbeg = 1 + jend = jmax + else + +c if (trkrinfo%westbd > 360.0 .or. trkrinfo%eastbd < 0.0 .or. +c & trkrinfo%westbd < 0.0 .or. + + if (trkrinfo%westbd > 360.0 .or. + & trkrinfo%northbd > 90.0 .or. trkrinfo%northbd <-90.0 .or. + & trkrinfo%southbd > 90.0 .or. trkrinfo%southbd <-90.0 .or. + & trkrinfo%westbd >= trkrinfo%eastbd .or. + & trkrinfo%southbd >= trkrinfo%northbd) then + + if (trkrinfo%westbd > trkrinfo%eastbd) then + + if (trkrinfo%westbd < 360.0 .and. + & trkrinfo%eastbd >= 0.0)then + + ! In this special case, the user has specified that the + ! western boundary be to the west of the Greenwich + ! meridian and the eastern boundary be to the east of it. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE: The user supplied grid lon boundaries' + print *,'++ span across the Greenwich meridian.' + print *,'++ ' + print *,'++ Western boundary: ',trkrinfo%westbd + print *,'++ Eastern boundary: ',trkrinfo%eastbd + print *,'++ Northern boundary: ',trkrinfo%northbd + print *,'++ Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ! Calculate the beginning and ending i and j points for + ! this case of spanning the Greenwich meridian. The + ! beginning and ending j points are, obviously, the same + ! as for the regular case below in the else. The + ! i-beginning point will also be the same as for the + ! regular case. However, the i-ending point will be + ! modified for the meridian wrap; it will be > imax. + + jbeg = int(((glatmax + dy - trkrinfo%northbd) + & / dy) + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) + & / dy) + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) + & / dx) + 0.5) +c iend = int(((trkrinfo%eastbd - glonmin + dx) +c & / dx) + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) + & / dx) + 0.5) + imax + + goto 377 + + endif + endif + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. There is a' + print *,'!!! problem with the user-supplied grid ' + print *,'!!! boundaries. Please check them and ' + print *,'!!! resubmit the program.' + print *,'!!!' + print *,'!!! Western boundary: ',trkrinfo%westbd + print *,'!!! Eastern boundary: ',trkrinfo%eastbd + print *,'!!! Northern boundary: ',trkrinfo%northbd + print *,'!!! Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ifgcret = 91 + return + + 377 continue + + else + ! Calculate the beginning and ending i and j points.... + jbeg = int(((glatmax + dy - trkrinfo%northbd) / dy) + & + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) / dy) + & + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) / dx) + & + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) / dx) + & + 0.5) + endif + endif + +c Scan the requested portion of the grid and pick out the max and +c min data values, figure out what the max and min contour levels +c will be, and fill an array with the values of the various +c intermediate, incremental contour levels. + + if (trkrinfo%contint <= 0) then + + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. For a midlat' + print *,'!!! or tcgen run of the tracker, the contour' + print *,'!!! interval supplied by the user is not ' + print *,'!!! greater than 0.' + print *,'!!! ' + print *,'!!! User-supplied contint = ',trkrinfo%contint + print *,' ' + endif + + ifgcret = 91 + return + endif + + dmin = 9.99e20 + dmax = -9.99e20 + + do j = jbeg,jend + do i = ibeg,iend + if (i > imax) then + itemp = i - imax ! If wrapping past GM + else + itemp = i + endif + if (valid_pt(itemp,j)) then + if (fxy(itemp,j) < dmin) dmin = fxy(itemp,j) + if (fxy(itemp,j) > dmax) dmax = fxy(itemp,j) + endif + enddo + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*--------------------------------------------*' + print *,'In first_ges_center, dmin= ',dmin,' dmax= ',dmax + endif + + +c We want to allow for storms moving out of the sub-region, +c in which case we might hit slightly lower or higher +c contours than were found in the sub-region, so allow for +c an extra buffer and modify dmin and dmax.... + + dbuffer = (dmax - dmin) / 2.0 + dmax = dmax + dbuffer + dmin = dmin - dbuffer + + if ( verb .ge. 3 ) then + print *,'after adjustment, dmin= ',dmin,' dmax= ',dmax + endif + +c Next 2 lines changed for compiler compatibility on +c other platforms.... +c contour_info%xmaxcont = dmax - amod(dmax,trkrinfo%contint) +c contour_info%xmincont = dmin - amod(dmin,trkrinfo%contint) + + tmp = trkrinfo%contint + contour_info%xmaxcont = dmax - mod(dmax,tmp) + contour_info%xmincont = dmin - mod(dmin,tmp) + + if ( verb .ge. 3 ) then + print *,'A1 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A1 contour_info%xmincont= ',contour_info%xmincont + endif + + if (contour_info%xmincont > contour_info%xmaxcont) then + contour_info%xmincont = contour_info%xmaxcont + endif + +c if (dmin > contour_info%xmincont) then +c contour_info%xmincont=contour_info%xmincont + trkrinfo%contint +c endif +c if (dmax < contour_info%xmaxcont) then +c contour_info%xmaxcont=contour_info%xmaxcont - trkrinfo%contint +c endif + + if ( verb .ge. 3 ) then + print *,'A2 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A2 contour_info%xmincont= ',contour_info%xmincont + print *,'maxconts= ',maxconts + endif + +c NOTE: In the loop below, the contour_info%contvals array is now +c (5/2003) no longer used in subsequent subroutines. But we still +c need to figure out the value of the contvals as we iterate the +c loop so we can know when we've surpassed dmax and can stop +c incrementing contour_info%numcont, which we do need in subsequent +c subroutines. + + contour_info%numcont = 0 + do n = 1,maxconts + contour_info%numcont = contour_info%numcont + 1 + contour_info%contvals(n) = contour_info%xmincont + + & float(n-1)*trkrinfo%contint +c print *,'n= ',n,' contour_info%contvals(n)= ' +c & ,contour_info%contvals(n) + if (contour_info%contvals(n) >= dmax) exit + enddo + + oldstormct = stormct + call find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) + + if (stormct > 0) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' ' + print *,'!!! ************************************************' + print *,'!!! ' + print *,'!!! NOTE: In first_ges_center, the value of stormct' + print *,'!!! returned from find_all_maxmins is not greater' + print *,'!!! than 0. This means there are no new centers' + print *,'!!! to track, which is not likely. Perhaps you are' + print *,'!!! searching over too small of an area??' + print *,'!!! ' + print *,'!!! ************************************************' + print *,' ' + endif + + endif + + print *,'ifh= ',ifh,' oldstormct= ',oldstormct + print *, ' stormct= ',stormct + + do mm = 1,300 + print *,'mm= ',mm,' maxmini(mm)= ',maxmini(mm) + & ,' maxminj(mm)= ',maxminj(mm) + enddo + + if (stormct > oldstormct .and. stormct > 0) then + isstart = oldstormct + 1 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,*) 'New search: ' + write (6,*) 'Possible new max/min locations at ifh= ',ifh + write (6,*) '--------------------------------------------' + endif + + do n = isstart,stormct + if (trkrinfo%type == 'midlat') then + storm(n)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(n)%tcv_center = 'TCG ' + endif + slonfg(n,ifh) = glonmin + (maxmini(n)-1)*dx + slatfg(n,ifh) = glatmax - (maxminj(n)-1)*dy + storm(n)%tcv_stspd = -99 + storm(n)%tcv_stdir = -99 + write (storm(n)%tcv_storm_id,'(i4.4)') n + write (storm(n)%tcv_storm_name,'(i4.4)') n + stormswitch(n) = 1 + if (cparm == 'mslp') then + + if ( verb .ge. 3 ) then + write (6,71) maxmini(n),maxminj(n),slonfg(n,ifh) + & ,360.-slonfg(n,ifh),slatfg(n,ifh) + & ,slp(maxmini(n),maxminj(n))/100.0 + endif + + endif + enddo + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' New search: ' + print *,'!!! NOTE: No new storms found in find_all_maxmins' + print *,'!!! at ifh = ',ifh,' stormct= ',stormct + print *,'!!! oldstormct= ',oldstormct + print *,' ' + endif + + endif + + 71 format (1x,'i= ',i4,' j= ',i4,' lon: ',f7.2,'E (',f6.2,'W)' + & ,2x,' lat: ',f6.2,' mslp: ',f6.1,' mb') +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) +c +c ABSTRACT: This subroutine will search an area delineated by +c input i and j indeces in order to find all local maxes or mins +c in that area. The (i,j) locations of the maxes/mins are returned +c in the maxmini and maxminj arrays. The input 3-character string +c cmaxmin will tell the subroutine to look for a "max" or a "min". +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c ibeg i-index for upper left location of grid to search +c iend i-index for lower right location of grid to search +c jbeg j-index for upper left location of grid to search +c jend j-index for lower right location of grid to search +c fxy Real array of data values +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c contour_info Type cint_stuff from module contours containing the +c the following 4 variables: +c 1. xmincont Real value for min contour level in the fxy data array +c 2. xmaxcont Real value for max contour level in the fxy data array +c 3. contvals Real array holding values of cont levels at this time +c 4. numcont Number of contour intervals found at this time +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c trkrinfo derived type containing various user-input tracker parms +c cmaxmin String that declares if "min" or "max" is being searched +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c +c OUTPUT: +c maxmini integer array containing i-indeces of the max/min points +c maxminj integer array containing j-indeces of the max/min points +c ifamret return code from this subroutine + + USE trkrparms; USE set_max_parms; USE contours + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + integer stormct,i,j,ibeg,iend,jbeg,jend,ix,jx,ixp1,ixm1 + integer ip,jp,maxstorm,jxp1,jxm1,ifamret,isret,iaret,iclmret + integer isoiret,icccret,igicwret,imax,jmax + character ccflag*1,get_last_isobar_flag*1,point_is_over_water*1 + character(*) cmaxmin + logical(1) still_finding_valid_maxmins,rough_gradient_check_okay + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real xavg,stdv,search_cutoff,dmin,dmax,sphere_cutoff + real plastbar,rlastbar,fract_land,dx,dy + +c----- + still_finding_valid_maxmins = .true. + + +c print *,'ctm beg of find_all_maxmins, maxstorm= ',maxstorm + + +c First, we want to get the mean and standard deviation of the input +c field to be searched. We can use the standard deviation info as +c part of our guideline for when to stop searching for maxes & mins. +c We will set the search cut-off threshold at 1/2 standard deviation +c above the mean for min searches. So, for the example of mslp, if +c the mean pressure over the whole domain is 1010 mb and the +c standard deviation is 12 mb, then when we are searching, if the +c lowest available (i.e., hasn't been found in a previous iteration +c of this loop) pressure is 1016, then it's time to stop searching. + + call avgcalc (fxy,imax*jmax,valid_pt,xavg,iaret) + call stdevcalc (fxy,imax*jmax,valid_pt,xavg,stdv,isret) + if (iaret /= 0 .or. isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_all_maxmins, the calls to avgcalc' + print *,'!!! and/or stdevcalc returned an error.' + print *,'!!! iaret= ',iaret,' isret= ',iaret + print *,' ' + endif + + ifamret = 98 + return + endif + + if (cmaxmin == 'min') then + search_cutoff = xavg + stdv*0.5 + else + search_cutoff = xavg - stdv*0.5 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In find_all_maxmins, search_cutoff= ',search_cutoff + print *,' ' + endif + +c Now begin to search the domain. We do a simple gridpoint scan, +c and once we find the max/min value, we pass the (i,j) coordinates +c at that point to a routine to check for a closed contour. Then +c we mask out those points in the contour (or, if there is not a +c closed contour, just the 8 points immediately surrounding the low +c center) and we do another iteration of search_loop to look for +c more lows. We mask out points we've found so that on subsequent +c iterations of search_loop, we don't find the same old center +c again and again and again..... + + search_loop: do while (still_finding_valid_maxmins) + + dmin = 9.99e20 + dmax = -9.99e20 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + ip = i + jp = j + + if (ip > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: In find_all_maxmins, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. The search' + print *,'!!! will not extend to the user-requested' + print *,'!!! grid boundary.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',ip + print *,' ' + endif + + exit iloop + + endif + endif + + if (valid_pt(ip,jp) .and..not. masked_out(ip,jp)) then + if (cmaxmin == 'min') then + if (fxy(ip,jp) < dmin) then + dmin = fxy(ip,jp) + ix = ip + jx = jp + endif + else + if (fxy(ip,jp) > dmax) then + dmax = fxy(ip,jp) + ix = ip + jx = jp + endif + endif + endif + + enddo iloop + enddo jloop + + if (cmaxmin == 'min') then + if (dmin < search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + else + if (dmax > search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + endif + +c As a rough first check, see if the neighboring points on all +c 4 sides have a gradient sloping down into the found min point, +c or at least that there is a flat field not having a gradient +c sloping away from the center point. + + call get_ijplus1_check_wrap (imax,jmax,ix,jx,ixp1,jxp1,ixm1 + & ,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In find_all_maxmins, the center we found' + print *,'!!! is too close to the grid boundary and will' + print *,'!!! NOT be checked for a closed contour.' + print *,'!!! ix= ',ix,' jx= ',jx,' fxy= ',fxy(ix,jx) + print *,'!!! ' + print *,' ' + endif + + masked_out(ix,jx) = .true. + cycle search_loop + endif + + if (cmaxmin == 'min') then + if (fxy(ix,jx) <= fxy(ixp1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxm1) .and. + & fxy(ix,jx) <= fxy(ixm1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + else + if (fxy(ix,jx) >= fxy(ixp1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxm1) .and. + & fxy(ix,jx) >= fxy(ixm1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + endif + + if (rough_gradient_check_okay) then + + if ( verb .ge. 3 ) then + print *,'Found a possible max/min at ix= ',ix,' jx= ',jx + endif + + +c From this rough check, we appear to have a gradient sloping +c in towards the center point. Now call the subroutine to +c check whether or not there is in fact a closed contour +c surrounding this local maximum or minimum. + + get_last_isobar_flag = 'n' + ccflag = 'n' + call check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,ccflag,cmaxmin,trkrinfo + & ,1,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if (ccflag == 'y') then + if (stormct < maxstorm) then + stormct = stormct + 1 + + if ( verb .ge. 3 ) then + print *,'AAA stormct= ',stormct,' ix= ',ix,' jx= ',jx + endif + + ! For a tcgen case, we will add in one additional check, + ! and that is to ensure the point is (mostly) over water. + ! Only do this check if the user has requested it (some + ! of the global models do not have a land-sea mask + ! included in the grib data files). + + point_is_over_water = 'u' + + if (trkrinfo%use_land_mask == 'y') then + call check_land_mask (imax,jmax,ix,jx,fract_land + & ,valid_pt,dx,dy,point_is_over_water,iclmret) + if (iclmret /= 0) then + print *,' ' + print *,'!!! ERROR from check_land_mask for ix= ',ix + & ,' jx= ',jx + print *,'!!! STOPPING PROGRAM' + stop 95 + endif + endif + + if (point_is_over_water /= 'n') then + maxmini(stormct) = ix + maxminj(stormct) = jx + endif + + else + + if ( verb .ge. 3 ) then + print *,'---max stormct reached, stormct= ', stormct + endif + + endif + else + + if ( verb .ge. 3 ) then + print *,'!!! contour check negative, ccflag= ',ccflag + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*-----------------------------------------------*' + print *,' ' + endif + + endif + +c Regardless of whether or not the found point turns out to have +c a closed contour, we don't want to find this local minimum or +c its 8 surrounding points again in a search on a subsequent +c iteration of this loop. + + masked_out(ix,jx) = .true. + masked_out(ix,jxp1) = .true. + masked_out(ixp1,jxp1) = .true. + masked_out(ixp1,jx) = .true. + masked_out(ixp1,jxm1) = .true. + masked_out(ix,jxm1) = .true. + masked_out(ixm1,jxm1) = .true. + masked_out(ixm1,jx) = .true. + masked_out(ixm1,jxp1) = .true. + + enddo search_loop + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine mask_based_on_wind_circ (imax,jmax,dx,dy,level + & ,valid_pt,masked_outc,trkrinfo + & ,ctlon,ctlat,cmodel_type,imbowret) +c +c ABSTRACT: This subroutine masks out grid points for a storm that +c is currently being tracked. It is called after a fix has been +c made at the current forecast hour. It is only used as a backup, +c that is, if the mslp data were not there and/or a fix position +c for mslp could not be made, then that means that the mask would +c not be able to get updated using the routine in subroutine +c check_closed_contour. But we still do need to update that mask, +c so we will instead do it based on wind circulation. We will go +c out radially from the center, starting at 40 km, then every +c 40 km from there on out. When the mean cyclonic Vt drops below +c 3 m/s, stop searching, and then mask out all grid points within +c that last-searched radius. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_outc Logical. T = data point is already accounted for, +c under the influence of another nearby max or min +c center; F = data point is available to be scanned by +c this subroutine for max or min centers. +c ctlon Fix longitude for the input parameter to this routine +c ctlon Fix latitude for the input parameter to this routine +c cmodel_type character, 'global' or 'regional' + + USE set_max_parms; USE trkrparms; USE grid_bounds + USE verbose_output; USE level_parms + + implicit none + + type (trackstuff) trkrinfo + + character(*) cmodel_type + integer, parameter :: numazim=24 + integer imax,jmax,level,imbowret,nlev,iazim,i,j + integer ibiret1,ibiret2,azimuth_ct,igvtret + integer jnfix,jsfix,iefix,iwfix + real vr(numazim),vt(numazim) + real dx,dy,ctlon,ctlat,rdist,bear,targlat,targlon + real xintrp_u,xintrp_v,grid_buffer,xmax_rdist_reached + real vt_mean,vt_azim_sum,xbear,dist,degrees + logical(1) valid_pt(imax,jmax),masked_outc(imax,jmax) + logical(1) searching_valid_pts + + imbowret = 0 + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + searching_valid_pts = .true. + + rdist = 40.0 ! units in km + xmax_rdist_reached = rdist ! units in km + + radial_loop: do while (searching_valid_pts) + + azimuth_ct = 0 + vt_azim_sum = 0.0 + vt = -999.0 + vr = -999.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (ctlat,ctlon,rdist,bear,targlat,targlon) + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + xmax_rdist_reached = rdist + exit radial_loop + endif + + ! These calls to bilin_int_uneven pass a variable, level, + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (ctlon,ctlat,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim) + & ,vt(iazim),igvtret) + azimuth_ct = azimuth_ct + 1 + vt_azim_sum = vt_azim_sum + vt(iazim) + else + xmax_rdist_reached = rdist + exit radial_loop + endif + + enddo azimloop + + if (azimuth_ct > 0) then + ! Compute azimuthally-averaged Vt at this distance + vt_mean = vt_azim_sum / float(azimuth_ct) + else + vt_mean = -999.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: rdist= ',rdist,' azimuth_ct= ',azimuth_ct + & ,' vt_azim_sum= ',vt_azim_sum,' vt_mean= ',vt_mean + endif + + if (ctlat >= 0.0) then + if (vt_mean >= 3.0) then + ! For a NH storm, if the cyclonic mean Vt >= 3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + else + if (vt_mean <= -3.0 .and. vt_mean > -998.0) then + ! For a SH storm, if the cyclonic mean Vt <= -3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + enddo radial_loop + + if ( verb .ge. 3 ) then + print *,'mbow: After radial_loop, rdist= ',rdist + & ,' xmax_rdist_reached= ',xmax_rdist_reached + endif + +c ----------------------------------------------------------------- +c At this point, we are done searching radially outwards away from +c the storm center. The max radial distance we reached is called +c xmax_rdist_reached. By getting to this spot in the subroutine, +c that means that we bumped out of radial_loop above because the +c rdist being used in that loop got to a radius at which the mean +c cyclonic Vt no longer was strong enough to continue the search +c outward, so we need to reduce it by 40 km here (back to the value +c for the last successful search). At a minimum, we will mask to a +c radius of 80 km. + + if (xmax_rdist_reached > 80.0) then + xmax_rdist_reached = xmax_rdist_reached - 40.0 + else + xmax_rdist_reached = 80.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: After adjustment of xmax_rdist_reached, rdist= ' + & ,rdist,' xmax_rdist_reached= ',xmax_rdist_reached + endif + + bearloop: do i = 1,4 + + ! Now find the values of the longitude for the farthest west + ! and east points and find the values of the latitude for the + ! farthest north and south points. The i and j indices + ! associated with these lons and lats will be used to define + ! the bounds of the grid over which we scan to find points + ! that will update the mask. + + select case (i) + case (1); xbear = 0.0; + case (2); xbear = 90.0; + case (3); xbear = 180.0; + case (4); xbear = 270.0; + end select + + call distbear (ctlat,ctlon,xmax_rdist_reached,xbear + & ,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,'mbow: distbear for i= ',i,' targlon= ',targlon + & ,' targlat= ',targlat + endif + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon > glonmax for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmax= ',glonmax + imbowret = 95 + return + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon < glonmin for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmin= ',glonmin + imbowret = 95 + return + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'either targlat > glatmx or targlat < glatmin, so' + print *,'we cannot update the mask.' + print *,'targlat= ',targlat,' glatmin= ',glatmin + print *,' glatmin= ',glatmin + imbowret = 95 + return + cycle bearloop + endif + + ! Get the i & j starting and ending points for our loop where + ! we will update the mask.... + + if (i == 1) then + + ! Get j for northern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jnfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jnfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 2) then + + ! Get i for eastern longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iefix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + elseif (i == 3) then + + ! Get i for southern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jsfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jsfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 4) then + + ! Get i for western longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iwfix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + endif + + enddo bearloop + + if ( verb .ge. 3 ) then + print *,'mbow: iwfix= ',iwfix,' iefix= ',iefix + & ,' jnfix= ',jnfix,' jsfix= ',jsfix + endif + + do i = iwfix,iefix + do j = jnfix,jsfix + + call calcdist (glon(i),glat(j),ctlon,ctlat,dist,degrees) + + if (dist < xmax_rdist_reached) then + masked_outc(i,j) = .true. + endif + + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,closed_contour,cmaxmin,trkrinfo + & ,num_requested_contours,contour_info + & ,get_last_isobar_flag,plastbar,rlastbar,icccret) +c +c ABSTRACT: This subroutine checks a field of data around an input +c (ix,jx) data point to see if a closed contour exists around +c that data point. It can check for a closed contour on a max or a +c min field, depending on the value of the input variable 'cmaxmin'. +c The algorithm works by examining rings of the 8 data points +c surrounding a data point that is in the contour interval. For +c example, in the diagram below, the X represents the location of +c the local minimum value which was passed into this routine with +c the coordinates (ix,jx), let's say it's 985 mb. And let's assume +c that the data values at points A-I are all in the 4 mb contour +c interval of 985-989 mb, and that all the surrounding points have +c data values >= 989. To test for a closed contour, we first check +c the ring of 8 points immediately around point X to see what their +c data values are. If a data value is found that is below the +c lower limit of this contour interval (985 mb) or lower than the +c local minimum value at the X point that we initially targeted +c (985 mb), then we do NOT have a closed contour, and we exit this +c subroutine. But in our example, that's not the case, and we have +c 5 points (B,D,E,F,G) that are in the interval. So in our next +c iteration of the loop, we set up 5 rings, each one set up around +c the points found in the first iteration (B,D,E,F,G), and we check +c the 8 points around each of those points. A logical array is +c used so that as soon as a point is found, it is flagged as being +c found. In this way, when we look at the ring around point D, for +c example, we won't pick point X again and set up another ring +c around it in the next ring iteration and end up in an infinite +c loop, going back and forth between point X and point D. While +c checking the 8 points in a ring, if a found data value is above +c our contour interval (i.e., >= 989 mb), we just ignore the +c point; we only mark points that are in our contour interval, +c and again, if we find a point below our contour interval, we +c exit the subroutine with a flag indicating a closed contour was +c NOT found. So in this method, we keep spreading out from the +c initial local minimum and creating and checking new rings until +c we either: (a) Hit the edge of the regional grid, in which case +c we consider a closed contour NOT found, (b) Run into a data +c point that has been marked as being under the influence of +c another nearby low, in which case we consider a closed contour +c NOT found, (c) Run into a point which is below (above) our +c contour interval for a min (max) check, in which case we +c consider a closed contour NOT found, or (d) we run out of +c points to keep searching, we have no rings left to create and +c check because all of the surrounding points are above (below) +c our contour interval for a min (max) check, and by default we +c consider this a closed contour and return to the calling +c subroutine a flag indicating such. +c +c + + + + + + + + + + +c + + + + + + + + + + +c + + A B + + + + + + +c + + C D X E + + + + +c + + + + F G + + + + +c + + + + + H I + + + +c + + + + + + + + + + +c + + + + + + + + + + +c +c UPDATE: This subroutine was updated to keep searching for +c multiple closed contours until it can't find anymore. The +c input parameter num_requested_contours dictates how many +c contours to search for. In the case of just trying to roughly +c locate new centers and establish that there is a closed +c circulation, num_requested_contours will = 1, and we will exit +c after finding that 1 contour. But for a check after making a +c full center fix, we set num_requested_contours = 999 so that +c we can keep searching for all closed contours around the low. +c In this 999 case, you will eventually get to a point where +c there is no closed contour. In that case, in the standard +c output you will see a message telling you that you hit a point +c that is not in the contour and that there is no closed contour, +c but you will also notice that the ccflag = y, meaning there is +c a closed contour (because you have found at least 1 closed +c contour along the way). The reason to keep searching for more +c closed contours is that we can then return the value of the +c outermost closed isobar. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c cmaxmin character string ('max' or 'min') that tells this +c routine what we're looking for. +c trkrinfo derived type that holds/describes various tracker parms +c contour_info Type cint_stuff from module contours. Contains +c contour information +c num_requested_contours For the simple first_ges_center check, +c this will be 1 (we just want to know if there's at +c least 1 closed contour). For the verifying check after +c we've found a center, this will be 9999 (i.e., just keep +c searching for more contours) +c get_last_isobar_flag character ('y' or 'n') to indicate whether +c or not to report on the value of the last closed isobar +c and the radius of the last closed isobar. +c +c OUTPUT: +c closed_contour character; A returned value of 'y' indicates that +c this routine was able to find a closed contour. +c plastbar Contains the value of the last closed isobar (unrounded) +c rlastbar Contains the mean radius of the last closed isobar +c +c LOCAL: +c num_pts_in_all_contours Counter for the number of pts inside of +c the contour we're looking at +c next_ring_ct Counter for the number of points that have been +c tagged to be used as center points for the next +c iteration of multiple_ring_loop. +c next_contour_ct Counter for the number of points that have been +c tagged to be used as center points in the first iteration +c through single_contour_scan_loop as we begin to scan +c points in the *next* contour interval. This counter gets +c incremented when, for example, we are searching points +c around a current center point and we find one that is not +c in our current interval, but rather is in the next +c interval. We want to remember this point and store the +c location, so we increment this counter and store the +c location in next_contour_i and next_contour_j arrays. +c beyond_contour_ct Counter for the number of points that have been +c tagged to be used as center points for some subsequent +c iteration of successive_contours_loop. This is +c different from next_contour_ct, which is used to hold +c the locations of points that are definitely in the +c *next* contour interval. Here, we have points that we +c just store in a pool of potential points to be searched +c in future iterations. These points can come about in +c cases where there is a very intense, very compact low +c with a tight pressure gradient, such that multiple +c contour intervals could be spanned in between 2 adjacent +c gridpoints (this is especially the case if the contour +c interval you have chosen is small). You need to be +c careful with how you handle this array. Once you find +c that you have searchable points in next_contour_i or +c next_contour_j, do not just simply empty out this +c beyond_contour count and its i and j arrays. The +c reason being that some of these "beyond" points may end +c up being used and searched in subsequent iterations, but +c not if we just delete them now. + + + USE set_max_parms; USE trkrparms; USE contours; USE grid_bounds + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,ir,iria,irja,irx,jrx,ix,jx,imax,jmax + integer nb,ibx,jby,nct,iflip + integer mr,ringct,ixp1,ixm1,jxp1,jxm1,nring,iter + integer icenx,jcenx,icccret,next_ring_ct,igicwret + integer num_pts_in_all_contours,next_contour_ct + integer beyond_contour_ct + integer num_pts_in_one_contour + integer num_requested_contours,num_found_contours + integer nm,im,jm,inall,insingle,isc_count,rlast_distct + character found_a_point_in_our_contour*1,closed_contour*1 + character found_a_point_below_contour*1 + character found_a_point_above_contour*1,get_last_isobar_flag*1 + character(*) cmaxmin + logical(1) still_scanning + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + logical(1) point_is_already_in_our_contour(imax,jmax) + logical(1) point_is_already_in_next_contour(imax,jmax) + logical(1) point_is_already_in_beyond_pool(imax,jmax) + integer isni,isnj,inci,incj,ibci,ibcj,ihmi,ihmj,itmi,itmj + integer, allocatable :: search_next_i(:) + integer, allocatable :: search_next_j(:) + integer, allocatable :: next_contour_i(:) + integer, allocatable :: next_contour_j(:) + integer, allocatable :: beyond_contour_i(:) + integer, allocatable :: beyond_contour_j(:) + integer, allocatable :: hold_mask_i_loc(:) + integer, allocatable :: hold_mask_j_loc(:) + integer, allocatable :: temp_mask_i_loc(:) + integer, allocatable :: temp_mask_j_loc(:) + integer, allocatable :: ringposi(:),ringposj(:) + real,allocatable :: ringpos(:,:) + real fxy(imax,jmax),contvals(maxconts) + real contlo,conthi,xcentval,contlo_next,conthi_next + real dist,degrees,rlast_distsum,plastbar,rlastbar +c + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + allocate (search_next_i(imax*jmax),stat=isni) + allocate (search_next_j(imax*jmax),stat=isnj) + allocate (next_contour_i(imax*jmax),stat=inci) + allocate (next_contour_j(imax*jmax),stat=incj) + allocate (beyond_contour_i((imax*jmax)/2),stat=ibci) + allocate (beyond_contour_j((imax*jmax)/2),stat=ibcj) + allocate (hold_mask_i_loc(imax*jmax),stat=ihmi) + allocate (hold_mask_j_loc(imax*jmax),stat=ihmj) + allocate (temp_mask_i_loc(imax*jmax),stat=itmi) + allocate (temp_mask_j_loc(imax*jmax),stat=itmj) + if (isni /= 0 .or. isnj /= 0 .or. inci /= 0 .or. incj /= 0 .or. + & ibci /= 0 .or. ibcj /= 0 .or. ihmi /= 0 .or. ihmj /= 0 .or. + & itmi /= 0 .or. itmj /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various search, hold and temp arrays.' + print *,'!!! isni = ',isni,' isnj= ',isnj + print *,'!!! inci = ',inci,' incj= ',incj + print *,'!!! ibci = ',ibci,' ibcj= ',ibcj + print *,'!!! ihmi = ',ihmi,' ihmj= ',ihmj + print *,'!!! itmi = ',itmi,' itmj= ',itmj + print *,' ' + endif + + STOP 98 + endif + + closed_contour = 'n' + xcentval = fxy(ix,jx) + num_found_contours = 0 + next_contour_ct = 0 + beyond_contour_ct = 0 + num_pts_in_all_contours = 0 + hold_mask_i_loc = 0 + hold_mask_j_loc = 0 + beyond_contour_i = 0 + beyond_contour_j = 0 + point_is_already_in_our_contour = .false. + point_is_already_in_beyond_pool = .false. + icccret = 0 + isc_count = 0 + plastbar = -999.0 + rlastbar = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* Top of check_closed_contour, ix= ',ix,' jx= ',jx + print *,'*-----------------------------------------------*' + print *,' ' + print *,'fxy(ix,jx)= ',fxy(ix,jx),' xcentval= ',xcentval + endif + +c First, set up the contour intervals that will be used. In +c the original version of this code, we used preset +c standard intervals (984,988,992,996,1000,1004....). But upon +c further review, it was decided that this was too arbitrary. +c So instead, we consider the found min (max) value to be the +c bottom (top) of the list of contour intervals. In this way, +c we can clearly specify and screen storms based on the "depth" +c of the pressure field as compared to the surroundings. + + i = 1 + do while (i <= maxconts) + if (cmaxmin == 'min') then + contvals(i) = xcentval + float(i-1)*trkrinfo%contint + i = i + 1 + else + iflip = maxconts - i + 1 + contvals(iflip) = xcentval - float(i-1)*trkrinfo%contint + i = i + 1 + endif + enddo + +c This successive_contours loop is the master loop.... + + successive_contours_loop: do while (num_found_contours < + & num_requested_contours) + +c Find the contour interval in which the center value resides. +c Note that the lower bound is included for a min check, while +c the upper bound is included for a max check. Note also that +c this subroutine can be used to find the last closed contour, +c and part of that functionality shows up in the next while +c statement where we reference "num_found_contours" in the +c array indeces for the contour values. Basically, the way we +c do this is, for example, if our central value is 990.4 mb and +c our contour interval is 4 mb, then in the first run through +c successive_contours_loop we see if we have a closed contour in +c the interval 990.4-994.4. If yes, then the next time through +c this loop, we see if we have a closed contour in the interval +c 994.4-998.4. If yes, then the next loop check is for 998.4- +c 1002.4, and so on.... We stop searching if we find a value +c that is either below the xcentval input into this subroutine +c or below the lower value of the current contour interval (this +c would mean a change in the gradient and would indicate that, +c in the case of mslp, we are heading down towards another, +c different low). + + isc_count = isc_count + 1 + + point_is_already_in_next_contour = .false. + + i = 1 + do while (i < maxconts) + if (cmaxmin == 'min') then + if (contvals(i) <= xcentval .and. xcentval < contvals(i+1)) + & then + + if ( verb .ge. 3 ) then + print *,'At A, num_found_contours= ',num_found_contours + endif + + contlo = contvals(i+num_found_contours) + conthi = contvals(i+1+num_found_contours) + + if ( verb .ge. 3 ) then + print *,'At A, contlo= ',contlo,' conthi= ',conthi + endif + exit + + endif + else + if (contvals(i) < xcentval .and. xcentval <= contvals(i+1)) + & then + contlo = contvals(i-num_found_contours) + conthi = contvals(i-num_found_contours+1) + exit + endif + endif + i = i + 1 + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'num_found_contours= ',num_found_contours + print *,'contlo= ',contlo,' conthi= ',conthi + print *,'xcentval= ',xcentval + endif + + +c This single_contour_scan_loop is the main loop for searching +c for one individual contour. If it is determined that a contour +c exists, control is returned to the successive_contours_loop, +c and if more contours were requested to be found, then the +c search continues onward & outward.... + + temp_mask_i_loc = 0 + temp_mask_j_loc = 0 + + iter = 1 + num_pts_in_one_contour = 0 + still_scanning = .true. + + rlast_distsum = 0.0 + rlast_distct = 0 + + single_contour_scan_loop: do while (still_scanning) + +c print *,' ' +c print *,' top of single contour scan loop' +c print *,'+++ iter= ',iter +c print *,' N1: next_contour_ct= ',next_contour_ct + + if (iter == 1 .and. num_found_contours == 0) then + ! For the first iteration, we have only the first ring, + ! which is centered on the input minimum/maximum point. + ringct = 1 + search_next_i(1) = ix + search_next_j(1) = jx + +c point_is_already_in_our_contour(ix,jx) = .true. +c num_pts_in_one_contour = num_pts_in_one_contour + 1 +c temp_mask_i_loc(num_pts_in_one_contour) = ix +c temp_mask_j_loc(num_pts_in_one_contour) = jx + + else if (iter == 1 .and. num_found_contours > 0) then + ! This is the first iteration in a *new* contour. + ! That is, we have already found 1 or more previous + ! contours while in previous iterations of + ! successive_contours_loop and we are now beginning + ! to look for the next contour. + +c print *,' N2: next_contour_ct= ',next_contour_ct + + if (next_contour_ct == 0) then + ! This would be for the special case in which, for + ! example, you've got a very intense, compact storm + ! that "skips" a contour. That is, suppose the + ! min pressure of a storm is 982 mb, and we are + ! utilizing a 4-mb contour interval, but all + ! surrounding data points are, say, 987 mb or + ! higher. Then, next_contour_ct would be 0 since no + ! data points were found in the next contour interval + ! of 982-986 mb, but we can continue searching since the + ! gradient is still sloping the correct way. The code in + ! this if statement handles this special case. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ALERT: next_contour_ct = 0 ' + endif + + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + +c print *,'b4 ZZ, ringct= ',ringct +c print *,'at ZZ, bcc= ',beyond_contour_ct +c & ,'contlo_next= ',contlo_next +c & ,'conthi_next= ',conthi_next + + bey_con_min_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_min_loop + endif + +c print *,'-- ZZ, ibx= ',ibx,' jby= ',jby +c & ,' fxy(ibx,jby)= ',fxy(ibx,jby) + + if (fxy(ibx,jby) >= contlo_next .and. + & fxy(ibx,jby) < conthi_next) then + +c print *,'>> ZZ HIT!!, ibx= ',ibx,' jby= ',jby +c +c print *,' +++ BEYOND in NEXT: i= ',ibx,' j= ',jby +c & ,' fxy= ',fxy(ibx,jby) + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + +c print *,'.. ZZ, next_contour_ct= ',next_contour_ct + + enddo bey_con_min_loop + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + +c print *,'At A, beyond_contour_ct= ',beyond_contour_ct +c print *,' contlo_next = ',contlo_next +c print *,' conthi_next = ',conthi_next + + bey_con_max_loop: do nb = 1,beyond_contour_ct + +c print *,'in bey_con_max_loop, nb= ',nb + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_max_loop + endif + +c print *,'ibx= ',ibx,' jby= ',jby,' data= ' +c & ,fxy(ibx,jby) + + if (fxy(ibx,jby) > contlo_next .and. + & fxy(ibx,jby) <= conthi_next) then + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + +c print *,' ++ HIT! ibx= ',ibx,' jby= ',jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + enddo bey_con_max_loop + endif + + if (next_contour_ct > 0) then + ringct = next_contour_ct + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! XXX next_contour_ct not > 0 !!!' + print *,'next_contour_ct= ',next_contour_ct + print *,'beyond_contour_ct= ',beyond_contour_ct + print *,'ringct= ',ringct + print *,'next_ring_ct= ',next_ring_ct + print *,'cycling to top of successive_contours_loop..' + print *,' ' + endif + + ! The number of rings that we have available to search + ! in the next contour interval is 0, so cycle all the + ! way back to the top of the outer loop, which is + ! successive_contours_loop, so that we can increase the + ! contour bounds and search inside those new bounds. + ! Again, this is for the case in which we have an + ! intense, compact storm and we are using a small + ! contour interval, such that we are essentially + ! "skipping" over one of these intervals in one of the + ! loop iterations. We need to bump up the + ! num_found_contours by one in order to increase the + ! array index in the contvals array at the top of the + ! successive_contours_loop. It is kosher to do this + ! since the reason we are cycling back to the top of + ! that loop is that we are skipping over a contour + ! interval. + + num_found_contours = num_found_contours + 1 + cycle successive_contours_loop + + endif + + else + + ringct = next_contour_ct + + endif + + do nring = 1,ringct + search_next_i(nring) = next_contour_i(nring) + search_next_j(nring) = next_contour_j(nring) +c print *,'at A, nring= ',nring,' next_contour_i(nring)= ' +c & ,next_contour_i(nring),' next_contour_j(nring)= ' +c & ,next_contour_j(nring) + enddo + + next_contour_ct = 0 + + else + ringct = next_ring_ct + endif + + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + allocate (ringposi(ringct),stat=iria) + allocate (ringposj(ringct),stat=irja) + if (iria /= 0 .or. irja /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various ring arrays. iria = ',iria + print *,'!!! irja = ',irja + print *,' ' + endif + + STOP 98 + endif + +ctm +c print *,' ' +c print *,'ringct= ',ringct + + do nring = 1,ringct + ringposi(nring) = search_next_i(nring) + ringposj(nring) = search_next_j(nring) +ctm +c print *,'nring= ',nring,' ringposi= ',ringposi(nring) +c & ,' ringposj= ',ringposj(nring) + enddo + + next_ring_ct = 0 + + ! This next loop reviews the points that have been + ! labelled for the "beyond_contour" pool. As we get further + ! into successive iterations of successive_contours_loop, + ! some of these previously "beyond" points are now within + ! the contour interval range that we are checking, so we + ! need to go through the list of "beyond" points and remove + ! any that are no longer in that "beyond" category.... + + check_beyond_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! This point may have been removed already in a + ! previous iteration of successive_contours_loop. + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle check_beyond_loop + endif + + ! Check to see if any of the points being searched in the + ! upcoming multiple_ring_loop are points that had previously + ! been saved as "beyond_contour" points. If so, remove + ! their status as "beyond_contour" points by setting the + ! logical flag to false. + + do nring = 1,ringct + + if (ibx == ringposi(nring) .and. jby == ringposj(nring)) + & then +c print *,' ' +c print *,'!!! beyond remove: ibx= ',ibx,' jby= ',jby + point_is_already_in_beyond_pool(ibx,jby) = .false. + endif + + enddo + + enddo check_beyond_loop + + +c In each iteration of single_contour_scan_loop, we can have a +c different number of rings to analyze. In the first +c iteration, we only have 1 ring, the initial ring around the +c local max/min that was input to this subroutine. Subsequent +c iterations will have a variable number of rings, depending on +c how many new data points within our contour interval were +c found in the previous iteration. + + multiple_ring_loop: do mr = 1,ringct + + icenx = ringposi(mr) + jcenx = ringposj(mr) + +ctm +c print *,' --- iter= ',iter,' mr= ',mr,' icenx= ',icenx +c & ,' jcenx= ',jcenx,' imax= ',imax,' jmax= ',jmax + + call get_ijplus1_check_wrap (imax,jmax,icenx,jcenx,ixp1,jxp1 + & ,ixm1,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NO CLOSED CONTOUR: The call to ' + print *,'!!! get_ijplus1_check_wrap indicates the' + print *,'!!! max/min contour extends past the edge of' + print *,'!!! our regional grid. ' + print *,' ' + print *,' ' + endif + + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c For each individual ring, we check all 8 points surrounding +c the center point. The points are numbered for each ring as +c shown in the diagram to the right of the "select case" +c statement just below. REMEMBER: The j in our grids +c increases from north to south, so that for a global grid, +c j=1 is at 90N and j=jmax is at 90S. + + individual_ring_loop: do ir = 1,9 + + select case (ir) + case (1); irx=ixm1; jrx=jcenx;! 2 3 4 + case (2); irx=ixm1; jrx=jxm1; ! + case (3); irx=icenx;jrx=jxm1; ! + case (4); irx=ixp1; jrx=jxm1; ! 1 (icenx,jcenx) 5 + case (5); irx=ixp1; jrx=jcenx;! + case (6); irx=ixp1; jrx=jxp1; ! + case (7); irx=icenx;jrx=jxp1; ! 8 7 6 + case (8); irx=ixm1; jrx=jxp1; ! + case (9); irx=icenx; jrx=jcenx; ! = center pt of ring + end select + +c Make sure the point we are looking at has valid data. +c This is an issue only on regional grids, where we have a +c buffer of bitmapped (null) data points surrounding the +c real grid. + +c print *,'ind ring loop: ir= ',ir,' irx= ',irx,' jrx= ',jrx + + if (.not. valid_pt(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a non-' + print *,'!!! valid point, meaning we are near the' + print *,'!!! bounds of the grid, or at least the ' + print *,'!!! bounds of the valid data for this ' + print *,'!!! grid. We will skip the' + print *,'!!! search for this center.' + print *,'!!! ' + print *,'!!! (i,j) of non-valid pt = (' + & ,irx,',',jrx,')' + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c Check to make sure that the point we are looking at is +c not considered under the influence of another nearby low. + + if (masked_out(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a point' + print *,'!!! that has been masked out, meaning it' + print *,'!!! belongs under the influence of ' + print *,'!!! another nearby low, so we will skip' + print *,'!!! the search for this center....' + print *,'!!! ' + print *,'!!! Min central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Masked-out value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of masked value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we have already hit this point on a previous ring +c check, then just ignore this point and cycle past it. + + if (point_is_already_in_our_contour(irx,jrx)) then +ctm +c print *,' ' +c print *,'Pt. AAA, already-in-contour.....' +c print *,'irx= ',irx,' jrx= ',jrx + cycle individual_ring_loop + endif + +c For a MIN check, check to see if the data point is below +c the contour interval or is below the local minimum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c For a MAX check, check to see if the data point is above +c the contour interval or is above the local maximum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c +c For example, for mslp, this would be as we're moving +c outward away from lower pressures to higher pressures, +c and then all of a sudden we come upon a lower pressure. +c This probably means we're heading toward another low +c pressure area, so mark the point and return to the +c calling routine. + + found_a_point_below_contour = 'n' + found_a_point_above_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) < xcentval .or. fxy(irx,jrx) < contlo) + & then + found_a_point_below_contour = 'y' + endif + else + if (fxy(irx,jrx) > xcentval .or. fxy(irx,jrx) > conthi) + & then + found_a_point_above_contour = 'y' + endif + endif + + if (found_a_point_below_contour == 'y' .or. + & found_a_point_above_contour == 'y') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a data' + print *,'!!! value that is less (greater) than the' + print *,'!!! current contour interval bound for a' + print *,'!!! min (max) and/or is less (greater) ' + print *,'!!! than the minimum (maximum) central ' + print *,'!!! value that we are centering the ' + print *,'!!! search on.' + print *,'!!! ' + print *,'!!! Central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Flagged value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of flagged value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we've made it this far, then we at least know that the +c gradient is still heading in the right direction. Do the +c check now to see if the value at this point is within our +c specific contour interval (there is the possibility that +c the value is beyond our interval, which will be checked +c for just below, and if that's the case, then that point +c will be processed in a subsequent iteration of this loop +c that encompasses that correct contour interval). + + found_a_point_in_our_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) >= contlo .and. fxy(irx,jrx) < conthi) + & then + found_a_point_in_our_contour = 'y' + endif + else + if (fxy(irx,jrx) > contlo .and. fxy(irx,jrx) <= conthi) + & then + found_a_point_in_our_contour = 'y' + endif + endif + + if (found_a_point_in_our_contour == 'y') then + ! We've found a data point in our interval, something + ! that is inside the closed contour, and it hasn't been + ! marked as being found in a previous iteration of this + ! loop, so mark it now and store the (i,j) location so + ! that we can scan a ring around this point in a + ! successive iteration of this loop for more potential + ! points within this interval... + + point_is_already_in_our_contour(irx,jrx) = .true. + + next_ring_ct = next_ring_ct + 1 + search_next_i(next_ring_ct) = irx + search_next_j(next_ring_ct) = jrx + +c print *,'at B, next_ring_ct= ',next_ring_ct +c & ,' search_next_i()= ',search_next_i(next_ring_ct) +c & ,' search_next_j()= ',search_next_j(next_ring_ct) + + num_pts_in_one_contour = num_pts_in_one_contour + 1 + temp_mask_i_loc(num_pts_in_one_contour) = irx + temp_mask_j_loc(num_pts_in_one_contour) = jrx + + if (get_last_isobar_flag == 'y') then + call calcdist (glon(ix),glat(jx) + & ,glon(irx),glat(jrx),dist,degrees) + rlast_distsum = rlast_distsum + dist + rlast_distct = rlast_distct + 1 + endif + +ctm +c print *,' ' +c print *,' PT IN! irx= ',irx,' jrx= ',jrx,' xval= ' +c & ,fxy(irx,jrx) +c print *,'next_ring_ct= ',next_ring_ct +c print *,'num_pts_in_one_contour= ' +c & ,num_pts_in_one_contour + endif + +c If we've made it this far AND the +c found_a_point_in_our_contour flag indicates that this +c point is not in our contour interval, then by default that +c means that this point is for a contour interval beyond +c what we're currently looking at. E.g., if we're looking +c at the contours around a 972 mb low and we're moving +c outward and currently checking the 984-988 mb contour +c interval, it means that we found, say, a gridpoint with +c 991 mb. So we want to mark that point for a future +c iteration of this loop that would be checking the +c 988-992 mb contour interval. + + if (found_a_point_in_our_contour /= 'y' .and. + & .not. point_is_already_in_next_contour(irx,jrx)) then + ! We've found a data point that is beyond our interval, + ! so this is not a concern for finding the bounds of + ! our current contour interval, but we want to mark + ! these points and remember them for the next iteration + ! of successive_scan_loop. (For example, suppose we + ! are currently searching for points in the 984-988 mb + ! range, and we find a point that is 990 -- mark it + ! here to be remembered when we scan for 988-992 mb). + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + if (fxy(irx,jrx) >= contlo_next .and. + & fxy(irx,jrx) < conthi_next) then + ! "NEXT_CONTOUR" Comment: + ! We've found a point that is in the very next + ! contour interval.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. + else if (fxy(irx,jrx) >= conthi_next) then + ! "BEYOND_CONTOUR" Comment: + ! This point is at least 1 contour interval beyond + ! the next contour interval. Dump the info into + ! these i and j arrays. This info will be used if + ! in the next iteration of single_contour_scan_loop, + ! next_contour_ct = 0. That would mean that we + ! have, e.g., an intensely deep low with a sharp + ! mslp gradient that essentially "skips" over a + ! contour interval. E.g., if using a 4 mb interval, + ! we go from 947 to 953 AND there are NO + ! intervening gridpoints in the 948-952 interval. + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) + endif + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + if (fxy(irx,jrx) > contlo_next .and. + & fxy(irx,jrx) <= conthi_next) then + ! See "NEXT_CONTOUR" comment just above.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. +c print *,'NEXT ncc= ',next_contour_ct +c & ,'next_contour_i()= ' +c & ,next_contour_i(next_contour_ct) +c & ,'next_contour_j()= ' +c & ,next_contour_j(next_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + else if (fxy(irx,jrx) <= contlo_next) then + ! See "BEYOND_CONTOUR" comment just above.... + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'BEYOND bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + endif + endif + endif + + enddo individual_ring_loop + + enddo multiple_ring_loop + + if (next_ring_ct > 0) then + iter = iter + 1 + else + icccret = 0 + still_scanning = .false. + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + num_found_contours = num_found_contours + 1 + closed_contour = 'y' + if (num_found_contours == 1) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Closed contour found ' + endif + + endif + endif + + enddo single_contour_scan_loop + + do insingle = 1,num_pts_in_one_contour + num_pts_in_all_contours = num_pts_in_all_contours + 1 + inall = num_pts_in_all_contours + hold_mask_i_loc(inall) = temp_mask_i_loc(insingle) + hold_mask_j_loc(inall) = temp_mask_j_loc(insingle) + enddo + + if (get_last_isobar_flag == 'y') then + if (cmaxmin == 'min') then + plastbar = conthi + else + plastbar = contlo + endif + if (rlast_distct > 0) then + rlastbar = rlast_distsum / float(rlast_distct) + rlastbar = rlastbar * 0.539638 ! convert km to nm + else + rlastbar = -999.0 + endif + endif + + enddo successive_contours_loop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'END SUM: num of iterations = ',isc_count + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_land_mask (imax,jmax,ix,jx,fract_land,valid_pt + & ,dx,dy,point_is_over_water,iclmret) +c +c ABSTRACT: This subroutine looks at the values for the land-sea +c mask surrounding an input (i,j) position to determine if less +c than 50% of the area surrounding the input (i,j) position within +c 75 km radius is land. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c +c OUTPUT: +c fract_land Fraction of points/area that is covered by land +c point_is_over_water y/n: A value of 'y' is returned if <50% +c of the points/area is covered by land +c iclmret Return code from this routine +c + USE grid_bounds; USE tracked_parms + USE trkrparms; USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + logical(1) valid_pt(imax,jmax) + character point_is_over_water*1 + integer, parameter :: numazim=8 + integer iazim,ibiret1,imax,jmax,ix,jx,iclmret,imct + real bear,targlat,targlon,xplon,yplat,rdist,xintrp_mask + real fract_land,dx,dy,xmask_sum +c + iclmret = 0 + +c First, calculate the longitude and latitude of the input ix and +c jx points. If the xplon value ends up being >360.0 (this can +c happen for basin-scale HWRF), don't worry about it. Just leave +c it be, as the trigonometry will work out the same for lons >360. + + xplon = glonmin + (ix-1)*dx + yplat = glatmax - (jx-1)*dy + + rdist = 75.0 ! (We will always look only 75 km radius out for + ! this particular land-sea mask application) + + imct = 0 + +c Now go around the storm via azimloop and get interpolated +c values of the land-sea mask at each azimuth at a radial +c distance of 75 km from the center point.... + + xmask_sum = 0.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 45.0 + + call distbear (yplat,xplon,rdist,bear,targlat,targlon) + + ! These calls to bilin_int_uneven pass a variable, level, + ! that is used for applications of interpolating wind + ! data. Here, we are instead interpolating the land-sea + ! mask data, so we don't care about the level, so just + ! pass a dummy value of 850, which never gets used. + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,850,'m',xintrp_mask,ibiret1) + + if (ibiret1 == 0) then + xmask_sum = xmask_sum + xintrp_mask + imct = imct + 1 + else + iclmret = 95 + return + endif + + enddo azimloop + +c Now get the mask value directly at the point that was input to +c this routine.... + + xmask_sum = xmask_sum + lsmask(ix,jx) + imct = imct + 1 + +c Now get the mean land fraction.... + + if (imct > 0) then + + fract_land = xmask_sum / float(imct) + if (fract_land < 0.50) then + point_is_over_water = 'y' + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Land check yes: Point is over water. ' + print *,' Land check value: fract_land= ',fract_land + endif + else + point_is_over_water = 'n' + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Land check NO: Point is over land. ' + print *,' Land check value: fract_land= ',fract_land + endif + endif + + else + + iclmret = 95 + return + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine get_ijplus1_check_wrap (imax,jmax,i,j,iplus1,jplus1 + & ,iminus1,jminus1,trkrinfo,igicwret) +c +c ABSTRACT: This subroutine takes an (i,j) position input and +c returns the four neighboring (i,j) points to the east, south, +c west and north. The routine checks for wrap around the GM, so +c that if, for example, you are on a global 360x181 grid and you +c are at point i=360, then i+1 = 361, so you need something to +c adjust that back to i = 1. Likewise, if you are at i=1 and +c looking for point i-1, it will adjust it to be point 360 +c instead of the meaningless point 0 (i=0). + + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer i,j,imax,jmax,iplus1,jplus1,iminus1,jminus1,igicwret + + igicwret = 0 + + jplus1 = j + 1 + jminus1 = j - 1 + iplus1 = i + 1 + iminus1 = i - 1 + + if (iplus1 > imax) then + if (trkrinfo%gridtype == 'global') then + iplus1 = iplus1 - imax ! If wrapping east of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is too close to the eastern bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested eastern ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',iplus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (iminus1 < 1) then + if (trkrinfo%gridtype == 'global') then + iminus1 = imax + iminus1 ! If wrapping west of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested western search boundary' + print *,'!!! is too close to the western bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested western ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested western i = ',iminus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (jplus1 > jmax .or. jminus1 < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The ' + print *,'!!! user-requested northern or southern search' + print *,'!!! boundary is too close to the bounds of the' + print *,'!!! grid. Cut back your requested northern or' + print *,'!!! southern boundary by a degree or 2 in the' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested northern j = ',jminus1 + print *,'!!! User-requested southern j = ',jplus1 + print *,'!!! jmax of grid = ',jmax + print *,' ' + endif + + igicwret = 91 + return + endif + + return + end + +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + SUBROUTINE qsort(x,ind,n) +c +c Code converted using TO_F90 by Alan Miller +c Date: 2002-12-18 Time: 11:55:47 + + IMPLICIT NONE + INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(12, 60) + + REAL (dp), INTENT(IN) :: x(n) + INTEGER, INTENT(OUT) :: ind(n) + INTEGER, INTENT(IN) :: n + +c *************************************************************************** +c +c ROBERT RENKA +c OAK RIDGE NATL. LAB. +c +c THIS SUBROUTINE USES AN ORDER N*LOG(N) QUICK SORT TO SORT A REAL (dp) +c ARRAY X INTO INCREASING ORDER. THE ALGORITHM IS AS FOLLOWS. IND IS +c INITIALIZED TO THE ORDERED SEQUENCE OF INDICES 1,...,N, AND ALL INTERCHANGES +c ARE APPLIED TO IND. X IS DIVIDED INTO TWO PORTIONS BY PICKING A CENTRAL +c ELEMENT T. THE FIRST AND LAST ELEMENTS ARE COMPARED WITH T, AND +c INTERCHANGES ARE APPLIED AS NECESSARY SO THAT THE THREE VALUES ARE IN +c ASCENDING ORDER. INTERCHANGES ARE THEN APPLIED SO THAT ALL ELEMENTS +c GREATER THAN T ARE IN THE UPPER PORTION OF THE ARRAY AND ALL ELEMENTS +c LESS THAN T ARE IN THE LOWER PORTION. THE UPPER AND LOWER INDICES OF ONE +c OF THE PORTIONS ARE SAVED IN LOCAL ARRAYS, AND THE PROCESS IS REPEATED +c ITERATIVELY ON THE OTHER PORTION. WHEN A PORTION IS COMPLETELY SORTED, +c THE PROCESS BEGINS AGAIN BY RETRIEVING THE INDICES BOUNDING ANOTHER +c UNSORTED PORTION. +c +c INPUT PARAMETERS - N - LENGTH OF THE ARRAY X. +c +c X - VECTOR OF LENGTH N TO BE SORTED. +c +c IND - VECTOR OF LENGTH >= N. +c +c N AND X ARE NOT ALTERED BY THIS ROUTINE. +c +c OUTPUT PARAMETER - IND - SEQUENCE OF INDICES 1,...,N PERMUTED IN THE SAME +c FASHION AS X WOULD BE. THUS, THE ORDERING ON +c X IS DEFINED BY Y(I) = X(IND(I)). +c +c ********************************************************************* + + ! NOTE -- IU AND IL MUST BE DIMENSIONED >= LOG(N) WHERE LOG HAS BASE 2. + + !********************************************************************* + + INTEGER :: iu(21), il(21) + INTEGER :: m, i, j, k, l, ij, it, itt, indx + REAL :: r + REAL (dp) :: t + + ! LOCAL PARAMETERS - + + ! IU,IL = TEMPORARY STORAGE FOR THE UPPER AND LOWER + ! INDICES OF PORTIONS OF THE ARRAY X + ! M = INDEX FOR IU AND IL + ! I,J = LOWER AND UPPER INDICES OF A PORTION OF X + ! K,L = INDICES IN THE RANGE I,...,J + ! IJ = RANDOMLY CHOSEN INDEX BETWEEN I AND J + ! IT,ITT = TEMPORARY STORAGE FOR INTERCHANGES IN IND + ! INDX = TEMPORARY INDEX FOR X + ! R = PSEUDO RANDOM NUMBER FOR GENERATING IJ + ! T = CENTRAL ELEMENT OF X + + IF (n <= 0) RETURN + + ! INITIALIZE IND, M, I, J, AND R + + DO i = 1, n + ind(i) = i + END DO + m = 1 + i = 1 + j = n + r = .375 + + ! TOP OF LOOP + + 20 IF (i >= j) GO TO 70 + IF (r <= .5898437) THEN + r = r + .0390625 + ELSE + r = r - .21875 + END IF + + ! INITIALIZE K + + 30 k = i + + ! SELECT A CENTRAL ELEMENT OF X AND SAVE IT IN T + + ij = i + r*(j-i) + it = ind(ij) + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) > t) THEN + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + END IF + + ! INITIALIZE L + + l = j + + ! IF THE LAST ELEMENT OF THE ARRAY IS LESS THAN T, + ! INTERCHANGE IT WITH T + indx = ind(j) + IF (x(indx) >= t) GO TO 50 + ind(ij) = indx + ind(j) = it + it = indx + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) <= t) GO TO 50 + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + GO TO 50 + + ! INTERCHANGE ELEMENTS K AND L + + 40 itt = ind(l) + ind(l) = ind(k) + ind(k) = itt + + ! FIND AN ELEMENT IN THE UPPER PART OF THE ARRAY WHICH IS + ! NOT LARGER THAN T + + 50 l = l - 1 + indx = ind(l) + IF (x(indx) > t) GO TO 50 + + ! FIND AN ELEMENT IN THE LOWER PART OF THE ARRAY WHCIH IS NOT SMALLER THAN T + + 60 k = k + 1 + indx = ind(k) + IF (x(indx) < t) GO TO 60 + + ! IF K <= L, INTERCHANGE ELEMENTS K AND L + + IF (k <= l) GO TO 40 + + ! SAVE THE UPPER AND LOWER SUBSCRIPTS OF THE PORTION OF THE + ! ARRAY YET TO BE SORTED + + IF (l-i > j-k) THEN + il(m) = i + iu(m) = l + i = k + m = m + 1 + GO TO 80 + END IF + + il(m) = k + iu(m) = j + j = l + m = m + 1 + GO TO 80 + + + ! BEGIN AGAIN ON ANOTHER UNSORTED PORTION OF THE ARRAY + + 70 m = m - 1 + IF (m == 0) RETURN + i = il(m) + j = iu(m) + + 80 IF (j-i >= 11) GO TO 30 + IF (i == 1) GO TO 20 + i = i - 1 + + ! SORT ELEMENTS I+1,...,J. NOTE THAT 1 <= I < J AND J-I < 11. + + 90 i = i + 1 + IF (i == j) GO TO 70 + indx = ind(i+1) + t = x(indx) + it = indx + indx = ind(i) + IF (x(indx) <= t) GO TO 90 + k = i + + 100 ind(k+1) = ind(k) + k = k - 1 + indx = ind(k) + IF (t < x(indx)) GO TO 100 + + ind(k+1) = it + GO TO 90 + END SUBROUTINE qsort + +c +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT11/FORT31 + subroutine open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + +C ABSTRACT: This subroutine must be called before any attempt is +C made to read from the input GRIB files. The GRIB and index files +C are opened with a call to baopenr. This call to baopenr was not +C needed in the cray version of this program (the files could be +C opened with a simple Cray assign statement), but the GRIB-reading +C utilities on the SP do require calls to this subroutine (it has +C something to do with the GRIB I/O being done in C on the SP, and +C the C I/O package needs an explicit open statement). +C +C INPUT: +c inp Contains user-input info on the date & data +C lugb The Fortran unit number for the GRIB data file +C lugi The Fortran unit number for the GRIB index file +c ifh integer index for lead time level +c gfilename If using individual files for each tau, gfilename will +c contain the grib data filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +c ifilename If using individual files for each tau, gfilename will +c contain the grib index filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +C lout The Fortran unit number for the output grib file +C +C OUTPUT: +C iret The return code from this subroutine + + USE inparms + USE verbose_output + + implicit none +c + type (datecard) inp + + logical(1) output_file_open + logical(1) file_open + character(*) gfilename,ifilename +c character(120) gopen_g_file,gopen_i_file +cPeng 05/28/2018 Bug Fixed for FV3-GFS Job Crashed. + character(255) gopen_g_file,gopen_i_file + character(2) lugb_c,lugi_c + character(6) enameb,enamei + integer igoret,iioret,iooret,lugb,lugi,lout,iret,nlen1,nlen2 + + iret=0 + + if (inp%file_seq == 'onebig') then + write(lugb_c,'(i2)')lugb + write(lugi_c,'(i2)')lugi + enameb='FORT'//adjustl(lugb_c) + enamei='FORT'//adjustl(lugi_c) + call get_environment_variable(enameb,gopen_g_file,status=igoret) + call get_environment_variable(enamei,gopen_i_file,status=iioret) + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + gopen_g_file(1:5) = "fort." + gopen_i_file(1:5) = "fort." + write(gopen_g_file(6:7),'(I2)') lugb + write(gopen_i_file(6:7),'(I2)') lugi + endif + else + nlen1 = len_trim(gfilename) + gopen_g_file = trim(gfilename(1:nlen1)) + nlen2 = len_trim(ifilename) + gopen_i_file = trim(ifilename(1:nlen2)) + endif + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + +c print *,'gopen_g_file= ',gopen_g_file,'....' +c print *,'gopen_i_file= ',gopen_i_file,'....' + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end + +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT12 + subroutine read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + ii=1 + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + maxstorm = numtcv + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that you have the Fortran' + print *,'!!! unit assigned right in your script.' + endif + + iret = 99 + return + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT14 + subroutine read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + ii = numtcv + 1 + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1,1x + & ,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_main_gfs.f_10072019 b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_main_gfs.f_10072019 new file mode 100644 index 0000000000..22c81397ed --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_main_gfs.f_10072019 @@ -0,0 +1,25611 @@ + program trakmain +c +c$$$ MAIN PROGRAM DOCUMENTATION BLOCK +c +c Main Program: GETTRK Track model vortices +C PRGMMR: MARCHOK ORG: NP22 DATE: 2002-05-20 +c +c ABSTRACT: This program tracks the average of the max or min +c of several parameters in the vicinity of an input +c first guess (lat,lon) position of a vortex in order to give +c forecast position estimates for that vortex for a given numerical +c model. For the levels 700 & 850 mb, the tracked parameters are: +c Relative vorticity (max), wind magnitude (min), and geopotential +c height (min). Also tracked is the min in the MSLP. So many +c parameters are tracked in order to provide more accurate position +c estimates for weaker storms, which often have poorly defined +c structures/centers. Currently, the system is set up to be able +c to process GRIB input data files from the GFS, MRF, UKMET, GDAS, +c ECMWF, NGM, NAM and FNMOC/NAVGEM models. Two 1-line files +c are output from this program, both containing the forecast fix +c positions that the tracker has obtained. One of these output +c files contains the positions at every 12 hours from forecast +c hour 0 to the end of the forecast. The other file is in ATCF +c format, which is the particular format needed by the Tropical +c Prediction Center, and provides the positions at forecast hours +c 12, 24, 36, 48 and 72, plus the maximum wind near the storm center +c at each of those forecast hours. +c +c Program history log: +c 98-03-16 Marchok - Original operational version. +c 98-07-15 Marchok - Added code to calculate radii of gale-, storm-, +c and hurricane-force winds in each quadrant. +c 99-04-01 Marchok - Added code to be able to read in 4-digit years +c off of the TC Vitals records. +c Added code, including subroutine is_it_a_storm, +c to make a better determination of whether or +c not the center that was found at each time is +c the center of a storm, and not just a passing +c vort max, etc. +c 99-06-15 Marchok - Fixed a bug in calcdist that was triggered by a +c rounding error sending a number just above 1 +c into ACOS to get the distance between 2 +c identical points (which, obviously, is 0). +c 00-06-20 Marchok - Added GDAS option for vortex relocation work. +c Changed nhalf from 3 to 5. Relaxed the +c requirements for pthresh and vthresh. +c 00-11-30 Marchok - Added ability to handle GFDL and NCEP Ensemble +c model data. Extended time range to be able to +c handle 5-day capability. Forecast hours are +c now input via a namelist (easiest way to account +c for NAM, GFS and GFDL having different forecast +c lengths at 00/12z and 06/18z). Model ID's are +c now input via a namelist (makes it easier, for +c example, to run for many different ensemble +c members). Added new output, the atcfunix +c format, needed for 5-day forecasts. +c 01-08-24 Marchok Fixed a bug in rvcal and getgridinfo. When a +c grid that was south-->north is flipped in +c conv1d2d_real to be north-->south, the scanning +c mode flag remains 64 and what we would consider +c the max and min latitudes are reversed, so I +c added code to correct this in both routines. +c 02-05-20 Marchok Weakened the mslp gradient threshold and v850 +c threshold in is_it_a_storm to cut down on the +c number of dropped storms. +c 03-03-18 Marchok Fixed a bug in get_ij_bounds that was allowing +c a cos(90) and cos(-90), which then led to a +c divide by zero. +c 05-08-01 Marchok Updated to allow tracking of ECMWF hi-res, ECMWF +c ensemble, CMC hi-res, CMC ensemble, NCEP +c ensemble. +c 06-11-07 Marchok Updated to locate, and report to the atcfunix +c file, the value of the gridpoint minimum value +c of mslp. Previously, the barnes-averaged +c value had been used. +c 08-01-10 Marchok Changed the storm ID for genesis tracking so +c that the ID includes info +c on storm detection location & time. Added +c algorithms for Hart's cyclone phase space. +c Added new output fields to the atcfunix +c records, actually creating a modified atcfunix +c record, to include things such as the mean & +c max values of zeta850 & zeta700 centered on +c the storm, the speed & direction of storm +c translation, and the Hart CPS parameters. +c 10-01-07 Marchok - input grib lead time can be hrs or minutes +c - added code for warm core check +c - added code to detect genesis +c - added code to report on sfc wind structure +c - added buffer ("grid_buffer") to avoid fixing +c center to boundaries on regional grids +c - modified rvcal to report missing zeta values +c as background coriolis instead of -999, since +c the -999 was messing up center-fixing +c - added 10-m wind and sfc zeta as center-fixing +c parms. +c +c 10-05-25 Slocum Add verbose feature to code +c 0 = Not terminal output, 1 = error messages only +c 2 = all output +c +c 10-05-26 Marchok - added flags and code to check the temporal +c consistency of the mslp closed contour and +c Vt850 checks for tcgen and midlat cases. +c +c 13-04-01 Marchok Added code to upgrade the wind radii diagnosid. +c Hurricane Sandy exposed an issue with the +c tracker for large storms. The code was modified +c to use an iterative technique that can +c diagnose radii for large storms but still +c accurately diagnost radii for small storms. See +c subroutine getradii for more details. +c +c 15-11-01 Marchok Replaced the routine which tracks the wind +c minimum at the center of a storm, as that +c routine proved troublesome with very hi-res +c grids (0.02-deg) from HWRF for very small +c storms. This has been replaced with a routine +c that looks for "wind circulation difference", +c whereby the center for this parm is located at +c the spot where the tangential wind circulation +c minus the wind magnitude at the candidate +c center position is maximized. ALSO: Added in +c tracking of thickness as an additional +c tracked parm. ALSO: Added a separate verbose +c flag for only the GRIB2 read diagnostics, which +c can be voluminous. +c +c 16-09-01 Marchok Added in the ability to read in NetCDF files. +c As with GRIB data, the NetCDF data must be on +c a lat/lon grid. +c +c 17-08-31 Marchok Added a logical bitmap capability for NetCDF +c files to prevent the accessing of missing data. +c Also modified the code to permit more accurate +c reporting of the grid point value of the +c minimum SLP for reporting to the atcfunix file. +c Finally, fixed a bug (reported by JTWC) whereby +c radii were being reported for thresholds that +c were in exceedance of the tracker-diagnosed +c Vmax (e.g., 34-kt radii for a storm with +c Vmax = 25 kts). +c +c Input files: +c unit 11 Unblocked GRIB1 file containing model data +c unit 12 Text file containing TC Vitals card for current time +c unit 31 Unblocked GRIB index file +c +c Output files: +c unit 61 Output file with forecast positions every 12h from +c vt=00h to the end of the forecast +c unit 62 Output file in ATCF format, with forecast positions +c at vt = 12, 24, 36, 48 and 72h, plus wind speeds. +c unit 63 Output file with forecast wind radii for 34, 50 and +c 64 knot thresholds in each quadrant of each storm. +c +c Subprograms called: +c read_nlists Read input namelists for input date & storm number info +c read_tcv_card Read TC vitals file to get initial storm position +c getgridinfo Read GRIB file to get basic grid information +c tracker Begin main part of tracking algorithm +c +c Attributes: +c Language: Standard Fortran_90 +c +c$$$ +c +c------- +c +c LOCAL: +c +c ifhours: Integer array holding numerical forecast times for +c the input model (99 = no more times available). +c These values are read in via a namelist. +c Model numbers used: (1) GFS, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) NAM, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble (13) SREF +c Ensemble, (14) NCEP Ensemble (from ensstat mean +c fields), (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) Ensemble RELOCATION (21) UKMET hi-res (NHC) +c (23) FNMOC Ensemble +c stormswitch: This switch tells how to handle each storm in +c the TCV file: +c 1 = process this storm for this forecast hour. +c 2 = Storm was requested to be tracked, but either +c the storm went off the grid (regional models), +c the storm dissipated, or the program was +c unable to track it. +c 3 = Storm was NOT requested to be tracked at all. +c storm: An array of type tcvcard. Each member of storm +c contains a separate TC Vitals card. +c maxstorm: Maximum number of storms the system is set up to +c handle at any 1 time. +c slonfg,slatfg: Holds first guess positions for storms. The +c very first, first guess position is read from the +c TC vitals card. (maxstorm,maxtime) +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms) +c + USE def_vitals; USE inparms; USE set_max_parms; USE level_parms + USE trig_vals; USE atcf; USE trkrparms; USE verbose_output + USE netcdf_parms +c + implicit none +c + logical(1) file_open + integer date_time(8) + character (len=10) big_ben(3) + character :: ncfile*180,ncfile_has_hour0*1 + integer itret,iggret,iicret,igcret,iret,ifhmax,maxstorm,numtcv + integer iocret,enable_timing,ncfile_id,ncfile_tmax,irnhret + integer, parameter :: lugb=11,lugi=31,lucard=12,lgvcard=14,lout=51 +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + +c -------------------------------------------------------- + + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: beginning ... ',i2.2,':',i2.2,':',i2.2) + + call w3tagb('GETTRK ',1999,0104,0058,'NP22 ') + + pi = 4. * atan(1.) ! Both pi and dtr were declared in module + dtr = pi/180.0 ! trig_vals, but were not yet defined. + ncfile_has_hour0 = 'n' ! Default value; set in read_netcdf_hours +c + call read_nlists (inp,trkrinfo,netcdfinfo) + enable_timing=trkrinfo%enable_timing + + call read_fhours (ifhmax) + + call read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_tcv_card, num vitals = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_tcv_card, rc= ',iret + endif + goto 890 + endif + + call read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_gen_vitals, total number of vitals (both' + & ,' TC and non-TC) now = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_gen_vitals, rc= ' + & ,iret + endif + goto 890 + endif + + if (inp%file_seq == 'onebig') then + if (trkrinfo%inp_data_type == 'netcdf') then + ncfile = netcdfinfo%netcdf_filename + print *,' ' + print *,'before open_ncfile call, ncfile= ',ncfile + call open_ncfile (ncfile,ncfile_id) + print *,'after open_ncfile call, ncfile_id= ',ncfile_id + call read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) + if (irnhret /= 0) then + print *,'(/,a32,a5,i4,/)','!!! ERROR: in read_netcdf_hours,' + & ,' rc= ',irnhret + goto 890 + endif + else + call open_grib_files (inp,lugb,lugi,'dummy','dummy',lout,iret) + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: in open_grib_files, rc= ' + & ,iret + goto 890 + endif + endif + endif + + call tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +890 continue + + igcret=0 + iicret=0 + iocret=0 + + inquire (unit=lugb, opened=file_open) + if (file_open) call baclose(lugb,igcret) + inquire (unit=lugi, opened=file_open) + if (file_open) call baclose(lugi,iicret) + inquire (unit=lout, opened=file_open) + if (file_open) call baclose(lout,iocret) + if ( verb .ge. 3 ) then + print *,'baclose: igcret= ',igcret,' iicret= ',iicret + print *,'baclose: iocret= ',iocret + endif + call w3tage('GETTRK ') +c + stop + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +c ABSTRACT: This subroutine is the core of the program. It contains +c the main loop for looping through all the forecast hours and all +c the storms. Basically, the way it works is that it has an outer +c loop that loops on the forecast hour. At the beginning of this +c loop, the data are read in for all parameters and levels needed +c for tracking. The full regional or global grid is read in. +c If vorticity was not read in (some of the centers do not send us +c vorticity), then vorticity calculations are done on the whole +c grid at both 850 and 700 mb. Then the program goes into the inner +c loop, which loops on storm number (program originally set up to +c handle a max of 15 storms). For each storm, subroutine +c find_maxmin is called for the following parameters: Rel Vort and +c geopotential hgt at 700 & 850 mb, and MSLP. Within find_maxmin, +c a barnes analysis is performed over the guess position of the +c storm to find the max or min value, and then iteratively, the +c grid size is cut in half several times and the barnes analysis +c rerun to refine the positioning of the max or min location. After +c the center positions for these parameters have been obtained, +c subroutine get_uv_center is called to get a center fix for the +c minimum in the wind field, specifically, a minimum in the +c magnitude of the wind speed (vmag). The calculation of the vmag +c minimum is done differently than the calculation for the other +c parameters; for vmag, the grid near the storm center guess +c position is interpolated down to a very fine grid, and then +c find_maxmin is called and a barnes analysis is done on that +c smaller grid. For vmag, there are no further calls made to barnes +c with a smaller grid, since the grid has already been interpolated +c down to a smaller grid. Once all of the parameter center fixes +c have been made, subroutine fixcenter is called to average these +c positions together to get a best guess fix position. Then a check +c is done with a call to subroutine is_it_a_storm to make sure that +c the center that we have found does indeed resemble a tropical +c cyclone. Finally, subroutine get_next_ges is called to make a +c guess position for the next forecast time for this storm. +c +c INPUT: +c inp contains input date and model number information +c maxstorm maximum # of storms to be handled +c numtcv number of storms read off of the tcvitals file +c ifhmax max number of analysis & forecast times to be handled +c trkrinfo derived type that holds/describes various tracker parms +c ncfile if the input data type is netcdf, then this ncfile +c variable contains the name of the netcdf file +c ncfile_id if the input data type is netcdf, then this ncfile_id +c variable contains an integer id assigned to the netcdf +c file from the open_ncfile subroutine +c ncfile_has_hour0 character flag (y|n) that, if the tracker is +c running on NetCDF data, tells if the NetCDF file +c actually contains hour0 data or not (some, like the +c 2016 version of FV3, do not). +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file itself in +c subroutine read_netcdf_fhours. +c +c OUTPUT: +c itret return code from this subroutine +c +c LOCAL PARAMETERS: +c storm contains the tcvitals for the storms +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c maxtime Max number of forecast times program can track +c maxtp Max number of tracked parameters program will track. +c Currently (7/2015), this maxtp is 11, and these 11 are +c listed just a few lines below. +c readflag L Indicates status of read for each of 16 parms: +c 1: 850 mb absolute vorticity +c 2: 700 mb absolute vorticity +c 3: 850 mb u-comp +c 4: 850 mb v-comp +c 5: 700 mb u-comp +c 6: 700 mb v-comp +c 7: 850 mb gp hgt +c 8: 700 mb gp hgt +c 9: MSLP +c 10: near-surface u-comp +c 11: near-surface v-comp +c 12: 500 mb u-comp +c 13: 500 mb v-comp +c 14: Mean temperature, centered at 400 mb +c 15: 500 mb gp hgt +c 16: 200 mb gp hgt +c 17: Land-Sea Mask (for use in tcgen applications, and +c even there, it's optional) +c +c calcparm L indicates which parms to track and which not to. +c Array positions are defined exactly as for clon +c and clat, listed next, except that, in general, when +c flag 3 is set to a value, flag 4 is set to the same +c value as 3, and when flag 5 is set to a value, flag +c 6 is set to the same value as 5. This is because +c 3 & 4 are for the 850 mb winds, and if either u or +c v is missing, we obviously can't calculate the +c magnitude of the wind. The same applies for 5 & 6, +c which are for the 700 mb winds. And also for reference, +c here is a list of all the variables & levels for the +c tracked parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms). +c For the third position (max_#_parms), here they are: +c 1: Relative vorticity at 850 mb +c 2: Relative vorticity at 700 mb +c 3: Wind circulation difference at 850 mb +c 4: NOT CURRENTLY USED +c 5: Wind circulation difference at 700 mb +c 6: NOT CURRENTLY USED +c 7: Geopotential height at 850 mb +c 8: Geopotential height at 700 mb +c 9: Mean Sea Level Pressure +c 10: Wind circulation difference at 10 m +c 11: Relative vorticity at 10 m +c 12: Lower-level thickness (500-850) +c 13: Upper-level thickness (200-500) +c 14: Deep-Layer thickness (200-850) +c +c xmaxwind Contains maximum near-surface wind near the storm +c center for each storm at each forecast hour. +c stderr Standard deviation of the position "errors" of the +c different parameters for each storm at each time. +c fixlat,fixlon: Contain the final coordinates for each storm at +c each forecast hour. These coordinates are a +c weighted average of all the individual parameter +c positions (hgt, zeta, mslp, vmag). +c cvort_maxmin: Contains the characters 'max' or 'min', and is +c used when calling the find_maxmin routine for the +c relative vorticity (Look for max in NH, min in SH). +c vradius Contains the distance from the storm fix position to +c each of the various near-surface wind threshhold +c distances in each quadrant. +c (3,4) ==> (# of threshholds, # of quadrants) +c See subroutine getradii for further details. +c wfract_cov Fractional coverage (areal coverage) of winds +c exceeding a certain threshold (34, 50, 64 kts) in +c each quadrant. +c (5,5,3) ==> (# of quadrants + 1, # of distance bins, +c # of thresholds). +c The "extra" array size for quadrants (5, instead of 4) +c is there to hold the total (i.e., "whole disc") +c statistics. +c See subroutine get_fract_wind_cov for further details +c +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c isastorm Character array used in the call to is_it_a_storm, +c tells whether the minimum requirement for an MSLP +c gradient was met (isastorm(1)), whether for the midlat +c and tcgen cases if a closed mslp contour was found +c (isastorm(2)), and if a circulation exists at 850 mb +c (isastorm(3)). Can have a value of 'Y' (requirement +c met), 'N' (requirement not met) or 'U' (requirement +c undetermined, due to the fact that no center location +c was found for this parameter). +c maxmini These 2 arrays contain the i and j indeces for the +c maxminj max/min centers that are found using the rough check +c in first_ges_ctr and subsequent routines. Only needed +c for a midlatitude or a genesis run, NOT needed for a +c TC tracker run. +c stormct Integer: keeps and increments a running tab of the +c number of storms that have been tracked at any time +c across all forecast hours. Used only for midlat or +c tcgen runs. +c gridprs This contains the actual value of the minimum pressure +c at a gridpoint. The barnes analysis will return an +c area-averaged value of pressure; this variable will +c contain the actual minimum value at a gridpoint near +c the lat/lon found by the barnes analysis. +c closed_mslp_ctr_flag This flag keeps track of the value of the +c closed contour flag returned from subroutine +c check_closed_contour. +c vt850_flag This flag keeps track of the value of the flag for +c the 850 mb Vt check. +c----- +c + USE def_vitals; USE inparms; USE tracked_parms; USE error_parms + USE set_max_parms; USE level_parms; USE grid_bounds; USE trkrparms + USE contours; USE atcf; USE radii; USE trig_vals; USE phase + USE gen_vitals; USE structure; USE verbose_output + USE waitfor_parms; USE module_waitfor; USE netcdf_parms + USE tracking_parm_prefs +c + implicit none +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (cint_stuff) contour_info +c + character, allocatable :: closed_mslp_ctr_flag(:,:)*1 + character, allocatable :: vt850_flag(:,:)*1 + character :: r34_check_okay*1,had_to_try_backup_850_vt_check*1 + character :: need_to_expand_r34(4)*1,ncfile_has_hour0*1 + character*(*), intent(in) :: ncfile + integer :: ncfile_id + integer, parameter :: nreadparms=17 + real, allocatable :: prstemp(:),iwork(:) + integer, parameter :: numdist=14,numquad=4,lout=51 + integer, allocatable :: prsindex(:) + integer imax,jmax,ifh,ist,irf,jj,istmp,ifhtemp,itret,ivpa + integer isiret1,isiret2,isiret3,idum,m,iix,jjx,imode,numtcv + integer iha,isa,iua,iva,iza,maxstorm,ivort,ifix,jfix,issret + integer imoa,imoca,iksa,isda,ileadtime,leadtime_check + integer ioaret,ioaxret,ifgcret,ifmret,igugret,isoiret,icccret + integer igrret,igmwret,iorret,ignret,iovret,icbret,igucret,ita + integer ifilret,ifret,iaret,isret,iotmret,iwa,iisa,sl_counter + integer iicret,igcret,pfcret,igwcret,imbowret,iatret + logical(1), allocatable :: valid_pt(:,:) + logical(1), allocatable :: masked_outc(:,:),masked_out(:,:) + logical(1) readflag(nreadparms),calcparm(maxtp,maxstorm) + logical(1) tracking_previously_known_storms + logical(1) need_to_flip_lats,need_to_flip_lons + logical(1) file_open,first_time_thru_getradii + character cvort_maxmin*3,isastorm(3)*1,ccflag*1,gotten_avg_value*1 + character cmaxmin*3,get_last_isobar_flag*1,wcore_flag*1 + character gfilename*120,ifilename*120,gridmove_status*7 + integer vradius(3,4),igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) + integer maxmini(maxstorm),maxminj(maxstorm),pdf_ct_bin(16) + integer ifcsthour,stormct,prevstormct,kf,istmspd,istmdir,iggret + integer igiret,iuret,jdum,icount,ilonfix,jlatfix,igpret,ifhmax + integer ibeg,jbeg,iend,jend,ix1,ix2,n,ilev,npts,icpsa,igzvret + integer igfwret,ioiret,igisret,iofwret,iowsret,igwsret,igscret + integer pdf_ct_tot,lugb,lugi,iret,icmcf,iccfh,ivt8f + integer waitfor_gfile_status,waitfor_ifile_status,ncfile_tmax + integer wait_max_ifile_wait,ivr,r34_good_ct,itha,ilma,inctcv + integer date_time(8) + character (len=10) big_ben(3) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridprs(maxstorm,maxtime) + real wfract_cov(5,5,3) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real ike(max_ike_cats) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmaxwind(maxstorm,maxtime),xmeanzeta + real stderr(maxstorm,maxtime),xval(maxtp),cps_vals(3) + real gridpoint_maxmin,dist,distnm,xknots,xmaxspeed + real uvgeslon,uvgeslat,xavg,stdv,search_cutoff,re,ri,dx,dy + real xinp_fixlat,xinp_fixlon,degrees,plastbar,rlastbar + real xinterval_fhr,cc_time_sum_tot,cc_time_sum_yes + real rmax,sdp,wdp,paramb,vtl_slope,vtu_slope + real xsfclon,xsfclat,cc_time_pct,radmax,r34_dist_thresh + real prev_latmax,prev_latmin,prev_lonmax,prev_lonmin + real vradius_km,hold_old_contint,tcv_max_wind_ms + real tcv_mslp_pa,r34_from_tcv,roci_from_tcv + real proci_from_tcv,prs_contint_thresh + integer enable_timing,igrct + character(pfc_cmd_len) :: pfc_final +c + prev_latmax = -999.0 + prev_latmin = -999.0 + prev_lonmax = -999.0 + prev_lonmin = -999.0 + enable_timing=trkrinfo%enable_timing + icmcf = 0 + ivt8f = 0 + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + allocate (closed_mslp_ctr_flag(maxstorm,ifhmax),stat=icmcf) + allocate (vt850_flag(maxstorm,ifhmax),stat=ivt8f) + ! Initialize flags to 'u', not 'n'. That way, + ! when we are evaluating its value back over recent past hours, + ! we can distinguish a "no" value from an initialized value of + ! 'u' for which a storm hadn't yet been detected. + closed_mslp_ctr_flag = 'u' + vt850_flag = 'u' + endif + + allocate (prsindex(maxstorm),stat=iisa) + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iisa /= 0 .or. iva /= 0 .or. iwa /= 0 .or. icmcf /= 0 .or. + & ivt8f /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating prsindex,' + print *,'!!! prstemp or iwork array for storms: iisa = ',iisa + print *,'!!! iva= ',iva,' iwa= ',iwa,' icmcf= ',icmcf + print *,'!!! ivt8f= ',ivt8f + endif + itret = 94 + return + endif + + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + clon = 0.0 + clat = 0.0 + stderr = stermn ! initialize stderr to 0.1 (error_parms) + itret = 0 + xmaxwind = 0.0 + stormct = 0 + + ! It is critical to initialize the gridprs array to something + ! greater than normal atmospheric pressures (I've chosen 9999.99 + ! mb). This is so that in the sort on pressure before stormloop, + ! the top of the sorting index array will be filled with pressure + ! values from active storms, while those inactive 9999 storms + ! will fill the bottom of the sorting index array (prsindex). + + gridprs = 999999.0 + fixlon = -999.0 + fixlat = -999.0 + + if (inp%file_seq == 'multi') then + ! Each tau will have a separate file, starting with unit + ! number 200 (GRIB data) and 5200 (GRIB index file) and + ! incrementing upwards from there for each tau. + if (trkrinfo%gribver == 1) then + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + else + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + endif + else + ! All lead times are included in one big file. These values + ! for lugb and lugi will remain static for all taus. + lugb = 11 + lugi = 31 + endif + + ifh = 1 + + if ( verb .ge. 3 ) then + print *,'top of tracker, ifh= ',ifh,' ifhmax= ',ifhmax + endif + + ifhloop: do while (ifh <= ifhmax) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------*' + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* New forecast hour: ',i4,':',i2.2) + print *,'*-------------------------------------------*' + endif + + if (inp%file_seq == 'multi') then + + lugb = lugb + 1 + lugi = lugi + 1 + + call get_grib_file_name (ifh,gfilename,ifilename) + + if (use_waitfor == 'y') then + + ! First check for existence of grib file.... + + call waitfor(trim(gfilename),waitfor_gfile_status + & ,wait_min_age,wait_min_size,wait_max_wait + & ,wait_sleeptime) + if (waitfor_gfile_status /= 0) then + print *,' ' + write(6,405) + write(6,406) wait_max_wait,trim(gfilename) + 405 format('ERROR: TIMEOUT from waitfor for GRIB file.') + 406 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + ! Now check for existence of index file. Use a separate + ! max_wait time -- a much shorter one -- since once the + ! grib file is there, the index file should appear within + ! a matter of seconds. Also, the index file is much + ! smaller, so set the wait_min_size accordingly. + + wait_max_ifile_wait = 180 + wait_min_size = 500 + call waitfor(trim(ifilename),waitfor_ifile_status + & ,wait_min_age,wait_min_size,wait_max_ifile_wait + & ,wait_sleeptime) + if (waitfor_ifile_status /= 0) then + print *,' ' + write(6,415) + write(6,416) wait_max_ifile_wait,trim(ifilename) + 415 format('ERROR: TIMEOUT from waitfor for INDEX file.') + 416 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + endif + + call open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: from open_grib_files, rc= ' + & ,iret + print *,'!!! Files after hour0 are missing, ' + & ,'exiting normally' + stop 0 + endif + endif + + if (trkrinfo%inp_data_type == 'grib') then + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is CLOSED' + endif + endif + + !-------------------------------------------------------------- + ! Within this next IF statement, we deal with writing out atcf + ! records for storms for the case in which we have netcdf data, + ! but that netcdf data does not have hour0 data (as of Nov 2016, + ! this is the case for FV3 data). In this case, we write out + ! missing values for the hour0 time, and then we update the + ! guess for next lead time by extrapolating data from TC Vitals. + ! Note in the IF statement itself, "iftotalmins" is the array + ! of *user-requested* lead times, meaning that the user has + ! requested to look at hour0, but the ncfile_has_hour0 flag + ! indicates the hour0 time is not in the NetCDF data. + !-------------------------------------------------------------- + + if (ifh == 1 .and. iftotalmins(ifh) == 0 .and. + & trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') then + + null_netcdf_hour0_storm_loop: do inctcv = 1,numtcv + + call output_atcfunix (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,inctcv + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',inctcv + write (6,431) storm(inctcv)%tcv_storm_id + & ,storm(inctcv)%tcv_storm_name + 431 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + + call advect_tcvitals_from_hour0 (slonfg,slatfg,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) + + if (iatret /= 0) then + fixlon (inctcv,ifh) = -999.0 + fixlat (inctcv,ifh) = -999.0 + stormswitch(inctcv) = 2 + cycle null_netcdf_hour0_storm_loop + endif + + stormswitch(inctcv) = 1 + + enddo null_netcdf_hour0_storm_loop + + ifh = ifh + 1 + cycle ifhloop + + endif + + !-------------------------------------------------------------- + ! Make call to getgridinfo in order to get info on the imax, + ! jmax, as well as the x- and y-increments, and also to see if + ! the grid is correctly oriented for the tracker so that the + ! data go north to south and west to east or if we need to flip + ! either the lats or the lons. + !-------------------------------------------------------------- + + if (trkrinfo%inp_data_type == 'grib') then + call getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) + else + print *,' ' + print *,'!!! ERROR: trkrinfo%inp_data_type NOT VALID ' + print *,'!!! trkrinfo%inp_data_type= ',trkrinfo%inp_data_type + print *,'!!! Should have value of grib or netcdf.' + print *,'!!! EXITING....' + print *,' ' + stop 93 + endif + + if (iggret == 0) then + if ( verb .ge. 1 ) then + print *,'TEST after getgridinfo in sub tracker, ' + & ,'iggret= ',iggret + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in getgridinfo, rc= ' + & ,iggret + endif + stop 95 + endif + + if (inp%modtyp == 'regional' .and. inp%nesttyp == 'moveable') + & then + if (glatmax == prev_latmax .and. glatmin == prev_latmin .and. + & glonmax == prev_lonmax .and. glonmin == prev_lonmin) then + ! The moveable, nested regional grid has not moved since + ! the last lead time. This could be an indication that the + ! model lost the storm and so the grid has not moved to + ! stay with the cyclone center. Set a flag to indicate this. + gridmove_status = 'stopped' + else + gridmove_status = 'moving' + endif + else + gridmove_status = 'notappl' + endif + + prev_latmax = glatmax + prev_latmin = glatmin + prev_lonmax = glonmax + prev_lonmin = glonmin + + gotten_avg_value = 'n' + +c First, allocate the working data arrays.... + + if (allocated(valid_pt)) deallocate (valid_pt) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + + ! Allocate all of the allocatable arrays.... + + allocate (valid_pt(imax,jmax),stat=ivpa) + allocate (zeta(imax,jmax,nlevzeta),stat=iza) + allocate (u(imax,jmax,nlevs),stat=iua) + allocate (v(imax,jmax,nlevs),stat=iva) + allocate (hgt(imax,jmax,nlevhgt),stat=iha) + allocate (slp(imax,jmax),stat=isa) + allocate (tmean(imax,jmax),stat=ita) + allocate (thick(imax,jmax,nlevthick),stat=itha) + allocate (lsmask(imax,jmax),stat=ilma) + allocate (masked_out(imax,jmax),stat=imoa) + allocate (masked_outc(imax,jmax),stat=imoca) + + ita=0 + icpsa=0 + if (phaseflag == 'y') then + if (phasescheme == 'cps' .or. phasescheme == 'both') then + if (allocated(cpshgt)) deallocate (cpshgt) + allocate (cpshgt(imax,jmax,nlevs_cps),stat=icpsa) + endif + endif + + if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. + & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. + & itha /= 0 .or. imoa /= 0 .or. imoca /= 0 .or. ilma /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating arrays.' + print *,'!!! iza = ',iza,' iua= ',iua,' iha= ',iha + print *,'!!! iva = ',iva,' isa= ',isa,' icpsa= ',icpsa + print *,'!!! iksa = ',iksa,' isda= ',isda,' ivpa= ',ivpa + print *,'!!! ita = ',ita,' imoa= ',imoa,' imoca= ',imoca + print *,'!!! itha = ',itha,' ilma= ',ilma + endif + itret = 94 + return + endif + + masked_out = .false. ! Initialize all pts to false at each hr + masked_outc = .false. ! Initialize all pts to false at each hr + + if ( verb .ge. 3 ) then + print *,'in beginning of tracker, imax= ',imax,' jmax= ',jmax + endif + +c Initialize all readflags to NOT FOUND for this forecast time, +c then call subroutine to read data for this forecast time. + + zeta = -9999.0 + u = -9999.0 + hgt = -9999.0 + v = -9999.0 + slp = -9999.0 + tmean = -9999.0 + + readflag = .FALSE. + + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: b4 getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + + if (trkrinfo%inp_data_type == 'grib') then + call getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,32) date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: after getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + +c Count how many parms were successfully read for this fcst time. +c Also, for right now, put the value of readflag into all of the +c calcparms for parameters 3 through 9. Note that in getdata we +c read in 17 parms, but in this next loop we only check the +c readflags up to maxtp (= 14 as of 7/2015). That's because +c parms 12 & 13 are for 500 mb u & v, which are not used for +c tracking, only for calculating the deep layer mean wind for +c the next guess, and parm 14 is the 300-500 mb mean temperature, +c which is used for determining storm phase. Parms 10 & 11 are +c for the near-surface winds, which are used in estimating surface +c winds near the storm, and will now also be used as a +c parameter for position estimates. Finally, parm 17 is the +c land-sea mask, which is not used as a tracking parm. + + idum = 0 + do irf = 1,nreadparms + if (readflag(irf)) idum = idum + 1 + if (irf > 2 .and. irf < 10) then + ! calcparm for parms > 9 is done further below. + do jj=1,maxstorm + calcparm(irf,jj) = readflag(irf) + enddo + endif + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Of ',nreadparms,' readable parms, you read in ',idum + print *,'parms for this fcst hour from the input grib file.' + endif + +c If not enough tracked parms were read in, exit the program.... + + if (idum == 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in subroutine tracker' + print *,'!!! Not enough tracked parms read in from getdata.' + print *,'!!! Check for a problem with the input GRIB file.' + print *,'!!! Model identifier = ',inp%model + print *,'!!! STOPPING EXECUTION FOR THIS MODEL' + endif + itret = 99 + ifhtemp = ifh + do while (ifhtemp <= ifhmax) + do istmp=1,maxstorm + fixlon (istmp,ifhtemp) = -999.0 + fixlat (istmp,ifhtemp) = -999.0 + enddo + ifhtemp = ifhtemp + 1 + enddo +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) + if (ifh == 1) then + ! Per Jim Gross (1/01), if the tracker ran but was unable + ! to get an initial fix (or, in this case, unable to get + ! the data needed to run), write out zeroes for the 00h + ! fixes to indicate that the tracker ran unsuccessfully, + ! but don't write out any subsequent forecast times + ! with zeroes.... + vradius = 0 + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initial value of 'undetermined' + do istmp = 1,maxstorm + if (stormswitch(istmp) /= 3) then + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0,-999.0,inp,istmp + & ,ifcsthour,0.0,0.0,vradius,maxstorm + & ,trkrinfo,-99.0,-99.0,-99.0,cps_vals + & ,wcore_flag,ioaxret) + call output_hfip (-999.0,-999.0,inp,istmp + & ,ifh,0.0,0.0,vradius,-99.0,ioaxret) + endif + enddo + endif + return + endif + +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for z850, z700 and mslp.... + + if (user_wants_to_track_gph850 == 'n' .or. + & user_wants_to_track_gph850 == 'N') then + do jj=1,maxstorm + calcparm(7,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_gph700 == 'n' .or. + & user_wants_to_track_gph700 == 'N') then + do jj=1,maxstorm + calcparm(8,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_mslp == 'n' .or. + & user_wants_to_track_mslp == 'N') then + do jj=1,maxstorm + calcparm(9,jj) = .FALSE. + enddo + endif + + +c Parameters 1 & 2 are abs vorticity at 850 & 700. If the data +c files had this parm at 850 & 700 (ECMWF & UKMET do NOT), then +c we don't need to re-calculate relative vorticity, we just need +c to subtract out the Coriolis component. If the files did not +c have vorticity, then we need to calculate relative vorticity. +c If we're able to read vorticity or calculate it, then set the +c vorticity calcparms to TRUE for all storms for now. + + vortloop: do ivort=1,2 + + if (ivort == 1) then + if (user_wants_to_track_zeta850 == 'n' .or. + & user_wants_to_track_zeta850 == 'N') then + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (ivort == 2) then + if (user_wants_to_track_zeta700 == 'n' .or. + & user_wants_to_track_zeta700 == 'N') then + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (readflag(ivort)) then + + call subtract_cor (imax,jmax,dy,ivort) + + do jj=1,maxstorm + calcparm(ivort,jj) = .TRUE. + enddo + else + if (ivort == 1) then + if (readflag(3) .and. readflag(4)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(1,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + endif + else + if (readflag(5) .and. readflag(6)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(2,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + endif + endif + endif + + enddo vortloop + + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for user preferences for the wind circulation +c difference at 850 & 700... + + if (readflag(3) .and. readflag(4)) then + if (user_wants_to_track_wcirc850 == 'n' .or. + & user_wants_to_track_wcirc850 == 'N') then + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(3,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + endif + + if (readflag(5) .and. readflag(6)) then + if (user_wants_to_track_wcirc700 == 'n' .or. + & user_wants_to_track_wcirc700 == 'N') then + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(5,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + endif + + +c Compute the sfc vorticity if sfc_u and sfc_v have been read in. + + if (readflag(10) .and. readflag(11)) then + + if (user_wants_to_track_wcircsfc == 'n' .or. + & user_wants_to_track_wcircsfc == 'N') then + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(10,jj) = .TRUE. + enddo + endif + + if (user_wants_to_track_zetasfc == 'n' .or. + & user_wants_to_track_zetasfc == 'N') then + do jj=1,maxstorm + calcparm(11,jj) = .FALSE. + enddo + else + ! The 3 in the next call to rvcal is to indicate the 3rd + ! level for the zeta array, which is for the surface (or + ! 10m) data. + call rvcal (imax,jmax,dx,dy,3,valid_pt) + do jj=1,maxstorm + calcparm(11,jj) = .TRUE. + enddo + endif + + else + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + calcparm(11,jj) = .FALSE. + enddo + endif + + +c Compute the thicknesses for 200-850, 200-500 and 500-850 mb +c if the gp hgt fields have been read in for 200, 500 and 850. + + if (readflag(7) .and. readflag(15) .and. readflag(16)) then + + call thickness_calc (imax,jmax,valid_pt) + + do jj=1,maxstorm + + if (user_wants_to_track_thick500850 == 'n' .or. + & user_wants_to_track_thick500850 == 'N') then + calcparm(12,jj) = .FALSE. + else + calcparm(12,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200500 == 'n' .or. + & user_wants_to_track_thick200500 == 'N') then + calcparm(13,jj) = .FALSE. + else + calcparm(13,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200850 == 'n' .or. + & user_wants_to_track_thick200850 == 'N') then + calcparm(14,jj) = .FALSE. + else + calcparm(14,jj) = .TRUE. + endif + + enddo + else + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Thickness will not be tracked since at least' + print *,'one of the gp height fields was not read in.' + print *,' readflag(7) -- 850 mb ---> ',readflag(7) + print *,' readflag(15) -- 500 mb ---> ',readflag(15) + print *,' readflag(16) -- 200 mb ---> ',readflag(16) + print *,' ' + endif + do jj=1,maxstorm + calcparm(12,jj) = .FALSE. + calcparm(13,jj) = .FALSE. + calcparm(14,jj) = .FALSE. + enddo + endif + +c --------------------------------------------------------------- +c Now call find_maxmin for the variables zeta, hgt and slp. Only +c process those storms for which stormswitch is set to 1. If a +c storm is selected to be processed, we still have to check the +c calcparm for each parameter, to make sure that the particular +c parm exists at that level and is able to be processed. +c +c The following commented-out data statements are just included +c as a reference so you can see the array positioning of the +c different parameters and levels that are read in: +c +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,sfc,sfc +c ,100,100,100,100,100/ +c data iglev /850,700,850,850,700,700,850,700,0,sfc,sfc +c ,500,500,400,500,200/ +c +c And also for reference, here are the variables / levels for +c the *tracked* parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c NOTE: For mid-latitude cases, we will track ONLY mslp, which +c is why we set all the other calcparms to 'false' just below. + + if (trkrinfo%type == 'midlat') then + do m = 1,maxstorm + calcparm(1,m) = .false. + calcparm(2,m) = .false. + calcparm(3,m) = .false. + calcparm(4,m) = .false. + calcparm(5,m) = .false. + calcparm(6,m) = .false. + calcparm(7,m) = .false. + calcparm(8,m) = .false. + calcparm(10,m) = .false. + calcparm(11,m) = .false. + calcparm(12,m) = .false. + calcparm(13,m) = .false. + calcparm(14,m) = .false. + enddo + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + call sort_storms_by_pressure (gridprs,ifh,maxstorm,prsindex + & ,issret) + if ( (ifh == 1) .or. + & (ifh == 2 .and. trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') ) then + stormct = numtcv + endif + endif + + prevstormct = stormct + tracking_previously_known_storms = .true. + + stormloop: do sl_counter = 1,maxstorm + + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initialized value of 'undetermined' + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + ist = prsindex(sl_counter) + else + ist = sl_counter + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + + if (ist == (prevstormct + 1)) then + + ! For the mid-latitude and tropical cyclogenesis cases, we + ! need to scan the mslp field to find new storms. If we + ! are at this point inside the if statement in stormloop, + ! then that means we have looped through and attempted to + ! track all storms that have already been found up to this + ! point in the forecast, and we need to scan the field for + ! any new storms at this forecast hour. If this is for + ! forecast hour = 0, then right off the bat we may be + ! scanning the field (if there were no tcvitals records + ! read in for this forecast), since ist = 1 and + ! (prevstormct + 1) = 0 + 1 = 1. All that the call just + ! below to first_ges_center does is return a rough idea + ! of the location of new lows; more specific locations are + ! obtained through the barnes analysis tracking algorithm + ! further below. + + if (readflag(9)) then + if (ifh > 1) then + ! We need the use of 2 different masks. One + ! (masked_out) is to be used when looking for new lows, + ! so that after we find a new low, we mask out the + ! surrounding area so we don't find it on a subsequent + ! search for this forecast hour. The other + ! (masked_outc) is used in the routine to check for a + ! closed contour. If checking for a closed contour + ! at, say 70W/25N, this and surrounding points may have + ! already been masked out in first_ges_center, so "N" + ! would misleadingly/incorrectly be returned from + ! check_closed_contour, so that is why we need 2 masks. + ! But now after the first forecast hour (t=0), the way + ! we have this set up is that we track previously known + ! storms first, and once we're done with them, we + ! search for new storms at that same forecast hour. + ! But when looking for new storms, we need to know the + ! positions of the previously tracked storms at this + ! current forecast hour, so we copy the masked_outc + ! array to masked_out in this case.... + + masked_out = masked_outc + + endif + call first_ges_center (imax,jmax,dx,dy,'mslp',slp + & ,'min',trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) + tracking_previously_known_storms = .false. + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In subroutine tracker, readflag' + print *,'!!! for mslp indicates that the mslp data' + print *,'!!! is not available for this forecast ' + print *,'!!! hour, and it is needed for a "midlat"' + print *,'!!! or "tcgen" run of the tracker. ' + print *,'!!! We will exit....' + print *,'!!! readflag(9) = ',readflag(9) + print *,'!!! ifh= ',ifh + print *,' ' + endif + itret = 98 + return + endif + endif + endif + + xval = 0.0 ! initialize entire xval array to 0 + isastorm = 'U' ! re-initialize flag for each time, each storm + + select case (stormswitch(ist)) + + case (1) + + vradius = 0 + + if ( verb .ge. 2 ) then + print *,' ---------------------------------------------' + print *,' | *** TOP OF STORM LOOP *** ' + print *,' | Beginning of storm loop in tracker for' + print *,' | Storm number ',ist + write (6,418) ifhours(ifh),ifclockmins(ifh) + 418 format (1x,' | Forecast hour: ',i4,':',i2.2) + print *,' | Storm name = ',storm(ist)%tcv_storm_name + print *,' | Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,' ---------------------------------------------' + print *,' ' + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3 + & ,'_',i3.3,a1,'_',i4.4,a1,'_',a3) + + endif +c First, make sure storm is within the grid boundaries... + + call check_bounds (slonfg(ist,ifh),slatfg(ist,ifh),ist,ifh + & ,trkrinfo,icbret) + if (icbret == 95) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + + if (slatfg(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + if (calcparm(1,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,1),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(1,ist),clon(ist,ifh,1),clat(ist,ifh,1) + & ,xval(1),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(2,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,2),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(2,ist),clon(ist,ifh,2),clat(ist,ifh,2) + & ,xval(2),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(7,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,1),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(7,ist) + & ,clon(ist,ifh,7),clat(ist,ifh,7),xval(7) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(8,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,2),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(8,ist) + & ,clon(ist,ifh,8),clat(ist,ifh,8),xval(8) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(9,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for mslp' + endif + + call find_maxmin (imax,jmax,dx,dy,'slp' + & ,slp,'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(9,ist) + & ,clon(ist,ifh,9),clat(ist,ifh,9),xval(9) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(11,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for sfc zeta' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,3),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(11,ist),clon(ist,ifh,11),clat(ist,ifh,11) + & ,xval(11),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 + + if (calcparm(12,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 500-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,1),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(12,ist) + & ,clon(ist,ifh,12),clat(ist,ifh,12),xval(12) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(13,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-500 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,2),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(13,ist) + & ,clon(ist,ifh,13),clat(ist,ifh,13),xval(13) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(14,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,3),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(14,ist) + & ,clon(ist,ifh,14),clat(ist,ifh,14),xval(14) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c Now get centers for wind circulation at 700 & 850 mb and +c at 10m. First, get a modified guess lat/lon position for +c wind circulation. Do this because we will be searching +c for this wind circulation center over a smaller area and +c so it's more crucial to have a better first guess position. +c This modified guess position will be an average of the first +c guess position for this time and the fix positions for this +c time from some of the other parameters. + + if (slatfg(ist,ifh) >= 0.0) then + cmaxmin = 'max' + else + cmaxmin = 'min' + endif + + if (calcparm(3,ist) .and. calcparm(4,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 850 mb ' + endif + + print *,' ' + print *,'Before first call to get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' inp%modtyp= ',inp%modtyp + print *,' cmaxmin= ',cmaxmin + print *,' nlev850= ',nlev850 + print *,' u(1,1,nlev850)= ',u(1,1,nlev850) + print *,' u(imax,jmax,nlev850)= ',u(imax,jmax,nlev850) + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' calcparm(3,ist)= ',calcparm(3,ist) + print *,' clon(ist,ifh,3)= ',clon(ist,ifh,3) + print *,' clat(ist,ifh,3)= ',clat(ist,ifh,3) + print *,' xval(3)= ',xval(3) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,141) date_time(5),date_time(6),date_time(7) + 141 format (1x,'TIMING: Before GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,850,valid_pt,calcparm(3,ist) + & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,142) date_time(5),date_time(6),date_time(7) + 142 format (1x,'TIMING: After GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,850,valid_pt,calcparm(3,ist) +c & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + endif + else + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + clon(ist,ifh,3) = 0.0 + clat(ist,ifh,3) = 0.0 + endif + endif + + if (calcparm(5,ist).and. calcparm(6,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 700 mb ' + endif + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,143) date_time(5),date_time(6),date_time(7) + 143 format (1x,'TIMING: Before GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,700,valid_pt,calcparm(5,ist) + & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,144) date_time(5),date_time(6),date_time(7) + 144 format (1x,'TIMING: After GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,700,valid_pt,calcparm(5,ist) +c & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + endif + else + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + clon(ist,ifh,5) = 0.0 + clat(ist,ifh,5) = 0.0 + endif + endif + + if (calcparm(10,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for the' + print *,'surface (10m) level' + endif + + ! NOTE: The 1020 in the call here is just a number/code + ! to indicate to the subroutine to process sfc winds. + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,145) date_time(5),date_time(6),date_time(7) + 145 format (1x,'TIMING: Before GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,1020,valid_pt,calcparm(10,ist) + & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) + & ,trkrinfo,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,146) date_time(5),date_time(6),date_time(7) + 146 format (1x,'TIMING: After GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,1020,valid_pt,calcparm(10,ist) +c & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) +c & ,trkrinfo,igucret) + + if (igwcret /= 0) then + calcparm(10,ist) = .FALSE. + endif + else + calcparm(10,ist) = .FALSE. + clon(ist,ifh,10) = 0.0 + clat(ist,ifh,10) = 0.0 + endif + endif + +c ------------------------------------------------------ +c All of the parameter center fixes have been done. Now +c average those positions together to get the best guess +c fix position. If a center fix is able to be made, then +c call subroutine get_max_wind to get the maximum near- +c surface wind near the center, and then call get_next_ges +c to get a guess position for the next forecast hour. + + if (stormswitch(ist) == 1) then + + call fixcenter (clon,clat,ist,ifh,calcparm + & ,slonfg(ist,ifh),slatfg(ist,ifh),inp + & ,stderr,fixlon,fixlat,xval,maxstorm,ifret) + + if (ifret == 0) then + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'regional')then + if (fixlon(ist,ifh) > (trkrinfo%eastbd + 7.0) .or. + & fixlon(ist,ifh) < (trkrinfo%westbd - 7.0) .or. + & fixlat(ist,ifh) > (trkrinfo%northbd + 7.0) .or. + & fixlat(ist,ifh) < (trkrinfo%southbd - 7.0)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! will NOT be made for this time due' + print *,'!!! the storm being more than 7 degrees' + print *,'!!! outside the user-specified lat/lon' + print *,'!!! bounds for this run. We will stop' + print *,'!!! tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + 432 format (1x,'!!! Fcst hr = ',i4,':',i2.2) + print *,'!!! fixlat= ',fixlat(ist,ifh) + print *,'!!! fixlon= ',fixlon(ist,ifh) + print *,'!!! User East Bound = ',trkrinfo%eastbd + print *,'!!! User West Bound = ',trkrinfo%westbd + print *,'!!! User North Bound = ',trkrinfo%northbd + print *,'!!! User South Bound = ',trkrinfo%southbd + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + endif + cycle stormloop + endif + endif + else + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + +c Just because we've found a center doesn't mean there is +c actually a storm there. I noticed in the first year that +c for some decaying or just weak storms, the tracker would +c identify a center to follow, but it may have only been +c a weak trough passing by, or something else that's not +c our storm. This next subroutine checks to see that the +c surface pressure gradient and/or tangential winds at +c 850 mb resemble a storm. It is called twice; the first +c time for MSLP, the 2nd time for 850 mb winds. We will +c apply these storm-checking criteria if either the mslp +c or v850 check come back negative. Remember, there +c is the possibility that centers could not be found for +c 1 or both of these parameters, in which case the isastorm +c flag will have a value of 'U', for "undetermined". + + isiret1 = 0; isiret2 = 0; isiret3 = 0 + + print *,' ttest, ifret= ',ifret + + if (ifret == 0) then + + print *,' ttest, calcparm(9,ist)= ',calcparm(9,ist) + + if (calcparm(9,ist)) then + + ! Do a check of the mslp gradient.... + + print *,' ttest, in IF part: ' + print *,' clon(ist,ifh,9)= ',clon(ist,ifh,9) + print *,' clat(ist,ifh,9)= ',clat(ist,ifh,9) + print *,' xval(9)= ',xval(9) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,clon(ist,ifh,9),clat(ist,ifh,9) + & ,xval(9),trkrinfo,isastorm(1),isiret1) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for mslp (e.g., + ! maybe the mslp fix was too far away from the + ! guess?), then this check isn't performed. We are + ! changing this so that the mslp gradient check will + ! still be performed, but using the mean fixlat and + ! fixlon positions as the center. Still, we first + ! need to check to see if mslp was even read in. If + ! it wasn't, then we are just out of luck. + + print *,' ttest, in ELSE part: ' + + if (trkrinfo%use_backup_mslp_grad_check == 'y' .or. + & trkrinfo%use_backup_mslp_grad_check == 'Y') then + + print *,' ttest ELSE, readflag(9)= ',readflag(9) + + if (readflag(9)) then + + print *,'ttest ELSE A, ist= ',ist,' ifh= ',ifh + print *,'ttest ELSE A, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE A, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,9999.0,ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + + print *,'ttest ELSE B, ifilret= ',ifilret + + if (ifilret == 0) then + + print *,'ttest ELSE B, ifilret= ',ifilret + print *,'ttest ELSE B, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE B, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,gridpoint_maxmin,trkrinfo,isastorm(1) + & ,isiret1) + + if (isiret1 == 0) then + ! Even though calcparm(9) is FALSE and mslp + ! will not be used for center-fixing + ! purposes, we need to fill the clat and clon + ! arrays just a few lines below so that + ! calls to fix_latlon_to_ij below do not + ! get screwed up. So, into the clat and clon + ! arrays we put the mean fixlat and fixlon + ! positions for this lead time. + clat(ist,ifh,9) = fixlat(ist,ifh) + clon(ist,ifh,9) = fixlon(ist,ifh) + xval(9) = gridpoint_maxmin + endif + + endif + + endif + + endif + + endif + + ! If we have found a valid mslp gradient, then make + ! a call to fix_latlon_to_ij to (1) get the actual + ! gridpoint value of the mslp (the value previously + ! stored in xval(9) is an area-averaged value coming + ! from the barnes analysis), and (2) to get the + ! (i,j) indices for this gridpoint to be used in the + ! call to check_closed_contour below. + ! + ! NOTE: If a mslp fix was not made, or if the mslp + ! "isastorm" flag comes back as no, we make the same + ! call to fix_latlon_to_ij, but we use the mean fix + ! position as our input to search around, and then + ! basically we just find the lowest mslp near that + ! mean fix position. There is a check on the value + ! of xinp_fixlat and xinp_fixlon to make sure that + ! they contain valid values and not just the + ! initialized -999 values. + + if (isiret1 == 0 .and. isastorm(1) == 'Y') then + xinp_fixlat = clat(ist,ifh,9) + xinp_fixlon = clon(ist,ifh,9) + if (verb >= 3) then + print *,' ttest at location C IF....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + else + xinp_fixlat = fixlat(ist,ifh) + xinp_fixlon = fixlon(ist,ifh) + if (verb >= 3) then + print *,' ttest at location C ELSE....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + endif + + if (xinp_fixlat > -99.0 .and. xinp_fixlon > -990.0) + & then + if (verb >= 3) then + print *,' ttest at location D' + endif + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,xinp_fixlon,xinp_fixlat + & ,xval(9),ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (verb >= 3) then + print *,' ttest at location E, ifilret= ',ifilret + endif + if (ifilret == 0) then + gridprs(ist,ifh) = gridpoint_maxmin + else + ! Search went out of regional grid bounds.... + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + print *,' ttest at location F' + + ! For a "tracker" case, check to see if the user has + ! requested to compute and write out the ROCI. If + ! so, then we make a call to check_closed_contour, + ! being sure to specify 999 as the number of levels + ! to check.... + + if (isiret1 == 0 .and. isastorm(1) == 'Y' .and. + & trkrinfo%type == 'tracker') then + + if (trkrinfo%want_oci) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + if (xval(9) < 1100.0) then + ! Pressure units are in mb... + prs_contint_thresh = 4.0 + elseif (xval(9) >80000.0) then + ! Pressure units are in Pa... + prs_contint_thresh = 400.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' tracker. The mslp value' + print *,' (xval(9)) is not in range.' + print *,' before call to' + print *,' check_closed_contour.' + print *,' xval(9) = ',xval(9) + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + if (trkrinfo%contint < prs_contint_thresh) then + hold_old_contint = trkrinfo%contint + trkrinfo%contint = prs_contint_thresh + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before going into routine to diagnose' + print *,'the ROCI for a tracker run, the ' + print *,'requested contour interval is being ' + print *,'adjusted up (coarser) to avoid having' + print *,'the contour check routine break and ' + print *,'return an invalid value.' + print *,'User-requested contint value (Pa) = ' + & ,hold_old_contint + print *,'Modified contint value (Pa) = ' + & ,trkrinfo%contint + endif + endif + + masked_outc = .false. + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ' + & ,rlastbar,' nm' + print *,' ' + endif + + endif + + endif + + ! For the midlat & tcgen cases, do a check to see if + ! there is a closed mslp contour. The ifix and jfix + ! values passed into check_closed_contour are the + ! values for the (i,j) at the gridpoint minimum, + ! which was obtained just above from the call to + ! fix_latlon_to_ij. + ! UPDATE 7/12/2016 tpm: A change was made to fix a + ! hole in the logic. Previously, for a genesis run + ! (type = midlat or tcgen), if a fix was not made + ! for mslp, then the isastorm(1) flag would not be + ! 'Y', and so the call to check_closed_contour in + ! the following IF statement would not be made, and + ! that would prevent the mask from getting updated + ! for this particular storm, allowing the same storm + ! to be detected when the scan for new storms takes + ! place at this lead time (i.e., after all previously- + ! known storms from the last lead time have been + ! tracked). As a fix, if that isastorm(1) flag is not + ! 'Y', then we call a new subroutine which updates the + ! mask based on the circulation at 850 mb. + + if (isastorm(1) == 'Y' .and. isiret1 == 0 .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ',rlastbar + & ,' nm' + print *,' ' + endif + + ! This next bit of code adds a second layer of closed + ! contour checking. This is to decrease the + ! occurrence of interrupted midlat and tcgen tracks, + ! which usually happens when the closed contour + ! criterion is not met for one time period. So in + ! this next code, we check to see if the ccflag was + ! 'y' for at least half the time over the last 24h. + ! For time periods shorter than 24h (e.g., the storm + ! was just detected at 144h and we are now at 156h), + ! the threshold is still that for at least half of + ! the time the system has been detected as a storm, + ! it must have a ccflag value of 'y'. + + if (ccflag == 'y') then + closed_mslp_ctr_flag(ist,ifh) = 'y' + else + closed_mslp_ctr_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & closed_mslp_ctr_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (closed_mslp_ctr_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.50) then + ccflag = 'y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE ON CLOSED CONTOUR CHECK: The' + print *,' ccflag returned for this hour was' + print *,' NO, but a check of recent ccflags' + print *,' indicates that more than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + ccflag = 'n' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!! NOTE ON CLOSED CONTOUR CHECK: The' + print *,'!! ccflag returned for this hour was' + print *,' NO, and a check of recent ccflags' + print *,' indicates that less than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + if (ccflag == 'y') then + isastorm(2) = 'Y' + else if (ccflag == 'n') then + isastorm(2) = 'N' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*---------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*---------------------------------------*' + print *,' ' + endif + + + else if (isastorm(1) /= 'Y' .and. + & calcparm(3,ist) .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + ! The isastorm(1) flag indicates that a mslp gradient + ! could not be found at this lead time, so the mask + ! cannot be updated using mslp. Instead, + ! do a check of the 850 mb wind circulation + ! surrounding the 850 wind circulation fix, and then + ! set the mask to be TRUE for all points within the + ! area where mean cyclonic Vt exceed +1 m/s.... + +c call check_closed_contour (imax,jmax,ifix,jfix,slp +c & ,valid_pt,masked_outc,ccflag,'min',trkrinfo +c & ,999,contour_info,get_last_isobar_flag,plastbar +c & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Calling mask_based_on_wind_circ at ' + & ,ifcsthour + endif + + call mask_based_on_wind_circ (imax,jmax,dx,dy,850 + & ,valid_pt,masked_outc,trkrinfo + & ,clon(ist,ifh,3),clat(ist,ifh,3),inp%modtyp + & ,imbowret) + + endif + + ! For tropical cyclones, check the avg 850 mb tangential + ! windspeed close to the storm center.... + + if (trkrinfo%type == 'tcgen' .or. + & trkrinfo%type == 'tracker') then + + had_to_try_backup_850_vt_check = 'n' + + if (calcparm(3,ist)) then + + if (verb .ge. 3) then + print *,' ' + print *,'Checking 850 mb Vt speed using 850 mb ' + print *,'wind circulation fix: ' + print *,' 850 mb wcirc fix lon= ',clon(ist,ifh,3) + print *,' 850 mb wcirc fix lat= ',clat(ist,ifh,3) + print *,' Multi-parm fix lon= ',fixlon(ist,ifh) + print *,' Multi-parm fix lat= ',fixlat(ist,ifh) + print *,' ' + endif + + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,clon(ist,ifh,3),clat(ist,ifh,3) + & ,xval(3),trkrinfo,isastorm(3),isiret3) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for 850 mb wind + ! circulation (maybe the 850 mb wind circulation fix + ! was too far away from the guess?), then this check + ! isn't performed. We are changing this so that the + ! 850 mb Vt wind speed check will still be + ! performed, but using the mean fixlat and fixlon + ! positions as the center. Still, we first need to + ! check to see if 850 mb u-comp and v-comp were even + ! read in. If they weren't, then we are just out + ! of luck. + + had_to_try_backup_850_vt_check = 'y' + isiret3 = -99 + + if (trkrinfo%use_backup_850_vt_check == 'y' .or. + & trkrinfo%use_backup_850_vt_check == 'Y') then + + if (readflag(3) .and. readflag(4)) then + + if (verb .ge. 3) then + print *,' ' + print *,'!!! NOTE: 850 mb wcirc fix not ' + print *,'available. We are instead ' + print *,'checking 850 mb Vt speed using ' + print *,'multi-parm fix position: ' + print *,' Multi-parm fix lon= ' + & ,fixlon(ist,ifh) + print *,' Multi-parm fix lat= ' + & ,fixlat(ist,ifh) + print *,' ' + endif + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,0.00,trkrinfo,isastorm(3),isiret3) + + endif + + endif + + endif + + if (calcparm(3,ist) .or. + & (had_to_try_backup_850_vt_check == 'y' .and. + & isiret3 == 0) ) then + + if (trkrinfo%type == 'tcgen') then + ! This next bit of code adds a second layer of 850 + ! mb Vt magnitude checking. This is to decrease + ! the occurrence of interrupted tcgen tracks, + ! which occasionally happens for weak storms when + ! this criterion is not met for one time period. + ! So in this next code, we check to see if the + ! vt850_flag was 'y' for at least 75% of the time + ! over the last 24h. For time periods shorter + ! than 24h (e.g., the storm was just detected at + ! 144h and we are now at 156h), the threshold is + ! still that for at least 75% of the time the + ! system has been detected as a storm, it must + ! have a vt850_flag value of 'y'. + + if (isastorm(3) == 'Y') then + vt850_flag(ist,ifh) = 'y' + else + vt850_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & vt850_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - + & fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (vt850_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / + & cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.75) then + isastorm(3) = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ NOTE ON Vt_850 CHECK: The ' + print *,' isastorm flag returned for ' + print *,' this hour was NO, but a' + print *,' check of recent vt850_flags' + print *,' indicates that more than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE ON Vt_850 CHECK: The ' + print *,'!!! isastorm flag returned for ' + print *,' this hour was NO, and a' + print *,' check of recent vt850_flags ' + print *,' indicates that less than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + endif + + endif + endif + + else + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + isastorm(1) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! could not be made for mslp, ' + print *,'!!! therefore we will stop tracking ' + print *,'!!! for this storm.' + endif + + else + isastorm(1) = 'N' + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a TC tracker case, a fix could' + print *,'!!! not be made using any tracked parms,' + print *,'!!! therefore we will stop tracking for' + print *,'!!! this storm.' + endif + + endif + + if ( verb .ge. 3 ) then + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + + endif + + if (isiret1 /= 0 .or. isiret2 /= 0 .or. isiret3 /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: One of the calls to ' + print *,'!!! is_it_a_storm produced an error.' + print *,'!!! Chances are this is from a call to ' + print *,'!!! get_ij_bounds, meaning we are too close' + print *,'!!! to a regional grid boundary to do this ' + print *,'!!! analysis. Processing will continue....' + print *,'!!! isiret1= ',isiret1,' isiret2= ',isiret2 + print *,'!!! isiret3= ',isiret3 + endif + + endif + + if (isastorm(1) == 'N' .or. isastorm(2) == 'N' .or. + & isastorm(3) == 'N') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! At least one of the isastorm flags from' + print *,'!!! subroutine is_it_a_storm is "N", so ' + print *,'!!! either we were unable to find a good ' + print *,'!!! mslp gradient and/or a valid 850 mb ' + print *,'!!! circulation for the storm at this time,' + print *,'!!! or, for the cases of midlat or tcgen ' + print *,'!!! tracking, a closed mslp contour could ' + print *,'!!! not be found, thus we will stop tracking' + print *,'!!! this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! mslp gradient flag = ',isastorm(1) + print *,'!!! closed contour flag = ',isastorm(2) + print *,'!!! 850 mb winds flag = ',isastorm(3) + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + + ! Now do another check for the tracker and tcgen cases. + ! If the isastorm flags for mslp gradient and v850 BOTH + ! came back positive AND you have been able to locate an + ! 850 mb vort center, just do a check to make sure that + ! the distance between the 850 vort center and the mslp + ! center is not too great. + + if (trkrinfo%type == 'tracker' .or. + & trkrinfo%type == 'tcgen') then + if (isastorm(1) == 'Y' .and. isastorm(3) == 'Y' .and. + & calcparm(1,ist) .and. stormswitch(ist) == 1) then + +c if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) >= 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) < 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else +c trkrinfo%max_mslp_850 = 323.0 +c endif + + call calcdist (clon(ist,ifh,9),clat(ist,ifh,9) + & ,clon(ist,ifh,1),clat(ist,ifh,1),dist + & ,degrees) + + if (dist > trkrinfo%max_mslp_850) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, the dist betw' + print *,'!!! the mslp center & the 850 zeta ' + print *,'!!! center is too great, thus we will' + print *,'!!! stop tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + print *,'!!! Actual distance (km) = ',dist + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Actual distance between the parm centers' + print *,'for 850 zeta and mslp is ',dist,' (km)' + print *,'Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + endif + + endif + endif + endif + + ! Do one final check. Check the new fix position and + ! the old fix position and calculate the speed that the + ! storm would have had to travel to get to this point. + ! If that speed exceeds a certain threshold (~60 kt), + ! assume you're tracking the wrong thing and quit. + ! Obviously, only do this for times > 00h. The check + ! in the if statement to see if the previous hour's + ! lats and lons were > -999 is for the midlat and + ! tcgen cases -- remember, they can have genesis at + ! any hour of the forecast, in which case the previous + ! forecast hour's lat & lon would be -999. + + if (ifh > 1 .and. stormswitch(ist) == 1) then + if (fixlon(ist,ifh-1) > -999.0 .and. + & fixlat(ist,ifh-1) > -999.0 ) then + + if (trkrinfo%type == 'midlat') then + xmaxspeed = maxspeed_ml + else + xmaxspeed = maxspeed_tc + endif + + call calcdist (fixlon(ist,ifh-1),fixlat(ist,ifh-1) + & ,fixlon(ist,ifh),fixlat(ist,ifh),dist + & ,degrees) + + ! convert distance from km to nm and get speed. + + distnm = dist * 0.539638 + xinterval_fhr = fhreal(ifh) - fhreal(ifh-1) + xknots = distnm / xinterval_fhr + + if (xknots > xmaxspeed) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, calculated spd' + print *,'!!! of the storm from the last position' + print *,'!!! to the current position is too high,' + print *,'!!! so we will stop tracking this storm' + print *,'!!! (For fear that we are not actually ' + print *,'!!! tracking our storm, but have instead' + print *,'!!! locked onto some other feature....)' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max speed allowed (kt) = ',xmaxspeed + print *,'!!! Actual speed (kt) = ',xknots + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'The average speed that the storm moved' + print *,'at since the previous forecast time is' + & ,xknots,' knots.' + endif + + endif + + endif + + endif + + endif + +c Now get the maximum near-surface wind speed near the storm +c center (get_max_wind). Also, call getradii to get the +c radii in each storm quadrant of gale-force, storm-force +c and hurricane force winds. + + if (readflag(10) .and. readflag(11) .and. ifret == 0 + & .and. stormswitch(ist) == 1) then + call get_max_wind (fixlon(ist,ifh),fixlat(ist,ifh) + & ,imax,jmax,dx,dy,valid_pt,levsfc + & ,xmaxwind(ist,ifh),trkrinfo,rmax,igmwret) +c if (igmwret /= 0 .and. gridmove_status == 'stopped') then + if (igmwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Return code from get_max_wind is /= 0. ' + print *,'!!! rcc= igmwret= ',igmwret + print *,'!!! Also, this is a moveable, regional grid' + print *,'!!! and the grid did not change from last' + print *,'!!! lead time to current one, so what has' + print *,'!!! likely happened is that the storm has ' + print *,'!!! moved close to the edge of the nested ' + print *,'!!! grid domain, but the nested grid itself' + print *,'!!! had stopped moving, probably because it' + print *,'!!! dropped or lost the storm.' + print *,'!!! ' + print *,'!!! TRACKING WILL STOP FOR THIS STORM' + print *,'!!! ' + endif + + stormswitch(ist) = 2 + cycle stormloop + endif + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + + ! For the radii, we encountered a problem with radmax + ! being too small. It was set at 650 km. Hurricane + ! Sandy exceeded this in the models, so the values + ! returned from getradii were close to the default + ! radmax value of 650 km (350 nm), instead of higher. + ! To fix it, we now use an iterative technique, where + ! we start with radmax as a small value (500 km). If + ! getradii returns a value for R34 in a quadrant that + ! does not exceed 0.97*radmax, then that value is ok. + ! If it does exceed 0.97*radmax, then we bump up radmax + ! by 50 km and call getradii again, looking to diagnose + ! radii only in those quadrants where the + ! need_to_expand_r34 flag = 'n'. BTW... note the + ! initial IF statement... we will only go into this + ! routine if the max wind just diagnosed for this lead + ! time is at least 34 kts (17.5 m/s). + + if (xmaxwind(ist,ifh) >= 17.5) then + + vradius = 0 + first_time_thru_getradii = .true. + r34_check_okay = 'n' + do ivr = 1,4 + need_to_expand_r34(ivr) = 'y' + enddo + radmax = 500.0 ! Initial radmax, in km + + igrct = 1 + + if ( verb .ge. 3 ) then + write (6,242) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 242 format (1x,'TIMING: b4 getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + getrad_iter_loop: do while + & (r34_check_okay == 'n' .and. radmax <= 1050.) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,244) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 244 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + call getradii (fixlon(ist,ifh),fixlat(ist,ifh),imax + & ,jmax,dx,dy,valid_pt,storm(ist)%tcv_storm_id + & ,ifcsthour,xmaxwind(ist,ifh),vradius + & ,trkrinfo,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) + + if (igrret /= 0) then + if (verb >= 3) then + print *,' ' + print *,'!!! ERROR: Return code from getradii = ' + & ,igrret + print *,'!!! Searching for radii will not be ' + print *,'!!! completed for this lead time and' + print *,'!!! all radii values will be set to ' + print *,'!!! missing.' + print *,' ' + exit getrad_iter_loop + endif + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,245) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 245 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + first_time_thru_getradii = .false. + igrct = igrct + 1 + r34_dist_thresh = 0.97 * radmax + r34_good_ct = 0 + do ivr = 1,4 + vradius_km = float(vradius(1,ivr)) / 0.5396 + if (vradius_km < r34_dist_thresh) then + r34_good_ct = r34_good_ct + 1 + need_to_expand_r34(ivr) = 'n' + endif + enddo + if (r34_good_ct == 4) then + r34_check_okay = 'y' + endif + radmax = radmax + 50.0 + enddo getrad_iter_loop + + if ( verb .ge. 3 ) then + write (6,246) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 246 format (1x,'TIMING: after getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + endif + + endif + +c If the user has requested so, then call a routine to +c determine the type of cyclone, using Bob Hart's +c cyclone phase space (CPS) algorithms. It is only used +c for times after t=0, since for the first check (of the +c "parameter B" thickness asymmetry), we need to know +c in which direction the storm is moving. Pulling that +c storm movement data off of the tcvitals is not reliable +c since the model storm may not be moving in the same +c direction as the observed storm. However, we could do +c an upgrade later where this storm movement data is +c pulled from the "genesis vitals", which are derived +c from the model forecast data itself, not the obs. + + if (phaseflag == 'y' .and. stormswitch(ist) == 1) then + wcore_flag = 'u' ! 'u' = undetermined + call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag,igpret) + endif + + if (structflag == 'y' .or. ikeflag == 'y') then + call get_sfc_center (fixlon(ist,ifh),fixlat(ist,ifh) + & ,clon,clat,ist,ifh,calcparm,xsfclon + & ,xsfclat,maxstorm,igscret) + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,er_wind,sr_wind,er_vr,sr_vr + & ,er_vt,sr_vt,maxstorm,trkrinfo,igwsret) + if (igwsret == 0) then + call output_wind_structure (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),er_wind,sr_wind + & ,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iowsret) + endif + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,wfract_cov,pdf_ct_bin + & ,pdf_ct_tot,maxstorm,trkrinfo,igfwret) + if (igfwret == 0) then + call output_fract_wind (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),wfract_cov,'earth' + & ,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) + endif + endif + + if (ikeflag == 'y' .and. stormswitch(ist) == 1) then + call get_ike_stats (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,ike,sdp,wdp,maxstorm + & ,trkrinfo,igisret) + if (igisret == 0) then + call output_ike (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),ike,sdp,wdp,maxstorm + & ,ioiret) + endif + endif + +c Now print out the current fix position and intensity +c (in knots) to standard output. Conversion for m/s to +c knots (1.9427) is explained in output_atcf. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to fixcenter, fix positions at ' + write (6,442) ifhours(ifh),ifclockmins(ifh) + 442 format (1x,'forecast hour= ',i4,':',i2.2,' follow:') + print *,' ' + endif + + if (ifret == 0 .and. stormswitch(ist) == 1) then + + if ( verb .ge. 3 ) then + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + & ,int((xmaxwind(ist,ifh)*1.9427) + 0.5) + print *,' ' + endif + + ! Only call output routines every atcffreq/100 hours.... + + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + + if (leadtime_check == 0) then + + ifcsthour = ileadtime / 100 + + call output_atcfunix (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + + ! Get the storm motion vector and the speed of + ! motion so that we can output this in the + ! "atcf_sink" forecast text file. + + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'vitals',trkrinfo + & ,ignret) + else + istmdir = -999 + istmspd = -999 + ignret = 0 + endif + + if ( verb .ge. 3 ) then + write (6,617) istmspd,istmdir,ignret + 617 format (1x,'+++ RPT_STORM_MOTION: istmspd= ',i5 + & ,' istmdir= ',i5,' rcc= ',i3) + endif + + ! Call a routine to find the mean & max relative + ! vorticity near the storm at 850 & 700. These will + ! be written out to the "atcf_sink" fcst text file. + + imeanzeta = -99 + igridzeta = -99 + call get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo + & ,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + endif + + call output_atcf_sink (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta + & ,igridzeta,cps_vals,plastbar,rlastbar + & ,ioaxret) + + if (inp%model == 12 .and. ifcsthour == 0) then + ! Write vitals for GFS ens control analysis + call output_tcvitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,iovret) + + endif + endif + + ! The exception here is for the call to the output_hfip + ! routine, which will be called for every lead time + ! that is processed.... + + call output_hfip (fixlon(ist,ifh),fixlat(ist,ifh),inp,ist + & ,ifh,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,rmax,ioaxret) + else + + if ( verb .ge. 3 ) then + write (6,452) 'fixpos ',storm(ist)%tcv_storm_id + & ,' fhr= ',ifhours(ifh),ifclockmins(ifh) + & ,' Fix not made for this forecast hour' + 452 format (1x,a7,1x,a4,a6,i4,':',i2.2,a36) + + print *,' ' + print *,'!!! RETURN CODE from fixcenter not equal to 0,' + print *,'!!! or output from is_it_a_storm indicated the' + print *,'!!! system found was not our storm, or the ' + print *,'!!! speed calculated indicated we may have ' + print *,'!!! locked onto a different center, thus a fix' + print *,'!!! was not made for this storm at this ' + print *,'!!! forecast hour.' + print *,'!!! mslp gradient check = ',isastorm(1) + print *,'!!! mslp closed contour check = ',isastorm(2) + print *,'!!! 850 mb winds check = ',isastorm(3) + print *,'!!! fixcenter return code = ifret = ',ifret + print *,' ' + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + +c if (inp%model == 1 .or. inp%model == 8 .or. +c & inp%model == 22) then +cPENG + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + + ! For the vt=00h lead time, if the tracker failed to + ! locate a position, we are going to write out an + ! atcfunix record that contains the position, + ! intensity, mslp and 34-kt wind radii from TC Vitals + ! for this storm and initial time. Only do this for + ! the GFS or GDAS runs of the tracker. + + tcv_max_wind_ms = float(storm(ist)%tcv_vmax) + tcv_mslp_pa = float(storm(ist)%tcv_pcen) * 100.0 + + ! Convert tcvitals NE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15ne) + if (r34_from_tcv > 0.0) then + vradius(1,1) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,1) = 0 + endif + + ! Convert tcvitals SE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15se) + if (r34_from_tcv > 0.0) then + vradius(1,2) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,2) = 0 + endif + + ! Convert tcvitals SW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15sw) + if (r34_from_tcv > 0.0) then + vradius(1,3) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,3) = 0 + endif + + ! Convert tcvitals NW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15nw) + if (r34_from_tcv > 0.0) then + vradius(1,4) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,4) = 0 + endif + + ! Convert tcvitals roci from km to nm + + if (storm(ist)%tcv_penvrad > 0) then + roci_from_tcv = float(storm(ist)%tcv_penvrad) + rlastbar = roci_from_tcv * 0.5396 + else + rlastbar = -99.0 + endif + + ! Convert tcvitals pressure at roci from km to nm + + if (storm(ist)%tcv_penv > 0) then + proci_from_tcv = float(storm(ist)%tcv_penv) + plastbar = proci_from_tcv * 100.0 + else + plastbar = -99.0 + endif + + write (6,291) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + & ,atcfymdh + 291 format (1x,'NOTE: TCVITALS_USED_FOR_ATCF_F00 ' + & ,' Storm ID: ',a4,' Storm name: ',a9 + & ,' YMDH: ',i10) + + call output_atcfunix (slonfg(ist,ifh) + & ,slatfg(ist,ifh),inp,ist + & ,ifcsthour,tcv_max_wind_ms + & ,tcv_mslp_pa,vradius,maxstorm,trkrinfo + & ,plastbar,rlastbar,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + else + + ! For all other models, we print out missing + ! data values at tau=00h if the tracker was + ! unable to find the storm.... + + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + + endif + + imeanzeta = -99 + igridzeta = -99 + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (trkrinfo%type == 'tracker') then + ! Update 11/11: For a 'tracker' run, i.e., one in + ! which we know that there is an observed storm in + ! the area, we will assume that there was some type + ! of problem in the initialization that prevented + ! the storm from being found. In this case, even + ! though we have written out zeroes for the 00h + ! time, we want to at least try tracking again at + ! the next lead time. Requested by HWRF folks.... + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',ist + write (6,301) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + 301 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + call get_next_ges (slonfg,slatfg,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + stormswitch(ist) = 1 + endif + + endif + cycle stormloop + endif + + +c Now get first guess for next forecast time's position. +c But first, if this is the first time level (ifh=1) and +c the user has requested that storm vitals be output (this +c is usually only done for model analyses in order to get +c an analysis position from one time to the next), we will +c write out a storm vitals record for this time level. +c Note that we have already gotten the next guess position +c info just above for the case of the repeated analysis +c data, so we'll just output the genesis vitals record. + + if (ifh <= ifhmax) then + if (ifh == 1 .and. trkrinfo%out_vit == 'y') then + call output_gen_vitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,istmspd,istmdir,iovret) + endif + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: Problem getting first guess ' + print *,'!!! position for next lead time. Return' + print *,'!!! code from call to get_next_ges = ' + print *,'!!! ignret = ',ignret + print *,'!!! Storm name = ' + & ,storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! TRACKING WILL STOP FOR THIS STORM.' + endif + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + else + istmdir = -999 + istmspd = -999 + endif + endif + + case (2) + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Case 2 in tracker for stormswitch' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + endif + + case (3) + continue + +c print *,' ' +c print *,'!!! Case 3 in tracker for stormswitch' +c print *,'!!! Storm name = ',storm(ist)%tcv_storm_name +c print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + if (leadtime_check == 0) then + ifcsthour = ileadtime / 100 + endif + if (trkrinfo%inp_data_type == 'grib') then + call output_tracker_mask (masked_outc,valid_pt,ifh + & ,ifcsthour,imax,jmax,iotmret) + endif + endif + + if(use_per_fcst_command=='y') then +c User wants us to run a command per forecast time + +! Replace %[FHOUR] with forecast hour, %[FMIN] with forecast minute. + +! The %[] format is chosen to avoid shell syntax errors if someone +! includes unknown %[] constructs. A stray , for example, +! would generate syntax errors or unexpected results in some +! shells. + +! If an unrecognized %[xxx] sequence is used, it will be retained in +! the final command. This allows the underlying command to detect +! the unreplaced %[] and use suitable default values or abort, as +! appropriate. + + pfc_final=per_fcst_command + call argreplace(pfc_final,pfc_cmd_len,'%[FHOUR]', & + & ifhours(ifh)) + call argreplace(pfc_final,pfc_cmd_len,'%[FMIN]', & + & iftotalmins(ifh)) + + if(verb.ge.2) then + print *,' ' + print *,'!!! Running per-fcst command' + print *,'!!! Unparsed = ',trim(per_fcst_command) + print *,'!!! Parsed = ',trim(pfc_final) + endif + call run_command(trim(pfc_final),pfcret) + if(pfcret/=0 .and. verb.ge.1) then + print *,' ' + print *,'!!! Non-zero exit status from per-fcst command' + print *,'!!! Command = ',trim(pfc_final) + print *,'!!! Exit status = ',pfcret + print *,'!!! Continuing anyway...' + elseif(pfcret==0 .and. verb.ge.2) then + print *,' ' + print *,'!!! Per-fcst command returned success status (0)' + endif + endif + + ifh = ifh + 1 + if (ifh > ifhmax) exit ifhloop + + if (inp%file_seq == 'multi') then + call baclose(lugb,igcret) + call baclose(lugi,iicret) + if ( verb .ge. 3 ) then + print *,'baclose return code for unit ',lugb,' = igcret = ' + & ,igcret + print *,'baclose return code for unit ',lugi,' = iicret = ' + & ,iicret + endif + endif + + enddo ifhloop +c +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) +c + 73 format ('fixpos ',a4,' fhr= ',i4,':',i2.2,' Fix position= ' + & ,f7.2,'E (',f6.2,'W)',2x,f7.2,' Max Wind= ',i3,' kts') + + if (allocated(prstemp)) deallocate (prstemp) + if (allocated(prsindex)) deallocate (prsindex) + if (allocated(iwork)) deallocate(iwork) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(vt850_flag)) deallocate (vt850_flag) + if (allocated(closed_mslp_ctr_flag)) + & deallocate (closed_mslp_ctr_flag) + if (allocated(netcdf_file_time_values)) + & deallocate (netcdf_file_time_values) + if (allocated(nctotalmins)) + & deallocate (nctotalmins) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine argreplace(arg,n,name,val) + ! This subroutine is used to generate the pre-forecast-command + ! It will edit the command (argument "arg") and replace string + ! name with value val. That is how the per-forecast-command + ! has these modifications: + + ! %[FHOUR] -> replace with -> last forecast hour + ! %[FMIN] -> replace with -> last forecast minute + + implicit none + + integer, intent(in) :: n + character(n), intent(inout) :: arg + character(*), intent(in) :: name + integer, intent(in) :: val + + integer found,namelen,i1,i2 + character(n) :: out + + found=index(arg,name) + namelen=len(name) + i1=found-1 ! last char that is before name + i2=found+namelen ! index of last char in name + + if(found==0) return + + out=' ' + + if(found>1 .and. i21) then +! special case: name is at end of string +! hope the value fits... + write(out,'(A,I0)') arg(1:i1),val + elseif(i2 + & ,'... gopen_i_file= ...',a,'...') + + print *,'gopen_g_file= ',gopen_g_file,'....' + print *,'gopen_i_file= ',gopen_i_file,'....' + + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + inquire (unit=lout, opened=output_file_open) + if (output_file_open) then + iooret = 0 + else + fnameo(1:5) = "fort." + write(fnameo(6:7),'(I2)') lout + call baopenw (lout,fnameo,iooret) + endif + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + inquire (file=gopen_g_file, opened=file_open4) + if (file_open4) then + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is OPEN' + else + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is CLOSED' + endif + + inquire (file=gopen_i_file, opened=file_open5) + if (file_open5) then + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is OPEN' + else + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'gettrk baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine open_ncfile (filename,ncid) + +c ABSTRACT: This subroutine opens a netcdf file specified by the +c input file "ncfile" and returns the netcdf file id that will be +c associated with that file. +c +c INPUT: +c ncfile character full-path file netcdf name +c +c OUTPUT: +c ncfile_id integer, netcdf id assigned to the netcdf file + + implicit none + + include "netcdf.inc" + + character*(*), intent(in) :: filename + integer, intent(out) :: ncid + integer :: status + + status = nf_open (filename, NF_NOWRITE, ncid) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine open_ncfile +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine is_it_a_storm (imax,jmax,dx,dy,cparm,ist + & ,defined_pt,parmlon,parmlat + & ,parmval,trkrinfo,stormcheck,isiret) + +c ABSTRACT: This subroutine is called after the center of the storm +c has been fixed. Its purpose is to determine whether or not +c the center that was found is actually a storm, and not just some +c passing trough (this has happened in the case of decaying or weak +c storms). It's called twice -- once to check for a minimum MSLP +c gradient, and once to check for a circulation at 850 mb. The +c subroutine input parameter "cparm" determines which parameter to +c check for. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is to be checked: +c slp = mslp, for a check of mslp gradient +c v850 = tangential winds at 850 mb +c ist integer storm number (internal to the tracker) +c defined_pt Logical; bitmap indicating if valid data at that pt. +c parmlon Longitude of the max/min value for the input parameter +c parmlat Latitude of the max/min value for the input parameter +c parmval Data value at parm's max/min point (used for mslp call) +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c stormcheck Character; set to 'Y' if mslp gradient or 850 mb +c tangential winds check okay. +c isiret Return code for this subroutine. +c + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE tracked_parms; USE atcf; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + real vt,vtavg,vr,parmlat,parmlon,parmval,dist + real pthresh,vthresh,degrees,dx,dy,dell,ri,radinf + real pgradient,xmaxpgrad + character(*) cparm + logical(1) defined_pt(imax,jmax) + character*1 stormcheck + integer isiret,imax,jmax,ist,npts,ilonfix,jlatfix,igvtret + integer ibeg,iend,jbeg,jend,ivt,i,j,iix,jix,bskip,igiret + + isiret = 0 + stormcheck = 'N' + + dell = (dx+dy)/2. + +c First define the radius of influence, which depends on the +c grid spacing of the model data being used. The ceiling statement +c for npts in the first if statement is needed in case the +c resolution of the grib files eventually goes very low, down to +c say a half degree or less, in order to cover enough points in +c the search. + + if (dell < 1.24) then ! GFS, MRF, NAM, NGM, NAVGEM, GDAS, + ! GFDL, NCEP Ensemble & Ensemble + ! Relocation, SREF Ensemble + ri = ritrk_most + if (cparm == 'slp') then + radinf = 300.0 + else + radinf = 225.0 + endif + npts = ceiling(radinf/(dtk*(dx+dy)/2.)) + else if (dell >= 1.24 .and. dell < 2.49) then ! UKMET + ri = ritrk_most + radinf = 275.0 + npts = 2 + else ! ECMWF + ri = ritrk_coarse + radinf = 350.0 + npts = 1 + endif + + pthresh = trkrinfo%mslpthresh ! These are read in in + vthresh = trkrinfo%v850thresh ! subroutine read_nlists.... + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,parmlon,parmlat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij B, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij B, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print*,' ' + print*,'!!! ERROR in is_it_a_storm from call to' + print*,'!!! get_ij_bounds, stopping processing for ' + print*,'!!! storm number ',ist + endif + + isiret = 92 + return + endif + +c If the input cparm is slp, then check to see that the MSLP +c gradient in any direction from the MSLP center is at least +c 1mb / 200km, or 0.005mb/km. This is based on discussions with +c Morris & Bob, who have had good results using a 2mb/200km +c requirement. Since their model has a much finer resolution than +c all of the models we run the tracker on AND a much better +c depiction of the hurricane vortex, we do not use a requirement +c as strict as theirs, and so make the requirement only half as +c strong as theirs. +c +c If the input cparm is v850, then check to see that there is +c a circulation at 850 mb. We will do this by calculating the +c tangential wind of all points within a specified radius of +c the 850 minimum wind center, and seeing if there is a net +c average tangential wind speed of at least 5 m/s. +c +c UPDATE APRIL 2000: I've relaxed the thresholds slightly from +c 0.005 mb/km to 0.003 mb/km, and the wind threshold from +c 5 m/s to 3 m/s. Also, note that a special case for GDAS has +c been hardwired in that is weaker (0.002 mb/km and 2 m/s). +c That weaker GDAS requirement is for Qingfu's relocation stuff. +c +c UPDATE JULY 2001: The relaxed requirement put in place in +c April 2000 for the GDAS relocation has also been put in place +c for the GFS ensemble relocation. + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the loop. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, ilonfix= ',ilonfix + & ,' jlatfix= ',jlatfix + print *,'ibeg jbeg iend jend = ',ibeg,jbeg,iend,jend + print *,'cparm= ',cparm,' parmlon parmlat = ',parmlon,parmlat + print *,'parmval= ',parmval + print *,' ' + endif + + vtavg = 0.0 + ivt = 0 + + xmaxpgrad = -999.0 + + jloop: do jix = jbeg,jend,bskip + iloop: do iix = ibeg,iend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine is_it_a_storm' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + i = iix - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine ' + print *,'!!! is_it_a_storm for a non-global grid.' + print *,'!!! STOPPING....' + print *,'!!! i= ',i,' imax= ',imax + print *,' ' + endif + + stop 97 + endif + endif + + call calcdist(parmlon,parmlat,glon(i),glat(j),dist,degrees) + + if (dist > radinf .or. dist == 0.0) cycle + + if (defined_pt(i,j)) then + + if (cparm == 'slp') then + pgradient = (slp(i,j) - parmval) / dist + if (pgradient > xmaxpgrad) xmaxpgrad = pgradient + + if ( verb .ge. 3 ) then + write (6,93) i,j,glon(i),glat(j),dist,slp(i,j),pgradient + endif + + if (pgradient > pthresh) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, valid pgradient found.' + print '(a23,f8.5)',' pgradient threshold = ',pthresh + print '(a23,f8.5)',' pgradient found = ',pgradient + print *,'mslp center = ',parmlon,parmlat,parmval + print *,'pgrad loc = ',glon(i),glat(j),slp(i,j) + endif + + stormcheck = 'Y' + exit jloop + endif + endif + + if (cparm == 'v850') then + call getvrvt (parmlon,parmlat,glon(i),glat(j) + & ,u(i,j,nlev850),v(i,j,nlev850),vr,vt,igvtret) + if ( verb .ge. 3 ) then + write (6,91) i,j,glon(i),glat(j),u(i,j,nlev850) + & ,v(i,j,nlev850),vr,vt + endif + + vtavg = vtavg + vt + ivt = ivt + 1 + endif + + endif + + enddo iloop + enddo jloop + + 91 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' u= ',f8.4,' v= ',f8.4,' vr= ',f9.5,' vt= ',f9.5) + + 93 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' dist= ',f8.2,' slp= ',f10.2,' pgradient= ',f8.5) + + if (stormcheck /= 'Y' .and. cparm == 'slp') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, valid pgradient NOT FOUND.' + write (6,94) '!!! (Max pgradient less than ',pthresh,' mb/km)' + 94 format (1x,a29,5x,f8.5,a7) + write (6,95) '!!! Max pgradient (mb/km) found = ',xmaxpgrad + 95 format (1x,a34,f8.5) + print *,' ' + endif + + endif + + if (cparm == 'v850') then + + if (ivt > 0) then + vtavg = vtavg / float(ivt) + else + vtavg = 0.0 + endif + + if (parmlat > 0) then + if (vtavg >= vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (>= +',vthresh,' m/s for a NH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed +',vthresh + & ,' m/s (NH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + else + if (vtavg <= -vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (<= -',vthresh,' m/s for a SH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed -',vthresh + & ,' m/s (SH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + endif + + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag,igpret) +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure or phase of a cyclone. Initially, we +c will just have it use the Hart cyclone phase space (CPS) scheme. + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trkrparms; USE grid_bounds + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character wcore_flag*1,okay_to_call_cps_routines*1 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real cps_vals(3) + real dx,dy,paramb,vtl_slope,vtu_slope + integer imax,jmax,igpret,igcpret,ist,ifh,maxstorm + integer igvpret,igcv1ret,igcv2ret + logical(1) valid_pt(imax,jmax) +c + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,611) + write (6,613) + write (6,615) + write (6,*) ' ' + + 611 format(1x,'#-----------------------------------------------#') + 613 format(1x,'# start of routine to determine cyclone phase...#') + 615 format(1x,'#-----------------------------------------------#') + endif + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + if (ifh > 1 .or. (ifh == 1 .and. trkrinfo%type == 'tracker')) + & then + + ! This condition that ifh > 1 is so that we *not* do the cps + ! stuff for fhour=0 if it's a tcgen or midlat case, since we + ! don't know the model storm motion direction for the + ! analysis. For a regular case where type = 'tracker', we + ! have the observed storm's heading direction from tc vitals, + ! so we can use that (even though the model's storm direction + ! may differ slightly from the observed storm). This current + ! if statement and the ones below carefully check for these + ! various instances. + + okay_to_call_cps_routines = 'n' + + if (ifh > 1) then + if (fixlon(ist,ifh-1) > -990.0 .and. + & fixlat(ist,ifh-1) > -990.0) then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level ' + print *,' >< since the fixlon and fixlat at the ' + print *,' >< previous lead time are undefined.' + print *,' >< This is likely the first found position' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + endif + endif + elseif (ifh == 1 .and. trkrinfo%type == 'tracker') then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level.' + print *,' >< The likely reason is that ifh=0 and' + print *,' >< this is a genesis case, so we do not ' + print *,' >< know the storm motion direction.' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + print *,' >< trkrinfo%type ',trkrinfo%type + endif + endif + + if (okay_to_call_cps_routines == 'y') then + + ! Similarly, these next two conditions (previous lat and + ! previous lon > -999) are in there in case we're doing a + ! tcgen or midlat case and this is the *first* time level + ! within a forecast that the storm has been detected (again, + ! we don't yet know the storm heading). + + call get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'lower',vtl_slope + & ,maxstorm,igcv1ret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'upper',vtu_slope + & ,maxstorm,igcv2ret) + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh) + & ,paramb,vtl_slope,vtu_slope + endif + + cps_vals(1) = paramb + cps_vals(2) = vtl_slope + cps_vals(3) = vtu_slope + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diagnostics were requested but will NOT' + print *,' >< be performed for this time level since we ' + print *,' >< are either at the first time level for a ' + print *,' >< genesis case (type = midlat or tcgen), or' + print *,' >< we are at any time level in which for some' + print *,' >< reason the fixlon and fixlat at the' + print *,' >< previous time level are not defined.' + print *,' >< ifh= ',ifh + endif + + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diags were requested but will NOT be' + print *,' >< performed for this time level since we are at' + print *,' >< time level 1 for a genesis case ' + print *,' >< (type = midlat or tcgen) and we cannot' + print *,' >< diagnose the model direction of storm' + print *,' >< movement. ifh= ',ifh + endif + + endif + + endif + + 73 format ('cps_stats: ',a4,' lead time= ',i3,':',i2,' paramb= ' + & ,f8.2,' vtl= ',f9.2,' vtu= ',f9.2) + + + if (phasescheme == 'vtt' .or. phasescheme == 'both') then + call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + + 631 format(1x,'#-------------------------------------------------#') + 633 format(1x,'# End of routine to determine cyclone phase... #') + 635 format(1x,'#-------------------------------------------------#') + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines "Parameter B", which determines +c the degree of thermal symmetry between the "left" and "right" +c hemispheres of a storm, in the layer between 900 and 600 mb. +c We evaluate only those points that are within 500 km of the +c storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zthicksum(2) + real rlonc,rlatc,rlonb,rlatb,xdist,degrees,d,cosarg + real st_heading,st_heading_rad,ricps,dx,dy + real pt_dir,pt_dir_rad,zthick,hemval,paramb + real zthick_right_mean,zthick_left_mean + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer left_ct,right_ct,hemis,icount,maxstorm,ip + logical(1) valid_pt(imax,jmax) +c + ricps = 500.0 + +c ----------------------------------------------------------------- +c First, determine the angle that the storm took getting from the +c last position to the current one. If this is for ifh=1 for a +c regular type=tracker case, we will just use the storm direction +c as read from the tcvitals card. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + if (d == 0.0) then + + ! Storm is stationary... + st_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + print '(a43,f9.3)' + & ,' In get_cps_paramb, model storm heading = ' + & ,st_heading + print *,' ' + endif + +c ----------------------------------------------------------------- +c Now call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_paramb from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + igcpret = 92 + return + endif + +c ----------------------------------------------------------------- +c Now loop through all of the points of the subdomain. If the +c point is further than 500 km from the storm center, discard it. +c Otherwise, evaluate the angle from the storm center to this point +c to determine the hemisphere of the point, that is, if the point +c is to the left or the right of the storm track. +c ----------------------------------------------------------------- + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the + ! loop for the evaluation of parameter B. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + left_ct = 0 + right_ct = 0 + zthicksum = 0 + icount = 0 + +c print *,'CPS CORE: ibeg= ',ibeg,' iend= ',iend +c print *,'CPS CORE: jbeg= ',jbeg,' jend= ',jend + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + icount = icount + 1 + +c print *,'CPS CORE: ist= ',ist,' ifh= ',ifh,' j= ',j,' i= ',i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_paramb, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Parameter B will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_paramb' + print *,'!!! for a non-global grid.' + print *,'!!! Parameter B will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_PARAMB....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon= ',glon(ip),' glat= ',glat(j) + print *,'!!! Parameter B will not be computed.' + print *,'!!! EXITING GET_CPS_PARAMB....' + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + + !---------------------------------------------------------- + ! Calculate angle from storm center to point, in a 0-360 + ! framework, clockwise positive. + !---------------------------------------------------------- + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-fixlon(ist,ifh)) * dtr + rlatb = fixlat(ist,ifh) * dtr + d = degrees * dtr + + if (d > 0.) then + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_dir_rad = acos(cosarg) + else + pt_dir_rad = 2*pi - acos(cosarg) + endif + else + pt_dir_rad = 0.0 + endif + + pt_dir = pt_dir_rad / dtr + + !------------------------------------------------------------ + ! Based on the angle that the point is from the storm center, + ! determine if the point is to the left or the right of the + ! storm track. + !------------------------------------------------------------ + + if (st_heading >= 180.0) then + if ((st_heading - pt_dir) > 0.0 .and. + & (st_heading - pt_dir) <= 180) then + hemis = 2 + left_ct = left_ct + 1 + else + hemis = 1 + right_ct = right_ct + 1 + endif + else + if ((pt_dir - st_heading) > 0.0 .and. + & (pt_dir - st_heading) <= 180) then + hemis = 1 + right_ct = right_ct + 1 + else + hemis = 2 + left_ct = left_ct + 1 + endif + endif + + !------------------------------------------------------------ + ! Calculate the 600-900 mb thickness at this point and add + ! the thickness value to the array for the correct "storm + ! hemisphere". + !------------------------------------------------------------ + + zthick = cpshgt(ip,j,7) - cpshgt(ip,j,1) + zthicksum(hemis) = zthicksum(hemis) + zthick + + if ( verb .ge. 3 ) then + write (6,51) rlonb/dtr,rlatb/dtr,rlonc/dtr,rlatc/dtr + & ,st_heading,pt_dir,hemis,zthick + endif + + enddo iloop + enddo jloop + + 51 format (1x,'stlon stlat = ',2(f6.2,2x),' ptlon ptlat = ' + & ,2(f6.2,2x),' sthead= ',f6.2,' ptdir= ',f6.2,' hemis= ' + & ,i1,' zthick= ',f7.2) + +c ------------------------------------------------------------------ +c Now calculate parameter B. The hemval parameter = +1 for storms +c in the Northern Hemisphere and -1 for Southern Hemisphere storms. +c ------------------------------------------------------------------ + + zthick_right_mean = zthicksum(1) / float(right_ct) + zthick_left_mean = zthicksum(2) / float(left_ct) + + if (fixlat(ist,ifh) < 0.0) then + hemval = -1.0 + else + hemval = 1.0 + endif + + paramb = hemval * (zthick_right_mean - zthick_left_mean) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' right_ct= ',right_ct,' left_ct= ',left_ct + print *,' zthicksum(1)= ',zthicksum(1) + print *,' zthicksum(2)= ',zthicksum(2) + print *,' zthick_right_mean= ',zthick_right_mean + print *,' zthick_left_mean= ',zthick_left_mean + print *,' hemval= ',hemval + print *,' END of get_cps_paramb, paramb= ',paramb + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,clayer,vth_slope,maxstorm,igcvret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines the thermal wind profile for +c either the lower troposphere (i.e., between 600 and 900 mb) or the +c upper troposphere (i.e., between 300 and 600 mb). We evaluate +c only those points that are within 500 km of the storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character clayer*5 + real tmp1,tmp2,tmp3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zmax(7),zmin(7),zdiff(7),xlolevs(7),xhilevs(7),plev(7) + real dlnp(7),dzdlnp(7),dz(7),lnp(7) + real vth_slope,xdist,degrees,d,cosarg + real ricps,dx,dy,R2 + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j,k,kix + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igcvret,igiret + integer kbeg,kend,maxstorm,ip + logical(1) valid_pt(imax,jmax) + + data xlolevs /900.,850.,800.,750.,700.,650.,600./ + data xhilevs /600.,550.,500.,450.,400.,350.,300./ +c data xlolevs /90000.,85000.,80000.,75000.,70000.,65000.,60000./ +c data xhilevs /60000.,55000.,50000.,45000.,40000.,35000.,30000./ +c + ricps = 500.0 + plev = 0.0 + + if (clayer == 'lower') then + kbeg = 1 + kend = 7 + plev = xlolevs + else + kbeg = 7 + kend = 13 + plev = xhilevs + endif + +c ----------------------------------------------------------------- +c First, call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_vtl from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + igcvret = 92 + return + endif + +c ------------------------------------------------------------------ +c Now loop through all of the points of the subdomain at each level. +c If a point is further than 500 km from the storm center, discard +c it. Otherwise, evaluate the gp height at the point to determine +c if it is a max or a min for the given level. Store the max and +c min height at each level in an array. +c ------------------------------------------------------------------ + +c ! We will want to speed things up for finer resolution grids. +c ! We can do this by skipping some of the points in the +c ! loop for the evaluation of parameter B. +c +c if ((dx+dy)/2. > 0.20) then +c bskip = 1 +c else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then +c bskip = 2 +c else if ((dx+dy)/2. <= 0.10) then +c bskip = 3 +c endif + + bskip = 1 ! Don't do any skipping for now.... + + zmax = -9999999.0 + zmin = 9999999.0 + zdiff = 0.0 + lnp = 0.0 + + levloop: do k = kbeg,kend + + if (kbeg == 7) then + ! processing upper layers (600-300 mb) + kix = k - 6 + else + ! processing lower layers (900-600 mb) + kix = k + endif + + lnp(kix) = log(plev(kix)) + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_vth, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_vth' + print *,'!!! for a non-global grid.' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_VTH....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j,' k= ',k + & ,' clayer= ',clayer + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon(ip)= ',glon(ip),' glat= ',glat(j) + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! EXITING GET_CPS_VTH....' + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + + tmp1 = zmax(kix) + tmp2 = cpshgt(ip,j,k) + tmp3 = zmin(kix) + + zmax(kix) = max(tmp1,tmp2) + zmin(kix) = min(tmp3,tmp2) + +c zmax(kix) = max(zmax(kix),cpshgt(ip,j,k)) +c zmin(kix) = min(zmin(kix),cpshgt(ip,j,k)) + + enddo iloop + enddo jloop + + zdiff(kix) = zmax(kix) - zmin(kix) + + enddo levloop + +c ------------------------------------------------------------------ +c Now calculate the vertical derivative of the gp height, that is, +c d(dz)/d(ln(p)). Here, zdiff is the gp height perturbation at a +c given level, calculated in the loop above; dz is the vertical +c change in that perturbation from one level to the next. +c ------------------------------------------------------------------ + + dz = 0.0 + dlnp = 0.0 + dzdlnp = 0.0 + + do k = 2,7 + dz(k) = zdiff(k) - zdiff(k-1) + dlnp(k) = log(plev(k)) - log(plev(k-1)) + dzdlnp(k) = dz(k) / dlnp(k) + enddo + +c ------------------------------------------------------------------ +c Now call a correlation routine to get the slope of a regression +c line. The independent variable that we input is dlnp, the change +c in log of pressure with height. The dependent variable is +c dzdlnp, the vertical change in the height perturbation with +c respect to the change in pressure. The slope that is returned +c defines whether we've got a cold core or warm core system. +c See Hart (MWR, April 2003, Vol 131, pp. 585-616) for more +c details, specifically his Fig. 3 and the discussion surrounding. +c Note that in the call to calccorr, we are sending only 6 of the +c 7 elements of the dlnp and dzdlnp arrays, beginning with the +c 2nd element of each. That's because the first array value for +c each of those arrays is empty, since in the loop just above, we +c start with kbeg+1, not kbeg. +c ------------------------------------------------------------------ + + call calccorr(lnp(2),zdiff(2),6,R2,vth_slope) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ In get_cps_vth, values for vth follow for ' + & ,'lead time= ',ifhours(ifh),':',ifclockmins(ifh),' ' + & ,storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' ... clayer = ',clayer + print *,' ' + endif + + do k = kbeg,kend + + if (kbeg == 7) then + kix = k - 6 + else + kix = k + endif + + if ( verb .ge. 3 ) then + print *,' ' + write (6,31) k,plev(kix),zmax(kix),zmin(kix),zdiff(kix) + if (kix > 1) then + write (6,32) plev(kix),log(plev(kix)) + & ,plev(kix-1),log(plev(kix-1)) + write (6,33) dz(kix),dlnp(kix),dzdlnp(kix) + else + write (6,34) + endif + endif + + enddo + + 31 format (1x,' +++ k= ',i2,' press= ',f8.1,' zmax= ',f7.2 + & ,' zmin= ',f7.2,' zdiff= ',f7.2) + 32 format (1x,' ln(',f7.1,')= ',f9.6,' ln(',f7.1,')= ',f9.6) + 33 format (1x,' dz= ',f10.2,' dlnp= ',f13.6,' dzdlnp= ',f12.3) + 34 format (1x,' --- First level... no derivatives done...') +c + return + end +c +C---------------------------------------------------- +C +C---------------------------------------------------- + subroutine calccorr(xdat,ydat,numpts,R2,slope) +c +c This subroutine is the main driver for a series of +c other subroutines below this that will calculate the +c correlation between two input arrays, xdat and ydat. +c +c INPUT: +c xdat array of x (independent) data points +c ydat array of y (dependent) data points +c numpts number of elements in each of xdat and ydat +c +c OUTPUT: +c R2 R-squared, the coefficient of determination +c slope Slope of regression line +c +c xdiff array of points for xdat - xmean +c ydiff array of points for ydat - ymean +c yestim array of regression-estimated points +c yresid array of residuals (ydat(i) - yestim(i)) + + USE verbose_output + + implicit none + + real xdat(numpts),ydat(numpts) + real xdiff(numpts),ydiff(numpts) + real yestim(numpts),yresid(numpts) + real xmean,ymean,slope,yint,R2 + integer numpts,i + +c + call getmean(xdat,numpts,xmean) + call getmean(ydat,numpts,ymean) +c + call getdiff(xdat,numpts,xmean,xdiff) + call getdiff(ydat,numpts,ymean,ydiff) +c + call getslope(xdiff,ydiff,numpts,slope) + yint = ymean - slope * xmean +c + call getyestim(xdat,slope,yint,numpts,yestim) + call getresid(ydat,yestim,numpts,yresid) +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * CPS Thermal wind regression details * ' + print *,' *--------------------------------------------------* ' + endif + + call getcorr(yresid,ydiff,numpts,R2) + + if ( verb .ge. 3 ) then + print *,' i ydat xdat ydiff xdiff e' + & ,' e2 ydiff2' + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + do i = 1,numpts + write(6,'(2x,i3,2x,f7.2,2x,f7.4,2x,f7.2,2x,f7.4,3(2x,f7.2))') + & i,ydat(i),xdat(i),ydiff(i) + & ,xdiff(i),yresid(i),yresid(i)*yresid(i) + & ,ydiff(i)*ydiff(i) + enddo + + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + print *,' ' + write (6,'(1x,a13,f9.3,3x,a5,f7.2)') ' means: y: ',ymean + & ,' x: ',xmean + + write (6,*) ' ' + write (6,30) 'slope= ',slope,' y-intercept = ',yint + 30 format (2x,a7,f10.3,a23,f10.3) + if (slope .gt. 0.0) then + write(6,40) 'Regression equation: Y = ',yint,' + ',slope + else + write(6,40) 'Regression equation: Y = ',yint,' - ' + & ,abs(slope) + endif + 40 format (2x,a27,f8.2,a3,f8.2,'X') +c + print *,' ' + write (6,'(1x,a17,f7.4,5x,a7,f7.4)') ' R2(r_squared) = ',R2 + & ,' r = ',sqrt(R2) + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * End of regression details * ' + print *,' *--------------------------------------------------* ' + endif + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getmean(xarr,inum,zmean) +c +c This subroutine is part of the correlation calculation, +c and it simply returns the mean of the input array, xarr. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c +c OUTPUT: +c zmean mean of data values in xarr + + implicit none + + real xarr(inum) + real xsum,zmean + integer i,inum +c + xsum = 0.0 + do i = 1,inum + xsum = xsum + xarr(i) + enddo +c + zmean = xsum / float(MAX(inum,1)) +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getdiff(xarr,inum,zmean,zdiff) +c +c This subroutine is part of the correlation calculation, +c and it returns in the array zdiff the difference values +c between each member of the input array xarr and the +c mean value, zmean. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c zmean mean of input array (xarr) +c +c OUTPUT: +c zdiff array containing xarr(i) - zmean + + implicit none + + real xarr(inum),zdiff(inum) + real zmean + integer i,inum +c + do i = 1,inum + zdiff(i) = xarr(i) - zmean + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + + subroutine getslope(xarr,yarr,inum,slope) +c +c This subroutine is part of the correlation calculation, +c and it returns the slope of the regression line. +c +c INPUT: +c xarr input array of xdiffs (x - xmean) +c yarr input array of ydiffs (y - ymean) +c inum number of points in x & y arrays +c +c OUTPUT: +c slope slope of regression line + + real xarr(inum),yarr(inum) + real slope,sumxy,sumx2 + integer i,inum + +c First sum up the xarr*yarr products.... + + sumxy = 0.0 + do i = 1,inum + sumxy = sumxy + xarr(i) * yarr(i) + enddo + +c Now sum up the x-squared terms.... + + sumx2 = 0.0 + do i = 1,inum + sumx2 = sumx2 + xarr(i) * xarr(i) + enddo + +c Now get the slope.... + + slope = sumxy / sumx2 + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getyestim(xarr,slope,yint,inum,yestim) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the predicted y-values using the +c regression equation that has been calculated. +c +c INPUT: +c xarr array of x data points +c slope slope of the calculated regression line +c yint y-intercept of the calculated regression line +c inum number of input points +c +c OUTPUT: +c yestim array of y pts estimated from regression eqn. + + implicit none + + real xarr(inum),yestim(inum) + real slope,yint + integer i,inum +c + do i = 1,inum + yestim(i) = yint + xarr(i) * slope + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getresid(yarr,yestim,inum,yresid) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the residual values between the +c input y data points and the y-estim predicted y values. +c +c INPUT: +c yarr array of y data points +c yestim array of y pts estimated from regression eqn. +c inum number of input points +c +c OUTPUT: +c yresid array of residuals (ydat(i) - yestim(i)) + + implicit none + + real yarr(inum),yestim(inum),yresid(inum) + integer i,inum +c + do i = 1,inum + yresid(i) = yarr(i) - yestim(i) + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getcorr(yresid,ydiff,inum,R2) +c +c This subroutine is part of the correlation calculation, +c and it does the actual correlation calculation. +c +c INPUT: +c yresid array of residuals (ydat(i) - yestim(i)) +c ydiff array of points for ydat - ymean +c inum number of points in the arrays +c +c OUTPUT: +c R2 R-squared, the coefficient of determination + + USE verbose_output + + implicit none + + real yresid(inum),ydiff(inum) + real R2,sumyresid,sumydiff + integer i,inum +c + sumyresid = 0.0 + sumydiff = 0.0 + + do i = 1,inum + sumyresid = sumyresid + yresid(i) * yresid(i) + sumydiff = sumydiff + ydiff(i) * ydiff(i) + enddo + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,30) 'Sum of y-residuals squared (e2) = ',sumyresid + write (6,30) 'Sum of y-diffs squared (ydiff2) = ',sumydiff + write (6,*) ' ' + 30 format (1x,a35,f15.2) + endif + +c if (sumydiff == 0.0) then +c R2=1.0 +c else +c R2 = 1 - sumyresid / sumydiff +c endif +c PENG 05/14/2018 Bug-fixed for R2 calculation with FENS job crashed. + if (sumyresid .lt. sumydiff) then + if (sumydiff .le. 0.000001) then + R2 = 1.0 + else + R2 = 1 - sumyresid / sumydiff + endif + else + R2=0.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. Here, we are only looking +c at the mid-to-upper tropospheric warm anomaly at the center of +c the storm. The temperature data that we are searching through in +c the tmean array should be the 300-500 mb mean temperature data. +c The criteria in this algorithm are based loosely on Vitart's +c criteria for warm core checking, but the nuts & bolts of the +c subroutine use algorithms from this tracker, including the barnes +c analysis. First, we locate the warm core with the find_maxmin +c routine. Then we use the check_closed_contour routine to see if +c there is a closed temperature contour surrounding the warm core. +c +c INPUT: +c inp +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c inp contains input date and model number information +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c ist integer storm number (internal to the tracker) +c ifh integer index for lead time +c trkrinfo derived type containing grid info on user boundaries +c fixlon array containing found fix longitudes +c fixlat array containing found fix latitudes +c valid_pt Logical; bitmap indicating if valid data at that pt. +c maxstorm maximum # of storms to be handled +c +c OUTPUT: +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c igvpret Return code for this subroutine. +c +c LOCAL: +c wcore_mean_val barnes-averaged value of the temperature at the +c location where the tracker found the warm core. +c wcore_point_max max temperature found at a gridpoint near the +c location where the tracker found the warm core using +c barnes analysis. + + USE set_max_parms; USE grid_bounds; USE trkrparms; USE contours + USE tracked_parms; USE gen_vitals; USE def_vitals; USE inparms + USE phase + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo,wcore_trkrinfo + type (cint_stuff) wcore_contour_info + type (datecard) inp + + character*1 get_last_contour_flag,wcore_flag + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dx,dy,wcore_mean_val,wcore_mean_lon,wcore_mean_lat + real wcore_point_max,tlastcont,rlastcont,tlastout,rlastout + integer imax,jmax,igvpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer icount,maxstorm,ip,ifmret,ifilret,ifix,jfix,icccret + integer num_check_conts + logical(1) valid_pt(imax,jmax),compflag,wcore_mask(imax,jmax) + logical(1) output_file_open +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of get_vtt_phase *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for warm core at hour ',i4,':',i2.2) + write (6,103) wcore_depth + 103 format (1x,'* Warm core depth threshold (wcore_depth) = ',f7.2) + print *,'*-------------------------------------------------*' + endif + +c ------------------------------------------------------------ + wcore_mask = .false. + wcore_mean_lon = -999.0 + wcore_mean_lat = -999.0 + wcore_trkrinfo = trkrinfo ! set equal to values from trkrinfo... + wcore_trkrinfo%contint = wcore_depth ! ...except use the warm + ! core contour interval specified by + ! the user in the extrkr.sh script. + +c ------------------------------------------------------------ +c First, call find_maxmin to locate the warm core + + call find_maxmin (imax,jmax,dx,dy,'tmp' + & ,tmean,'max',ist,fixlon(ist,ifh),fixlat(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,compflag + & ,wcore_mean_lon,wcore_mean_lat,wcore_mean_val + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + + if (verb .ge. 3) then + print *,' ' + print *,'After call to find_maxmin for wcore, ifmret= ',ifmret + print *,' wcore_mean_val= ',wcore_mean_val + endif + +c ------------------------------------------------------------ +c Once find_maxmin returns a value and a location for the +c barnes-averaged value of a warm core, then make a call to +c fix_latlon_to_ij to (1) get the actual gridpoint value of the +c temperature (the value stored in wcore_mean_val is an +c area-averaged value coming from the barnes analysis), and +c (2) to get the (i,j) indeces for this gridpoint to be used in +c the call to check_closed_contour below. + + if (wcore_mean_lat > -99.0 .and. wcore_mean_lon > -990.0) then + call fix_latlon_to_ij (imax,jmax,dx,dy,tmean,'max' + & ,valid_pt,wcore_mean_lon,wcore_mean_lat + & ,wcore_mean_val,ifix,jfix,wcore_point_max,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Warm core stats: ' + write (6,105) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_mean_lon,360.-wcore_mean_lon + & ,wcore_mean_lat,wcore_mean_val + write (6,106) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,ifix,jfix,wcore_point_max + endif + + else + ! Search went out of regional grid bounds.... + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN get_vtt_phase. The call to ' + print *,'!!! fix_latlon_to_ij returned a non-zero return ' + print *,'!!! code, which means that the search for the fix' + print *,'!!! i and j went out of bounds for a regional ' + print *,'!!! grid. This should have been caught in a ' + print *,'!!! previous call to find_maxmin for one of the ' + print *,'!!! various fix parms. In any event, we will not' + print *,'!!! search for a warm core for this storm and ' + print *,'!!! lead time.' + print *,' ' + write (6,115) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,'U',-999.99,-9999.99 + endif + + igvpret = 95 + wcore_flag = 'u' + return + endif + endif + + 105 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' mean_lon: ',f7.2,'E' + & ,1x,'(',f7.2,'W)',2x,'mean_lat: ',f7.2,2x + & ,'wcore_mean_val(K): ',f12.3) + 106 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' ifix: ',i5,2x + & ,' jfix: ',i5,2x,'wcore_point_max(K): ',f12.3) + + +c ------------------------------------------------------------ +c The Vitart scheme specifies that the temperature must decrease +c by at least 1.0C in all directions from the warm core center +c within a distance of 8 deg. A rigorous check of this criterion +c is performed here by utilizing the check_closed_contour routine. +c If we have a closed contour in the temperature field +c surrounding the warm core (using a 1 deg K interval), that +c criterion is satisfied. For diagnostic purposes, we set the +c value of num_check_conts to 999 in order to keep searching for +c all contours surrounding the warm core, and this allows us to +c get an idea of the "depth" or magnitude of the warm core when +c the tlastcont and rlastcont values are returned. + + wcore_contour_info%numcont = maxconts + num_check_conts = 999 + + get_last_contour_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,tmean + & ,valid_pt,wcore_mask,wcore_flag,'max',wcore_trkrinfo + & ,num_check_conts,wcore_contour_info,get_last_contour_flag + & ,tlastcont,rlastcont,icccret) + + if (wcore_flag == 'y') then + tlastout = tlastcont + rlastout = rlastcont/0.539638 + else + tlastout = -999.0 + rlastout = -9999.0 + endif + + if ( verb .ge. 3 ) then + write (6,115) storm(ist)%tcv_storm_id,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_flag,tlastout,rlastout + + 115 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2 + & ,' wcore_flag= ',a1,2x,' Temp of last contour(K) = ' + & ,f7.2,2x,'Radius of last contour(km) = ',f8.2) + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_sfc_center (xmeanlon,xmeanlat,clon + & ,clat,ist,ifh,calcparm,xsfclon,xsfclat + & ,maxstorm,igscret) +c +c ABSTRACT: This subroutine computes a modified lat/lon fix position +c to use as the input center position for the subroutines that +c follow which calculate surface-wind related values. The reason +c for this is that since we are concerned with the positioning of +c low-level wind features (e.g., rmax), we want the center position +c to be based solely on low-level features. We'll use mslp and the +c min in the sfc wind speed. If a center fix was unable to be made +c at this forecast hour for mslp and low-level winds, then we will +c stick with just using the mean position we got using all the other +c parameters. +c +c INPUT: +c xmeanlon The mean center longitude computed from all the various +c parameter fixes found in array clon +c xmeanlat The mean center latitude computed from all the various +c parameter fixes found in array clat +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Index for storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c (if a parameter fix could not be made at this forecast +c hour, then calcparm is set to false for this time for +c that parameter). +c maxstorm Maximum number of storms that can be tracked +c +c OUTPUT: +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c igscret Return code from this subroutine + + USE set_max_parms + USE verbose_output + + implicit none + + integer ist,ifh,ipct,igscret,maxstorm + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmeanlon,xmeanlat + real xsfclon,xsfclat,xlonsum,xlatsum + logical(1) calcparm(maxtp,maxstorm) + + ipct = 0 + xlonsum = 0.0 + xlatsum = 0.0 + + ! Do NOT include MSLP for the surface center at this time. +c if (calcparm(9,ist)) then +c ipct = ipct + 1 +c xlonsum = xlonsum + clon(ist,ifh,9) +c xlatsum = xlatsum + clat(ist,ifh,9) +c endif + + if (calcparm(10,ist)) then +c ! NOTE: Put double weighting on surface wind center if +c ! the tracker was able to find a center for it.... +c ipct = ipct + 2 +c xlonsum = xlonsum + 2.*clon(ist,ifh,10) +c xlatsum = xlatsum + 2.*clat(ist,ifh,10) + ! Just use single weighting for the sfc wcirc fix + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,10) + xlatsum = xlatsum + clat(ist,ifh,10) + endif + + if (calcparm(11,ist)) then + ! This is for the sfc vorticity center.... + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,11) + xlatsum = xlatsum + clat(ist,ifh,11) + endif + + if (ipct > 0) then + xsfclon = xlonsum / float(ipct) + xsfclat = xlatsum / float(ipct) + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In get_fract_wind_cov, CANNOT get modified fix ' + print *,'!!! position because the parameter fixes for mslp' + print *,'!!! and the sfc winds could not be obtained at this' + print *,'!!! forecast hour. ist= ',ist,' ifh= ',ifh + print *,'!!! We will use the fixlon and fixlat values for' + print *,'!!! this forecast hour.' + endif + + xsfclon = xmeanlon + xsfclat = xmeanlat + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ In get_sfc_center, modified fix (mslp + sfc_winds)' + print *,'+++ position follows: ' + print *,'+++ ' + print *,'+++ mslp: lon: ',clon(ist,ifh,9),' lat: ' + & ,clat(ist,ifh,9) + print *,'+++ sfc_winds: lon: ',clon(ist,ifh,10),' lat: ' + & ,clat(ist,ifh,10) + print *,'+++ sfc_vorticity: lon: ',clon(ist,ifh,11),' lat: ' + & ,clat(ist,ifh,11) + print *,'+++ multi-parm mean: lon: ',xmeanlon,' lat: ' + & ,xmeanlat + print *,'+++ sfc-only mean: lon: ',xsfclon,' lat: ',xsfclat + endif + + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,er_wind,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm + & ,trkrinfo,igwsret) +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure of the low level winds of a cyclone. +c The algorithm will search out at specified distances from the +c storm center along arcs in each quadrant of the storm, +c evaluating the winds every 15 degrees along the arc. In each +c arc, start 7.5 degrees in, then make stops at 22.5, 37.5, +c 52.5, 67.5, and 82.5 degrees. At each of those points, we +c will bilinearly interpolate the winds to the points along those +c arcs. Then we compute a quadrant average of the wing magnitude, +c as well as the mean Vt and Vr values. This will be done +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 earth-relative +c quadrants: NE, SE, SW and NW. For the storm-relative estimates, +c these mean values of the wind will be computed for the same +c relative quadrants (front-right, back-right, back-left, front- +c left, but with respect (positive clockwise) to the +c direction of storm motion. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated +c er_wind: Quadrant winds in earth-relative framework +c sr_wind: Quadrant winds in storm-relative framework +c er_vr: Quadrant radial winds in earth-relative framework +c sr_vr: Quadrant radial winds in storm-relative framework +c er_vt: Quadrant tangential winds in earth-relative framework +c sr_vt: Quadrant tangential winds in storm-relative framework + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,num_qtr_azim=6 + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igvtret,ipct,maxstorm,iazim,azimuth_ct + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,xsfclon,xsfclat,wmag,wmag_sum + real vr,vt,vr_sum,vt_sum + logical(1) valid_pt(imax,jmax) +c + data rdist/10.,25.,50.,75.,100.,125.,150.,200.,250.,300.,350. + & ,400.,450.,500./ + + igwsret = 0 + + er_wind = 0.0 + sr_wind = 0.0 + er_vr = 0.0 + er_vt = 0.0 + sr_vr = 0.0 + sr_vt = 0.0 + +c ----------------------------------------------------------------- +c Now determine the angle that the storm took getting from the +c last position to the current one. If this is the initial time, +c use the observed direction of motion from the TC Vitals. This +c may not match up with the model storm's initial direction of +c motion, but it is all we have available to us in order to get +c a heading estimate for the initial time. This storm heading +c information will be used for the storm-relative profiles. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_wind_structure, fhr= ',fhreal(ifh) + & ,' ',storm(ist)%tcv_storm_id + & ,' ',storm(ist)%tcv_storm_name + print '(a25,a23,f9.3)',' In get_wind_structure, ' + & ,' model storm heading = ',st_heading + print *,' ' + endif + + endif + +c ----------------------------------------------------------------- +c Get the profiles for the earth-relative coordinate system. +c Start with NE, then SE, SW, and NW. First go through +c radiusloop, which goes from one radial distance to the next, +c then do the quadloop, which goes through each quadrant, and +c then within each quadrant, the qtr_azimloop goes through for +c six points along an arc, spaced 15 degrees apart, starting at +c 7.5 degrees clockwise from the north. +c ----------------------------------------------------------------- + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *****************************************************' + print *,' Wind Structure: distbear bilin interp starts here.' + print *,' *****************************************************' + print *,' ' + endif + + radiusloop1: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- ER structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop1: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + ! In each quadrant, run through six points along an + ! arc and evaluate the winds. + + qtr_azimloop1: do iazim = 1,num_qtr_azim + + bear = ((iquad-1) * 90.) + ((iazim-1) * 15.) + 7.5 + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' earth-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,' ' + print '(5(a10,f7.2))',' sfclat= ',xsfclat + & ,' sfclon= ',xsfclon + & ,' rdist= ',rdist(idist),' targlat= ',targlat + & ,' targlon= ',targlon + print '(19x,a8,f7.2,35x,a9,f7.2)','sfclon= ',360.-xsfclon + & ,'targlon= ',360.-targlon + endif + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop1 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + er_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + er_vr(iquad,idist) = vr_sum / float(azimuth_ct) + er_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + er_wind(iquad,idist) = -999.0 + er_vr(iquad,idist) = -999.0 + er_vt(iquad,idist) = -999.0 + endif + + enddo quadloop1 + + enddo radiusloop1 + +c ----------------------------------------------------------------- +c Get the profiles for the storm-relative coordinate system. +c Start with the front-right quadrant and go clockwise through +c back-right, back-left and front-left. +c ----------------------------------------------------------------- + + radiusloop2: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- SR structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop2: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + qtr_azimloop2: do iazim = 1,num_qtr_azim + +c temp_bear = st_heading + ((iquad-1) * 90.) + 45. + + temp_bear = st_heading + ((iquad-1) * 90.) + & + ((iazim-1) * 15.) + 7.5 + bear = mod(temp_bear,360.) + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' storm-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop2 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + sr_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + sr_vr(iquad,idist) = vr_sum / float(azimuth_ct) + sr_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + sr_wind(iquad,idist) = -999.0 + sr_vr(iquad,idist) = -999.0 + sr_vt(iquad,idist) = -999.0 + endif + + enddo quadloop2 + + enddo radiusloop2 +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,calcparm,wfract_cov,pdf_ct_bin,pdf_ct_tot,maxstorm + & ,trkrinfo,igfwret) +c +c ABSTRACT: This subroutine determines the fractional areal coverage +c of winds exceeding various thresholds within specified arcs +c (e.g., 200 km, 400 km, etc) in each quadrant of a storm. The bins +c that are used go as follows: (1) 0-100; (2) 0-200; (3) 0-300; +c (4) 0-400; (5) 0-500. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real wfract_cov(numquad+1,numbin,numthresh) + real area_total_quad_bin(numquad,numbin) + real area_exceed_quad_bin(numquad,numbin,numthresh) + real xintlon,xintlat + real :: windthresh(numthresh) = (/17.5,25.74,32.94/) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,conv_ms_knots,vmagkts + real rads,ri,dell,vmag,xarea,grdintincr,xsfclon,xsfclat + real sum_exceed_area(numbin,numthresh) + real sum_total_area(numbin,numthresh) + integer pdf_ct_bin(16) + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igfwret,ipct,i,j,numinterp,ixoa,ixaa,iq,ib,it,ii + integer jlatfix,ilonfix,npts,ibeg,iend,jbeg,jend,ngridint,ni,nj + integer itret,igiret,idistbin,ipdfbin,pdf_ct_tot,maxstorm + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) + character got_pdf*6 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*5 :: cbin(5) = + & (/'0-100','0-200','0-300','0-400','0-500'/) + character*2 :: cthresh(3) = (/'34','50','64'/) +c + igfwret = 0 + conv_ms_knots = 1.9427 + rads = 500.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + + wfract_cov = 0.0 + area_total_quad_bin = 0.0 + area_exceed_quad_bin = 0.0 + sum_exceed_area = 0.0 + sum_total_area = 0.0 + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_fract_wind_cov from call to ' + print *,'!!! get_ij_bounds, stopping processing for storm' + print *,'!!! number ',ist + endif + + igfwret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_fract_wind_cov calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igfwret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + +c When evaluating the winds at a gridpoint, keep in mind that each +c gridpoint represents area around it. There are 2 special cases +c we need to watch out for. The first is for cases in which the +c area of a gridpoint straddles across a distance threshold, so +c that some of the gridpoint's area is in the "<200" bin, while +c some is in the "<100" bin. The other is for the case in which +c the area of a gridpoint straddles between 2 adjacent quadrants +c (e.g., a gridpoint exactly to the north of the center would have +c half its area in the NW quadrant and half in the NE quadrant). +c +c To properly "partition" and assign gridpoint areas, we need to +c interpolate the current grid down to a fine resolution. +c +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the guidelines that +c will be used, keeping in mind that we want the final grid spacing +c to be on the order of between 0.05 and 0.10 degree (finer than +c 0.05 deg is superfluous, and coarser than 0.10 deg is too coarse). +c +c Original grid size (deg) # of interps +c ------------------------- ------------ +c 0.8 <= g 4 +c 0.4 <= g < 0.8 3 +c 0.2 <= g < 0.4 2 +c 0.1 <= g < 0.2 1 +c g < 0.1 0 + + + if ((dx+dy)/2. >= 0.8) then + numinterp = 4 + else if ((dx+dy)/2. < 0.8 .and. (dx+dy)/2. >= 0.4) then + numinterp = 3 + else if ((dx+dy)/2. < 0.4 .and. (dx+dy)/2. >= 0.2) then + numinterp = 2 + else if ((dx+dy)/2. < 0.2 .and. (dx+dy)/2. >= 0.1) then + numinterp = 1 + else + numinterp = 0 + endif + + grdintincr = (dx+dy)/2. + do i = 1,numinterp + grdintincr = 0.5 * grdintincr + enddo + +c Now loop through the points in this subdomain, determine if any +c are within 500 km of the center, and then determine what quadrant +c the point is in relative to the center, and then calculate the +c fractional area coverage for winds. + + pdf_ct_tot = 0 + pdf_ct_bin = 0 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_fract_wind_cov, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_fract_wind_cov' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle iloop ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > (rads+(0.75*((dx+dy)/2.)*dtk*cos(glat(j)*dtr)))) + & then + + ! If the distance is greater than "rads" (500 km at initial + ! writing) plus another 3/4 of a gridpoint, then cycle. + ! The extra 3/4 of a gridpoint is to allow for the case of + ! some portion of the area around a gridpoint (whose + ! center point > 500 km) being within the 500 km arc... + ! although that is only factored in for grids with spacing + ! >= 0.1 deg. For smaller grids, where no interpolation is + ! done in this subroutine, then the distance to that point + ! is considered representative and the point is ignored if + ! it is not less than 500 km from the center. + + cycle iloop + + else + + ! First interpolate the area surrounding each grid point to + ! get fine resolution of lats & lons for determining how to + ! partition the area of a gridpoint among quadrants as well + ! as among distance thresholds. + + vmag = sqrt (u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + vmagkts = vmag * conv_ms_knots + + if (numinterp > 0) then + + grdintincr = ((dx+dy)/2.) / 2**numinterp ! "grid spacing" + ! of interpolated grid + ngridint = (2**numinterp) / 2 + + got_pdf = 'notyet' + + njloop: do nj= ngridint,-ngridint,-1 + + xintlat = glat(j) + float(nj) * grdintincr + + niloop: do ni= -ngridint,ngridint + + xintlon = glon(ii) + float(ni) * grdintincr + + call calcdist (xintlon,xintlat,xsfclon + & ,xsfclat,xdist,degrees) + + if (xdist <= 350. .and. got_pdf == 'notyet') then + ! The got_pdf flag is needed because in these loops + ! for niloop & njloop, we are actually looking at + ! tiny areas around the same grid point. So we + ! want to make sure we only count each gridpoint + ! once. + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + got_pdf = 'got_it' + endif + + if (xdist < 500.) then + + ! Compute area of this fraction of a grid box + xarea = (grdintincr * 111195) * + & (grdintincr * 111195 + & * cos(xintlat * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Go through a loop of the bins. The purpose of + ! this is that these "bins" all go from the + ! the center out to a specified radius, they are + ! NOT 100-km wide bins. So if we are dealing with + ! a point at r = 250 km, then that falls in the + ! 0-300 km bin, but it also falls in the 0-400 and + ! 0-500 km bins as well. So we need to run through + ! this binloop multiple times to get the area data + ! into multiple bins. Here are the bins & indices: + ! 1: 0-100 km + ! 2: 0-200 km + ! 3: 0-300 km + ! 4: 0-400 km + ! 5: 0-500 km + + binloop: do ib = idistbin,numbin + + if (xintlon >= xsfclon .and. + & xintlat >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (xintlon >= xsfclon .and. + & xintlat < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop + + endif + + enddo niloop + + enddo njloop + + else + + ! In this else statement is the case for a grid whose + ! resolution is already fine enough that we don't need + ! to interpolate any further. For example, we will have + ! the H*Wind data on a 0.05 degree grid, so that's already + ! fine enough. + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat + & ,xdist,degrees) + + if (xdist <= 350.) then + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + endif + + if (xdist < 500.) then + + ! Compute area of this grid box + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Why the binloop2? See explanation above in the "if" + ! part of this if-then block, where binloop is. + + binloop2: do ib = idistbin,numbin + + if (glon(ii) >= xsfclon .and. + & glat(j) >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (glon(ii) >= xsfclon .and. + & glat(j) < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop2 + + endif + + endif + + endif + + enddo iloop + + enddo jloop + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different quadrants, bins and thresholds... +c ------------------------------------------------- + + if ( verb .ge. 3 ) then + write (6,109) ' ' + & ,' ' + & ,' ' + write (6,109) ' Quadrant Bin Wind_Thresh ' + & ,'Fract_coverage (%) Area_exceeded' + & ,' Area_total' + write (6,109) ' -------- --- ----------- ' + & ,'------------------ -------------' + & ,' ----------' + write (6,109) ' ' + & ,' ' + & ,' ' + + do iq = 1,numquad + do ib = 1,numbin + do it = 1,numthresh + wfract_cov(iq,ib,it) = area_exceed_quad_bin(iq,ib,it) / + & area_total_quad_bin(iq,ib) + write (6,117) cquad(iq),cbin(ib),cthresh(it) + & ,wfract_cov(iq,ib,it)*100.0 + & ,area_exceed_quad_bin(iq,ib,it) + & ,area_total_quad_bin(iq,ib) + enddo + enddo + enddo + endif + + + 109 format (1x,a33,a37,a16) + 117 format (5x,a2,5x,a5,7x,a2,13x,f6.2,10x,f16.1,2x,f16.1) + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different bins and thresholds, but for the +c entire "disc" of the storm, that is, summing all +c quadrants together. +c ------------------------------------------------- + + do it = 1,numthresh + do ib = 1,numbin + do iq = 1,numquad + sum_total_area(ib,it) = sum_total_area(ib,it) + & + area_total_quad_bin(iq,ib) + sum_exceed_area(ib,it) = sum_exceed_area(ib,it) + & + area_exceed_quad_bin(iq,ib,it) + enddo + wfract_cov(5,ib,it) = sum_exceed_area(ib,it) + & / sum_total_area(ib,it) + enddo + enddo + + if ( verb .ge. 3 ) then + do ib = 1,numbin + do it = 1,numthresh + write (6,117) 'TT',cbin(ib),cthresh(it) + & ,wfract_cov(5,ib,it)*100.0 + & ,sum_exceed_area(ib,it) + & ,sum_total_area(ib,it) + enddo + enddo + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_ike_stats (imax,jmax,inp,dx,dy,ist,ifh + & ,fixlon,fixlat,xsfclon,xsfclat,valid_pt,calcparm + & ,ike,sdp,wdp,maxstorm,trkrinfo,igisret) +c +c ABSTRACT: This subroutine computes the Integrated Kinetic Energy +c (IKE) and Storm Surge Damage Potential (SDP) values, based on +c Powell (BAMS, 2007). At this time, we are only computing the IKE +c values for TS threshold (17.5 m/s) and above. We are not yet +c computing wind damage potential (WDP) since, per Mark Powell +c (4/2008), he is currently re-formulating an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c sdp Storm surge damage potential + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4 + integer npts,ipct,igisret,imax,jmax,ist,ifh,ilonfix,jlatfix + integer ibeg,jbeg,iend,jend,igiret,i,j,maxstorm,ii + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real ike(max_ike_cats) + real dx,dy,degrees,rads,ri,dell,xdist,vmag,xarea + real xsfclon,xsfclat,sdp,wdp + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) +c + igisret = 0 + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + rads = 400.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_ike_stats from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for storm ' + print *,'!!! number ',ist + endif + + igisret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_ike_stats calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igisret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + +c Search a grid of points near the storm center, evaluate if the +c storm is within the "rads" distance threshold. If so, compute +c the IKE values for all applicable thresholds (10, 18, 33 m/s). + + do j = jbeg,jend + do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ike_stats, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_ike_stats' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > rads) then + cycle + else + + vmag = sqrt(u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + + if (vmag > 10.0) then + ! Add gridpoint to IKE_10. Compute area first... + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + ike(1) = ike(1) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 18.0) then + ! Add gridpoint to IKE_ts. Area already computed for 10 + ike(2) = ike(2) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 33.0) then + ! Add gridpoint to IKE_h. Area already computed for 10 + ike(3) = ike(3) + (0.5 * (vmag**2) * xarea) + endif + + endif + + enddo + enddo + + ike(1) = ike(1) * 1.e-12 ! Convert from J to TJ + ike(2) = ike(2) * 1.e-12 ! Convert from J to TJ + ike(3) = ike(3) * 1.e-12 ! Convert from J to TJ + +c Compute the storm surge damage potential (sdp) + + if (ike(2) >= 0.0) then + sdp = 0.676 + (0.43 * sqrt(ike(2))) + & - (0.0176 * ((sqrt(ike(2)) - 6.5)**2) ) + else + sdp = -99.0 + endif + +c Print out the IKE and SDP statistics... + + if ( verb .ge. 3 ) then + print *,' IKE_10 (storm energy) = ',ike(1) + print *,' IKE_TS (tropical storm) = ',ike(2) + print *,' IKE_H (hurricane) = ',ike(3) + print *,' SDP = ',sdp + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine distbear (xlatin,xlonin,dist,bear,xlatt,xlont) +c +c ABSTRACT: Given an origin at latitude, longitude=xlato,xlono, +c this subroutine will locate a target point at a distance dist in +c km or nautical miles (depends on what you use for "rad_earth..." +c below), at bearing bear (degrees clockwise from north). +c Returns latitude xlatt and longitude xlont of target point. +c +c *** NOTE *** +c This subroutine was written to handle input lats & lons as this: +c All latitudes are in degrees, north positive and south negative. +c All longitudes are in degrees, west positive and east negative. +c *** **** *** +c +c However, for the longitudes, the rest of the tracker uses all +c 0-360 longitudes. Therefore, we need to convert the input lons +c and then once again convert the lons that are returned back to +c the calling routine. +c +c NOTE-- When origin is at north or south pole, bearing is no +c longer measured from north. Instead, bearing is measured +c clockwise from the longitude opposite that specified in xlono. +c Example-- if xlato=90., xlono=80., the opposite longitude is +c -100 (100 East), and a target at bearing 30. will lie on the +c -70. (70 East) meridian. +c +c AUTHOR: The core of this subroutine was written by Albion +c Taylor, another NOAA employee, in 1981. +c + USE trig_vals + + implicit none +c + real, parameter :: rad_earth_nm = 3440.170 ! radius of earth + real, parameter :: rad_earth_km = 6372.797 ! radius of earth + real xlato,xlono,dist,bear,xlatt,xlont,xlatin,xlonin + real cdist,sdist,clato,slato,clono,slono,cbear,sbear + real z,y,x,r,xlattz,xlontz,ddist,dbear,dxlato,dxlono +c + xlato = xlatin + xlono = xlonin + +cstr print *,' ' +cstr print *,'+++ At top of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlon= ',xlono,'E ',360.-xlono +cstr & ,'W xlat=',xlato +cstr print '(a6,f7.2,a8,f7.2)','dist= ',dist,' bear= ',bear + + if (xlono > 180.) then + ! Longitude input for this subroutine must be positive west + xlono = 360. - xlono + else + ! Longitude input for this subroutine must be negative east + xlono = -1. * xlono + endif + +cstr print '(a31,a8,f8.2)','After conversion for distbear, ' +cstr & ,' xlono= ',xlono + + ddist = dist + dbear = bear + dxlato = xlato + dxlono = xlono + + cdist = cos(ddist/rad_earth_km) + sdist = sin(ddist/rad_earth_km) + clato = cos(dtr*dxlato) + slato = sin(dtr*dxlato) + +cstr print *,'cdist= ',cdist,' sdist= ',sdist,' clato= ',clato +cstr & ,' slato= ',slato + + clono = cos(dtr*dxlono) + slono = sin(dtr*dxlono) + +cstr print *,'dxlono= ',dxlono,' clono= ',clono +cstr & ,' slono= ',slono + + cbear = cos(dtr*dbear) + sbear = sin(dtr*dbear) + +cstr print *,'cbear= ',cbear,' sbear= ',sbear + + z=cdist*slato + clato*sdist*cbear + y=clato*clono*cdist + sdist*(slono*sbear - slato*clono*cbear) + x=clato*slono*cdist - sdist*(clono*sbear + slato*slono*cbear) + +cstr print *,'z= ',z,' y= ',y,' x= ',x + + r = sqrt(x**2 + y**2) + +cstr print *,'r = sqrt(x**2 + y**2) = ',r + + xlattz = atan2(z,r)/dtr + +cstr print *,'xlattz = datan2(z,r)/dtr = ',xlattz + + xlatt = xlattz + + if (r <= 0.) go to 20 + + xlontz = atan2(x,y)/dtr + +cstr print *,'xlontz = atan2(x,y)/dtr = ',xlontz + +c xlont = xlontz + + ! Return the target longitude back to the calling routine + ! as a 0-360 positive east longitude.... + + xlont = mod(360.-xlontz,360.) + +c xlont = mod(360.+xlontz,360.) + +cstr print *,' ' +cstr print *,'At end of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlont= ',xlont,'E ' +cstr ,360.-xlont,'W xlatt=',xlatt + + return + 20 xlont=0. +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_uneven (targlat,targlon,dx,dy + & ,imax,jmax,trkrinfo,level,cparm,xintrp_val,ibiret) +c +c ABSTRACT: This subroutine performs a bilinear interpolation to get +c a data value at a given lat/lon that may be anywhere within a box +c defined by the four surrouding grid points. In the diagram below, +c remember that for our grids we are using in the tracker, the +c latitude index starts at the north pole and increases southward. +c The point "X" indicates the target lat/lon location of the value +c for which we are bilinearly interpolating. The values to and ta +c below are ratios that determine how geographically close the +c target location is to the point of origin (pt.1 (i,j)) in terms +c of both longitude (to) and latitude (ta). +c +c +c pt.1 pt.2 +c (i,j) (i+1,j) +c +c +c +c X +c +c pt.4 pt.3 +c (i,j+1) (i+1,j+1) +c + + USE grid_bounds; USE tracked_parms; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + character cparm*1 + real targlat,targlon,xintrp_val,dx,dy + real to,ta,d1,d2,d3,d4,z,eastlon + integer ie,iw,jn,js,ibiret,imax,jmax,level,nlev + + ibiret = 0 + +c -------------------------------------------------------------- +c For the latitudes and longitudes surrounding our target +c lat/lon location, convert the lat/lon values into i- and +c j-indices. +c -------------------------------------------------------------- + +c Find the j-indices for the points just to the north and the +c south of targlat.... + + if (targlat >= 0.0) then + ! For a northern hemisphere storm, jn is the j-index for the + ! point just to the *NORTH* (poleward) of targlat. + jn = int((glatmax - targlat)/dy + 1.) + js = jn + 1 + else + ! For a southern hemisphere storm, js is the j-index for the + ! point just to the *SOUTH* (poleward) of targlat. + js = ceiling((glatmax - targlat)/dy + 1.) + jn = js - 1 + endif + + ! Check to make sure that points are not being requested beyond + ! the northern or southern boundaries of the grid. This is most + ! likely to happen for a smaller, regional grid. + + if (jn > jmax .or. js > jmax) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jmax exceeded in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + + if (jn < 1 .or. js < 1) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jn < 0 or js < 0 in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + +c Find the i-indices for the points just to the east and the +c west of targlon.... + + ie = int((targlon - glonmin)/dx + 2.) + iw = ie - 1 + + ! Check for GM wrapping. Check ie to see if it is between the + ! most eastward gridpoint and the GM (i.e., on a 1-deg global + ! grid (360x181), it would be if targlon was between 359.0 (i=360) + ! and the GM (i=1, not i=361)). Similarly then, if we adjust ie + ! to then be 1, then we have a problem with iw, + ! since iw = 1 - 1 = 0. + + if (ie > imax) then + if (trkrinfo%gridtype == 'global') then + ie = ie - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ie > imax in subroutine ' + print *,'!!! bilin_int_uneven for a non-global grid. ' + print *,'!!! Returning to calling routine after ' + print *,'!!! assigning missing wind value of -99.' + print *,'!!! ie= ',ie,' imax= ',imax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + + if (iw < 1) then + if (trkrinfo%gridtype == 'global') then + iw = iw + imax + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: iw < 1 in subroutine bilin_int_uneven' + print *,'!!! for a non-global grid. Returning to calling ' + print *,'!!! routine after assigning missing wind value ' + print *,'!!! of -99. iw= ',iw + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' +++ Interpolating winds for cparm= ',cparm +ctmwc print '(6x,4(a4,i3))','jn= ',jn,' js= ',js,' iw= ',iw,' ie= ',ie +ctmwc endif + +c ---------------------------------------------------------------- +c Calculate the longitude (to) and latitude (ta) location ratios. +c Check for GM wrapping, as we can run into a problem here if +c interpolating for points that are just west of the GM, since we +c would be interpolating using values of longitude just west of +c GM (say, glon(iw)=359.5) and the GM (glon(ie) = 0.0). This +c makes for an incorrect "to" ratio below, with 0-359.5 in the +c denominator. We have to account for this.... +c ---------------------------------------------------------------- + + if (glon(iw) > 300.0 .and. + & (glon(ie) < 10. .and. glon(ie) >= 0.)) then + eastlon = 360. - glon(ie) + else + eastlon = glon(ie) + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,'glat(js)= ',glat(js),' glat(jn)= ',glat(jn) +ctmwc endif + + to = (targlon - glon(iw)) / (eastlon - glon(iw)) + ta = (targlat - glat(jn)) / (glat(js) - glat(jn)) + +c -------------------------------------------------------------- +c Copy the data values at the 4 known points into simple scalar +c variables +c -------------------------------------------------------------- + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cparm == 'u') then + d1 = u(iw,jn,nlev) + d2 = u(ie,jn,nlev) + d3 = u(ie,js,nlev) + d4 = u(iw,js,nlev) + else if (cparm == 'v') then + d1 = v(iw,jn,nlev) + d2 = v(ie,jn,nlev) + d3 = v(ie,js,nlev) + d4 = v(iw,js,nlev) + else if (cparm == 'm') then + d1 = lsmask(iw,jn) + d2 = lsmask(ie,jn) + d3 = lsmask(ie,js) + d4 = lsmask(iw,js) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in bilin_int_uneven.' + print *,'!!! Input cparm not recognized.' + print *,'!!! cparm= ',cparm + print *,'!!! EXITING....' + endif + + stop 95 + endif + + z = 1.9427 + +cstr print '(2x,4(a4,f8.2))',' d1= ',d1*z,' d2= ',d2*z +cstr & ,' d3= ',d3*z,' d4= ',d4*z + +c ------------------------------------------------------------- +c Compute the interpolated value +c ------------------------------------------------------------- + + xintrp_val = (1.-to) * (1.-ta) * d1 + & + to * (1.-ta) * d2 + & + to * ta * d3 + & + (1.-to) * ta * d4 + +cstr print '(2x,2(a11,f8.2))',' xintrp= ',xintrp_val,' (in kts)= ' +cstr & ,xintrp_val*z +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine sort_storms_by_pressure (gridprs,ifh,maxstorm,sortindex + & ,issret) +c +c ABSTRACT: This subroutine sorts storms by mslp. It is called by +c subroutine tracker just before the loop for "stormloop" is done +c for all the storms at a particular forecast hour. It is only +c called for the "midlat" and "tcgen" cases. The end result of +c this sort is an array (prsindex) that contains the indeces of +c the storms, arranged from lowest pressure to highest (and note +c that the "undefined" storms have a pressure of 9999.99 mb and +c thus get sorted to the bottom of the array). The purpose of +c doing this is so that we track the most intense storms first. +c Why go to the trouble? Imagine a scenario in which we are +c tracking a complex system in which there are 2 low pressure +c centers. Let's say that one is becoming dominant and +c intensifying, while the other is weakening. Now, let's assume +c that the weakening one eventually gets absorbed into the +c stronger, more dominant low. Now we only have 1 low, but if in +c the tracker stormloop, we first process the data for the +c weakening low, we will attribute the track to that storm, and +c then when we get to the point in the loop where we are trying +c to get the track for the stronger storm, we will (erroneously) +c stop the tracking for that storm since the storm center has +c already been attributed to the weaker storm. But by using this +c subroutine, we will track the stronger storm first, and thus +c avoid this problem. +c +c NOTE: The pressures used in the sort are those obtained at the +c previous forecast hour. At forecast hour = 0, just use the +c values as they were input to this routine, since they were +c found in first_ges_center from strongest to weakest already. +c +c INPUT: +c gridprs real array of storm mslp values +c ifh integer index for the current forecast hour +c maxstorm max num of storms that can be handled in this run +c +c OUTPUT: +c sortindex contains a sorted array of indeces. The orders +c sort routine does NOT rearrange the data. Rather, it +c returns this array of sorted indeces which point to +c the correct order of data values in the data array. +c issret return code from this subroutine +c + USE set_max_parms + USE verbose_output + + real, allocatable :: iwork(:) + real gridprs(maxstorm,maxtime) + integer ifh,maxstorm + integer sortindex(maxstorm) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: prstemp(:) +c + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iva /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub sort_storms_by_pressure allocating' + print *,'!!! prstemp or iwork arrays: ' + print *,'!!! iva= ',iva,' iwa= ',iwa + endif + + STOP 94 + return + endif + + if (ifh > 1) then + +c print *,' ' +c print *,'--- Before sort, original prs values follow:' +c print *,' ' + + do ist = 1,maxstorm + prstemp(ist) = gridprs(ist,ifh-1) +c write (6,81) ist,prstemp(ist)/100.0 + enddo + + imode = 2 + sortindex = 0 + call qsort (prstemp,sortindex,maxstorm) + +ccccc call orders (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) +ccccc call orders_4byte (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Pressure-sorted storm list:' + print *,' ' + + do ist = 1,maxstorm + if (prstemp(sortindex(ist))/100.0 < 9999.0) then + write (6,82) ist,sortindex(ist) + & ,prstemp(sortindex(ist))/100.0 + endif + enddo + + 81 format (1x,'ist= ',i5,' Original (unsorted) prstemp= ',f7.2) + 82 format (1x,'ist= ',i5,' sortindex(ist)= ',i5 + & ,' prstemp= ',f7.2) + endif + + else + do ist = 1,maxstorm + sortindex(ist) = ist + enddo + endif + + deallocate (prstemp); deallocate (iwork) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getvrvt (centlon,centlat,xlon,xlat + & ,udat,vdat,vr,vt,igvtret) +c +c ABSTRACT: This subroutine takes as input a u-wind and v-wind value +c at an input (xlon,xlat) location and returns the tangential and +c radial wind components relative to the input center lat/lon +c position (centlon,centlat). The only trick to this whole +c subroutine is figuring out the angle from the center point to the +c data point, and we do this by creating a triangle with the leg +c from the center point to the data point being the hypotenuse. +c +c NOTE: All longitudes must be in positive degrees east (0-360) !!! +c +c INPUT: +c centlon Longitude of center point +c centlat Latitude of center point +c xlon Longitude of pt at which vr & vt will be computed +c xlat Latitude of pt at which vr & vt will be computed +c udat u-value of wind at the point (xlon,xlat) +c vdat v-value of wind at the point (xlon,xlat) +c +c OUTPUT: +c vr Radial wind component at (xlon,xlat) wrt (centlon,centlat) +c vt Tang wind component at (xlon,xlat) wrt (centlon,centlat) +c igvtret Return code from this subroutine +c + USE trig_vals + USE verbose_output + + implicit none + + real centlon,centlat,xlon,xlat,udat,vdat,vr,vt,degrees,tmpxlon + real angle,xlondiff,xlatdiff,opp_dist,hyp_dist,sin_value + real cos_value,adj_dist,tmpangle,sin_angle,cos_angle + real uvrcomp,vvrcomp,uvtcomp,vvtcomp + integer igvtret +c + call calcdist(centlon,centlat,xlon,xlat,hyp_dist,degrees) + +c xxxx + + tmpxlon = xlon + + if (centlon > 330.0) then + + if (xlon > 360.0) then + + tmpxlon = xlon ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (xlon < 30.0) then + + tmpxlon = xlon + 360. ! In this case, the fix center is just + ! to the west of the GM with a lon (centlon) + ! > 330, while the point being evaluated + ! (xlon) is just east of the GM, but with a + ! lon (centlon) < 30. Need to adjust here to + ! to get the xlon in the 330+ frame of + ! reference. + + endif + + elseif (centlon >= 0 .and. centlon < 30.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = 360. - xlon + + endif + + elseif (centlon < 0.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = -1 * (360. - xlon) + + endif + + endif + + xlatdiff = abs(centlat - xlat) + xlondiff = abs(centlon - tmpxlon) + + if (centlon > 355.0) then + write (6,91) centlon,tmpxlon,hyp_dist,degrees,xlondiff + 91 format (1x,'centlon= ',f8.3,' tmpxlon= ',f8.3,' hyp_dist= ' + & ,f10.2,' degrees= ',f10.2,' xlondiff= ',f12.2) + endif + + if (xlondiff == 0 .and. xlatdiff > 0) then + + if (centlat > xlat) angle = 180 ! pt directly south of ctr + if (centlat < xlat) angle = 0 ! pt directly north of ctr + + else if (xlondiff > 0 .and. xlatdiff == 0) then + + if (centlon > tmpxlon) angle = 270 ! pt directly west of ctr + if (centlon < tmpxlon) angle = 90 ! pt directly east of ctr + + else + + ! This next part figures out the angle from the center point + ! (centlon,centlat) to the data point (tmpxlon,xlat). It does + ! this by setting up a triangle and then using inverse trig + ! functions to get the angle. Since this is a kludgy way to + ! do it that doesn't account for the curvature of the earth, + ! we'll do it 2 ways, using asin and then acos, then take the + ! average of those 2 for the angle. hyp_dist, calculated just + ! above, is the distance from the center pt to the data pt. + + opp_dist = xlatdiff/360. * ecircum + sin_value = opp_dist / hyp_dist + if (sin_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, sin_value > 1, setting to 1.' + print *,'!!! opp_dist= ',opp_dist,' hyp_dist= ',hyp_dist + print *,'!!! sin_value = ',sin_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + sin_value = 0.99999 + endif + sin_angle = asin(sin_value) / dtr + + call calcdist(centlon,centlat,tmpxlon,centlat,adj_dist,degrees) + cos_value = adj_dist / hyp_dist + if (cos_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, cos_value > 1, setting to 1.' + print *,'!!! adj_dist= ',adj_dist,' hyp_dist= ',hyp_dist + print *,'!!! cos_value = ',cos_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + cos_value = 0.99999 + endif + cos_angle = acos(cos_value) / dtr + + tmpangle = 0.5 * (sin_angle + cos_angle) + + ! The previous lines of code just calculated an angle between + ! 0 and 90. This next if structure adjusts that angle to + ! instead be between 0 and 360. + + if (centlat <= xlat .and. centlon <= tmpxlon) then + angle = 90 - tmpangle + else if (centlat > xlat .and. centlon <= tmpxlon) then + angle = 90 + tmpangle + else if (centlat >= xlat .and. centlon >= tmpxlon) then + angle = 270 - tmpangle + else if (centlat < xlat .and. centlon >= tmpxlon) then + angle = 270 + tmpangle + endif + + endif + + uvrcomp = udat * sin(angle * dtr) + vvrcomp = vdat * cos(angle * dtr) + vr = uvrcomp + vvrcomp + + uvtcomp = (-udat) * cos(angle * dtr) + vvtcomp = vdat * sin(angle * dtr) + vt = uvtcomp + vvtcomp + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcfunix (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in the new ATCF UNIX format. +c Unlike the old atcf DOS format in which you waited until the +c whole tracking was over to write the output for all forecast +c hours, with this atcfunix format, each time we are calling this +c subroutine, it is to only write out 1 record, which will be the +c fix info for a particular storm at a given time. Also, even +c though we have some data (GFS, NAM) at 6-hour intervals, Jim +c Gross informed me that TPC does not need the positions at such +c frequency, and keeping the reporting at 12 hour intervals is fine. +c +c While this new atcfunix format contains much more information than +c the old 1-line atcf dos message, for our purposes we will use the +c slots for mslp and wind radii. An example set of output records +c will look like the following: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c plastbar pressure of the outermost closed isobar +c rlastbar radius (nm) of the outermost closed isobar +c rmax radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c cps_vals real array with the values for the 3 cyclone phase +c space parameters: (1) is for Parameter B (thermal +c asymmetry); (2) is for lower level (600-900 mb) thermal +c wind; (3) is for upper level (300-600 mb) thermal wind. +c wcore_flag character for value of 300-500 mb warm core: y, n, or +c 'u' for undetermined. +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE phase + USE verbose_output + + type (datecard) inp + type (trackstuff) trkrinfo + + real cps_vals(3) + real outlon,outlat,rmax,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,irmax,output_fhr,ic,iplastbar,irlastbar + integer vradius(3,4),icps_vals(3) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + character comma_fill1*48,comma_fill2*31,comma_filler*79 + + if ( verb .ge. 3 ) then + print *,'TTT top of atcfunix, ist= ',ist,' ifh= ',ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcfunix. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'in output_atcfunix, tcv_storm_id= ' + & ,storm(ist)%tcv_storm_id + print *,'in output_atcfunix, tcv_storm_id(3:3)= ' + & ,storm(ist)%tcv_storm_id(3:3) + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' +!zhang case ('A','a'); basinid = 'NA' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + if (trkrinfo%want_oci) then + if (plastbar > 0.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -99 + endif + if (rlastbar > 0.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -99 + endif + else + iplastbar = -99 + irlastbar = -99 + endif + + if ( verb .ge. 3 ) then + print *, 'output: rlastbar=',rlastbar,' irlastbar=',irlastbar + print *, 'output: plastbar=',plastbar,' iplastbar=',iplastbar + endif + +c Now convert all of the cyclone phase space parameter values from +c real to integer. + + do ic = 1,3 + if (cps_vals(ic) > -9999.0) then + if (cps_vals(ic) >= 0.0) then + icps_vals(ic) = int(cps_vals(ic)*10. + 0.5) + else + icps_vals(ic) = int(cps_vals(ic)*10. - 0.5) + endif + else + icps_vals(ic) = -9999 + endif + enddo + + if (wcore_flag == 'y'.or. wcore_flag == 'Y') then + wcore_flag = 'Y' + elseif (wcore_flag == 'n' .or. wcore_flag == 'N') then + wcore_flag = 'N' + elseif (wcore_flag == 'u' .or. wcore_flag == 'U') then + wcore_flag = 'U' + else + wcore_flag = 'U' + endif + + comma_fill1 = ', 0, 0, , 0, , 0, 0, ,' + comma_fill2 = ' , , , 0, 0, 0, 0' + comma_filler = comma_fill1//comma_fill2 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + else + + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,'rmax= ',rmax,' irmax= ',irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,a79,', THERMO PARAMS' + & ,3(', ',i7),', ',a1,', ',i2,', DT, -999') + 91 format (a2,', ',a4,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,2(', ',i3),', ',a3) + +c bug fix for IBM: flush the output stream so it actually writes + flush(64) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each +c storm. This message contains the model identifier, the forecast +c initial date, and the positions for 0, 12, 24, 36, 48, 60 and 72 +c hours. In the case of the regional models (NGM, Eta), which +c only go out to 48h, zeroes are included for forecast hours +c 60 and 72. +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by Steve Lord for plotting purposes, and his +c plotting routines need the longitudes in 0 - 360, increasing +c westward. Thus, a necessary adjustment is made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + print *,'top of output_all' + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + intlon(ifh) = 0 + intlat(ifh) = 0 + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + endif + + enddo ifhloop + + print *,'before select case, atcfname= ' + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(5),intlon(5),intlat(9),intlon(9),intlat(13) + & ,intlon(13),intlat(17),intlon(17),intlat(21),intlon(21) + & ,0,0,storm(ist)%tcv_storm_id + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(3),intlon(3),intlat(5),intlon(5),intlat(7) + & ,intlon(7),intlat(9),intlon(9),intlat(11),intlon(11) + & ,intlat(13),intlon(13),storm(ist)%tcv_storm_id + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3),intlat(4) + & ,intlon(4),intlat(5),intlon(5),intlat(6),intlon(6) + & ,intlat(7),intlon(7),storm(ist)%tcv_storm_id + + case ('GDA','HDA') ! GDAS, HDAS + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),0,0,0,0,0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case default +c print *,'!!! ERROR in subroutine output_all. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + print *,'!!! Model name is not identified: ',atcfname + + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + 81 format (i2,a4,4i2.2,14i4,1x,a3) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm + & ,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each storm +c in ATCF format. This message contains the model identifier, the +c forecast initial date, and the positions for 12, 24, 36, 48 +c and 72 hours. This message also contains the intensity +c estimates (in knots) for those same hours. The conversion for +c m/s to knots is to multiply m/s by 1.9427 (3.281 ft/m, +c 1 naut mile/6080 ft, 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by the atcf system at TPC for plotting +c purposes, and the atcf plotting routines need the longitudes in +c 0 - 360, increasing westward. Thus, a necessary adjustment is +c made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real xmaxwind(maxstorm,maxtime) + real conv_ms_knots + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4,basinid*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + conv_ms_knots = 1.9427 + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + + intlon(ifh) = 0 + intlat(ifh) = 0 + + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + + endif + + enddo ifhloop + + basinid = ' ' + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid(1:2) = 'AL' + case ('E','e'); basinid(1:2) = 'EP' + case ('C','c'); basinid(1:2) = 'CP' + case ('W','w'); basinid(1:2) = 'WP' + case ('O','o'); basinid(1:2) = 'SC' + case ('T','t'); basinid(1:2) = 'EC' + case ('U','u'); basinid(1:2) = 'AU' + case ('P','p'); basinid(1:2) = 'SP' + case ('S','s'); basinid(1:2) = 'SI' + case ('B','b'); basinid(1:2) = 'BB' +cPENG case ('A','a'); basinid(1:2) = 'NA' + case ('A','a'); basinid(1:2) = 'AA' + case default; basinid(1:2) = '**' + end select + basinid(3:4) = storm(ist)%tcv_storm_id(1:2) + + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(5),intlon(5) + & ,intlat(9),intlon(9),intlat(13),intlon(13),intlat(17) + & ,intlon(17),0,0 + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,17)*conv_ms_knots) + 0.5) + & ,0 + & ,basinid,inp%byy + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble, ECMWF hi-res + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4),intlat(5) + & ,intlon(5),intlat(7),intlon(7) + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('GDA','HDA') ! GDAS, HDAS + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4) + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,0,0,0,0 + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case default +c print *,'!!! ERROR in subroutine output_atcf. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + end select + + enddo stormloop + + 82 format (i2,a4,4i2.2,10i4,5i3,1x,a4,i2.2) +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_hfip (outlon,outlat,inp,ist + & ,ifh,vmaxwind,xminmslp,vradius,rmax,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified ATCF UNIX format. +c The modification is to allow for sub-hourly output. That is, +c instead of just integer output hours, we can have output at +c 10, 15 or 20 past an hour. This necessitates a change in the +c "forecast hour" placeholder in the ATCF format. Instead of it +c being an I3, we'll make it an I5, with something like a lead time +c of 36.25h being rounded and truncated to 03625 for output. +c +c An example set of output records using the standard atcf format +c looks like the following: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c An example set of modified output records will look like the +c following, for the case of a lead time of 36:15 (36.25): +c +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifh index for the lead time array +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c rmax Radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms + USE verbose_output + + type (datecard) inp + + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,rmax + integer intlon,intlat,output_fhr,irmax,ileadtime + integer vradius(3,4) + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_hfip. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + ! ST: ifcsthour does not exist, so output_fhr is always + ! filled with invalid data here. However, output_fhr is + ! never used, so it is safe to remove. + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + ! output_fhr = ifcsthour + 3 + ileadtime = nint((fhreal(ifh) + 3.0) * 100.0) + else + ! output_fhr = ifcsthour + ileadtime = nint(fhreal(ifh) * 100.0) + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4),irmax + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4),irmax + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4),irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i5.5,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', 0, 0, ',i3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(69) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_fract_wind (outlon,outlat,xsfclon,xsfclat + & ,inp,ist,ifcsthour,vmaxwind,xminmslp,wfract_cov + & ,wfract_type,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values for the fractional areal coverage of various wind +c thresholds. In addition, this subroutine also writes out +c records to a file containing data on the PDF of wind magnitudes +c within r=350 km. +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with areal coverage thresholds. +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, NEE, 981, 857, 629, 810 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, NEE, 874, 732, 319, 610 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, NEE, 454, 327, 99, 270 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, AAE, 721, 721, 721, 721 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, AAE, 465, 465, 465, 465 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, AAE, 298, 298, 298, 298 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the pctgs for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind pctg info; all the +c other info is identical for each entry. +c +c Listed after the "XX" in each record is the radius from which +c the coverage is valid (000 km in this case); Next is the radius +c at which the coverage stops (100 km in this case). Next is the +c wind threshold (34, 50, 64). Next is an identifier for which +c quadrant the coverage starts in (first 2 characters are NE, SE, +c SW, NW); the last character indicates if the coverages are +c computed in the quadrants as earth-relative ("E") or +c storm-motion relative ("R"). The ones listed there as "AAE" +c are for the full disc (i.e., 4-quadrant average), earth-relative. +c Next are the wind coverage percentages, listed as percentage * 10 +c (e.g., 981 = 98.1%). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c wfract_cov percent areal coverage for various wind thresholds +c wfract_type 'earth' or 'storm' relative analysis +c pdf_ct_bin array for pdf of wind magnitudes within r=350 km +c pdf_ct_tot total count of pdf points for r < 350 km +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,pdfval + real wfract_cov(numquad+1,numbin,numthresh) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer :: windthresh(numthresh) = (/34,50,64/) + integer pdf_ct_bin(16) + integer intlon,intlat,output_fhr,intlon100,intlat100,pdf_ct_tot + integer maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (wfract_type == 'earth') then + wt = 'E' + else if (wfract_type == 'storm') then + wt = 'R' + else + wt = 'X' + endif + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'NE',wt + & ,int((1000.*wfract_cov(1,ib,it))+0.5) + & ,int((1000.*wfract_cov(2,ib,it))+0.5) + & ,int((1000.*wfract_cov(3,ib,it))+0.5) + & ,int((1000.*wfract_cov(4,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'AA',wt + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a6,i3.3,', ',i3.3,', ' + & ,i3,', ',a2,a1,4(', ',i4),', ',i4,a1,', ',i5,a1) + +c -------------------------------------------------- +c Now compute and write out the pdf values for the +c wind magnitude.... +c -------------------------------------------------- + + do ip = 1,16 + pdfval = float(pdf_ct_bin(ip)) / float(pdf_ct_tot) + write (76,85) atcfymdh,basinid,storm(ist)%tcv_storm_id(1:2) + & ,output_fhr,10*(ip-1),10*ip,pdf_ct_bin(ip) + & ,pdf_ct_tot,pdfval + enddo + + 85 format (1x,i10.10,3x,a2,a2,3x,i3,3x,i3.3,'_',i3.3,3x,i7,2x,i7 + & ,2x,f6.3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(73) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_wind_structure (outlon,outlat,xsfclon + & ,xsfclat,inp,ist,ifcsthour,vmaxwind,xminmslp,er_wind + & ,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values of the winds at specified distances along 45-degree +c radials in each storm quadrant. These are output +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 radials: NE, SE, +c SW and NW (45,135,225,315). For the storm-relative estimates, +c these radials will be computed at the same relative angles (i.e., +c 45,135,225,315), but with respect (positive clockwise) to the +c direction of storm motion. For example, for a storm moving with +c a heading of 280, the wind structure is evaluated at these +c radials: 325 (front-right; 45 deg CW from heading), 55 (back- +c right; 135 deg CW from heading), 145 (back-left; 225 deg CW from +c heading), 235 (front-left; 315 deg CW from heading). +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with wind values at the 13 specified distances +c (10, 25, 50, 75, 100, 150, 200, 250, 300, 350, 400, 450, 500 km) +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NEE, 1137, 1221, 854, 655, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SEE, 947, 982, 474, 396, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SWE, 645, 683, 328, 277, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NWE, 725, 753, 619, 429, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FRR, 1134, 1224, 852, 654, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BRR, 944, 984, 472, 393, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BLR, 649, 686, 321, 272, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FLR, 729, 756, 613, 421, etc., ... out to 500 km +c +c NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text. +c NOTE: These winds are in m/s coming into this routine and will +c be converted to knots*10 for output (e.g., 1221 = 122.1 kts) +c +c The "71" ID indicates earth-relative winds, the "72" ID indicates +c storm-relative winds. Here are the other IDs that will be used: +c 81: Tangential winds, earth-relative (m/s) +c 82: Tangential winds, storm-relative (m/s) +c 91: Radial winds, earth-relative (m/s) +c 92: Radial winds, storm-relative (m/s) +c +c Note that in this example, for this 36h forecast hour, there are +c 8 entries. This is so that we can include the wind values for +c the 4 different quadrants, for both the earth relative analyses +c (NEE, SEE, SWE, NWE) and the storm-relative analyses (FRR, BRR, +c BLR, FLR). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + integer ioutwind(numdist) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real outlon,outlat + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,id,intlon100,intlat100,ir + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*2 :: crel(4) = (/'FR','BR','BL','FL'/) + + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + +c Total wind (converted to knots*10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 71, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Total wind (converted to knots*10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 72, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 81, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 82, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 92, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a10,a2,a1,14(', ',i4) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(72) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_ike (outlon,outlat,xsfclon,xsfclat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,ike,sdp,wdp,maxstorm + & ,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the Integrated Kinetic Energy (IKE) and Storm Surge Damage +c Potential (SDP), based on Powell (BAMS, 2007). At this time, we +c are only computing the IKE values for TS threshold (17.5 m/s) and +c above. We are not yet computing wind damage potential (WDP) +c since, per Mark Powell (4/2008), he is currently re-formulating +c an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with WDP, SDP and IKE values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, 340, 560, 212, 174, 42, 93, 12, 0 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, WDP, SDP, I10, ITS, IH ,I2540,I4154, I55 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Values for WDP and SDP are multiplied by 10 in this routine +c before being written out. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c ike integrated kinetic energy, in units of TJ +c sdp storm surge damage potential +c wdp wind damage potential +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,sdp,wdp + real ike(max_ike_cats) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,intlon100,intlat100,maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (74,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, IKE',int((wdp*10)+0.5),int((sdp*10)+0.5) + & ,int(ike(1)+0.5),int(ike(2)+0.5),int(ike(3)+0.5) + & ,int(ike(4)+0.5),int(ike(5)+0.5),int(ike(6)+0.5) + & ,intlat100,clatns,intlon100,clonew +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a14,8(',',i5) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(74) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_phase (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,paramb,vtl_slope + & ,vtu_slope,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the three parameters that comprise Bob Hart's cyclone phase +c space (CPS). These parameters are his "parameter B", which +c assesses the left-right thermal asymmetry, and the upper +c troposphere (300-600 mb) and lower troposphere (900-600 mb) +c thermal wind values. +c +c LOCAL: +c +c Arrays: +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with paramb, vtl_slope and vtu_slope values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, 340, 560, 212 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, B, VTL, VTU +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c paramb thermal asymmetry +c vtl_slope thermal wind value for lower troposphere (900-600 mb) +c vtu_slope thermal wind value for upper troposphere (600-300 mb) +c +c OUTPUT: +c ioiret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + real outlon,outlat,paramb,vtl_slope,vtu_slope + real vmaxwind,conv_ms_knots,xminmslp + integer intlon,intlat,output_fhr + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (71,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 95, CPS',int(paramb+0.5),int(vtl_slope+0.5) + & ,int(vtu_slope+0.5) +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a14,3(',',i6)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(71) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf_gen (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different. Here's an example of the +c TPC standard atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c be the one that is pulled from the tcvitals or gen_vitals +c record: +c +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 50, NEQ, 155, 000, 000, 000 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 64, NEQ, 000, 000, 000, 000 +c +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd storm translation speed +c istmdir direction of storm movement +c plastbar pressure of last closed isobar +c rlastbar radius of last closed isobar +c rmax radius of max winds +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals; USE level_parms + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real outlon,outlat,plastbar,rlastbar,rmax + real vmaxwind,conv_ms_knots,xminmslp,mslp_outp_adj + real cps_vals(3) + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar,irmax + integer ivtl,ivtu,iparamb,output_fhr + integer vradius(3,4) + integer imeanzeta(nlevgrzeta),igridzeta(nlevgrzeta) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + + if ( verb .ge. 3) then + print *,'+++ Top of output_atcf_gen, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (rmax > -90.0) then + irmax = int(rmax + 0.5) + else + irmax = -99 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(66) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf_sink (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta,igridzeta + & ,cps_vals,plastbar,rlastbar,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The "sink" in the subroutine name indicates that this output +c contains the whole kitchen sink of forecast storm info. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different, and the part after the radii +c data is different. Here's an example of the TPC standard +c atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c indicate the lat/lon at which the storm was *first* found in +c the model. The position may be either found within this run +c of the tracker, or that position may have been pulled from the +c tcvitals or gen_vitals record: +c +c 2000092500_F000_206N_0623W_13L, 2000092500, 03, GFSO, 036 +c , 243N, 675W, 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c , PLAS, RLAS, RMX, DIR, SPD, B, VTU, VTL +c , Z8MN, Z8MX, Z7MN, Z7MX +c +c As noted above, there is extra info at the end, after the +c "34, NEQ, 242, 163, 124, 208" radii info. Here is a key +c to indicate what these items are: +c +c PLAS: Pressure (mb) of last closed isobar +c RLAS: Radius of the last closed isobar in nm, 0 - 9999 nm. +c RMX: Radius of max winds, 0 - 999 nm. +c DIR: Direction of storm motion. +c SPD: Speed of storm motion (m/s * 10). +c B: Hart's CPS "Parameter B" thickness asymmetry value (m). +c VTL: Hart's CPS thermal wind (Lower, 900-600) value. +c VTU: Hart's CPS thermal wind (Upper, 600-300) value. +c Z8MN: Mean value of 850 mb zeta surrounding storm. +c Z8MX: Max value of 850 mb zeta near storm. +c Z7MN: Mean value of 700 mb zeta surrounding storm. +c Z7MX: Max value of 700 mb zeta near storm. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd speed of storm translation +c istmdir direction of storm motion +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c plastbar pressure of last closed isobar (pa) +c rlastbar radius of last closed isobar (nm) +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real cps_vals(3) + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar + integer iparamb,ivtl,ivtu,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1 + + if ( verb .ge. 3 ) then + print *,'+++ Top of output_atcf_sink, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4)) + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',2(i4,', '),4(i3,', '),2(i5,', '),4(i4,', '),a9) + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',2(i4,', '),i3,', ',2(i4,', '),3(i6,', '),4(i6,', ') + & ,a9) + +c write (68,87) gstm%gv_gen_date,gstm%gv_gen_lat +c & ,gstm%gv_gen_latns,gstm%gv_gen_lon +c & ,gstm%gv_gen_lonew,gstm%gv_gen_type +c & ,inp%bcc,inp%byy,inp%bmm,inp%bdd,inp%bhh +c & ,adjustr(atcfname),ifcsthour,intlat,clatns,intlon,clonew +c & ,int((vmaxwind*conv_ms_knots) + 0.5) +c & ,int(xminmslp/100.0 + 0.5) +c & ,'XX, 34, NEQ' +c & ,istmspd,istmdir,imeanzeta(1),igridzeta(1) +c & ,imeanzeta(2),igridzeta(2) +c +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,6(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(68) + + return + end + +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_tcvitals (xlon,xlat,inp,ist,iovret) +c +c ABSTRACT: This subroutine outputs a tcvitals record. The +c lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE inparms; USE set_max_parms + USE verbose_output + + type (tcvcard) stm + type (datecard) inp + real xlon,xlat +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "storm" +c components for this storm, then we will change the specific +c components that we need to. + + stm = storm(ist) + + stm%tcv_center = 'AEAR' + + stm%tcv_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + stm%tcv_latns = 'S' + else + stm%tcv_latns = 'N' + endif + + if (xlon >= 180.) then + stm%tcv_lon = 3600 - int(xlon * 10. + 0.5) + stm%tcv_lonew = 'W' + else + stm%tcv_lon = int(xlon * 10. + 0.5) + stm%tcv_lonew = 'E' + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) stm + endif + + write (65,21) stm + + 21 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + +c +c bug fix for IBM: flush the output stream so it actually writes + flush(65) + + return + end + +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_gen_vitals (xlon,xlat,inp,ist,istmspd,istmdir + & ,iovret) +c +c ABSTRACT: This subroutine outputs a modified vitals record. +c The lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c The storm identifier is different than that for a standard +c tcvitals. The storm identifier contains the date/time that +c the storm was first identified, and the lat/lon position at +c which it was first identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE gen_vitals; USE inparms; USE set_max_parms + USE verbose_output + + implicit none + + type (gencard) gstm + type (datecard) inp + real xlon,xlat + integer ist,iovret,istmspd,istmdir +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the vitals record. + + if (gstm%gv_gen_date /= 99999) then + + if (gstm%gv_gen_type /= 'FOF') then + ! If this is not a 'FOF' storm (found on the fly storm), then + ! it must be a TC vitals storm, or a tropical cyclone, and we + ! don't want to create a vitals record for a tropical cyclone, + ! since we will rely on reading them from the TC Vitals + ! database instead. + return + endif + + else + + ! This storm is new in this forecast/analysis and was found on + ! the fly in the first time level for this run and there was no + ! previous vitals record for this system + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = 0 + + gstm%gv_gen_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_gen_latns = 'S' + else + gstm%gv_gen_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_gen_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'W' + else + gstm%gv_gen_lon = int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'E' + endif + + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + gstm%gv_obs_ymd = inp%bcc * 1000000 + & + inp%byy * 10000 + & + inp%bmm * 100 + & + inp%bdd + + gstm%gv_obs_hhmm = inp%bhh * 100 + + gstm%gv_obs_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_obs_latns = 'S' + else + gstm%gv_obs_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_obs_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'W' + else + gstm%gv_obs_lon = int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'E' + endif + + gstm%gv_stdir = istmdir + gstm%gv_stspd = istmspd + + gstm%gv_depth = 'U' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) gstm + endif + + write (67,21) gstm + + 21 format (i10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1,'_',a3,1x,i8,1x + & ,i4.4,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x + & ,i3,4(1x,i4),1x,a1) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(67) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_tracker_mask (masked_outc,lb,ifh,ifcsthour + & ,imax,jmax,iotmret) +c +c ABSTRACT: This subroutine outputs a GRIB record that contains the +c "mask" used to mask out areas surrounding low pressure centers +c that have been found during the search at each forecast hour. This +c mask is written out purely for diagnostic purposes. The GRIB +c identifier given to the mask in the pds is 850 mb height (you can +c make it anything you want). This is only done for the "midlat" +c and "tcgen" cases, since the runs for those cases use a mask while +c the regular "tracker" run (that is, the run which strictly tracks +c only those storms in the TC vitals file) does not. +c +c INPUT: +c masked_outc logical array containing mask +c ifh integer counter for current forecast hour +c ifcsthour integer current forecast hour +c imax num points is i-direction of input grid +c jmax num points is j-direction of input grid +c +c OUTPUT: +c iotmret return code from this subroutine + + implicit none +c + integer ifh,imax,jmax,iotmret,kf,igoret,iix,jjx,ipret + integer ifcsthour + integer kpds(200),kgds(200) + logical(1) masked_outc(imax,jmax),lb(imax,jmax) + real xmask(imax,jmax) +c + if (ifh == 1) then + call baopenw (77,"fort.77",igoret) + print *,'baopenw: igoret= ',igoret + + if (igoret /= 0) then + print *,' ' + print *,'!!! ERROR in sub output_tracker_mask opening' + print *,'!!! **OUTPUT** grib files. baopenw return codes:' + print *,'!!! grib file 1 return code = igoret = ',igoret + STOP 95 + return + endif + endif + + xmask = 0.0 + do jjx = 1,jmax + do iix = 1,imax + if (masked_outc(iix,jjx)) then + xmask(iix,jjx) = 1.0 + else + xmask(iix,jjx) = 0.0 + endif + enddo + enddo + + kf = imax * jmax + +c kpds(5) = 7 +c kpds(6) = 100 +c kpds(7) = 850 +c kpds(22) = 0 + + kpds(1) = 7 ; kpds(2) = 80 + kpds(3) = 255 ; kpds(4) = 192 + kpds(5) = 7 ; kpds(6) = 100 + kpds(7) = 850 ; kpds(8) = 99 + kpds(9) = 7 ; kpds(10) = 20 + kpds(11) = 12 ; kpds(12) = 0 + kpds(13) = 1 ; kpds(14) = ifcsthour + kpds(15) = 0 ; kpds(16) = 10 + kpds(17) = 0 ; kpds(18) = 1 + kpds(19) = 2 ; kpds(20) = 0 + kpds(21) = 20 ; kpds(22) = 0 + kpds(23) = 0 ; kpds(24) = 0 + kpds(25) = 0 + kgds(1) = 0 ; kgds(2) = imax + kgds(3) = jmax ; kgds(4) = -90000 + kgds(5) = 0 ; kgds(6) = 128 + kgds(7) = 90000 ; kgds(8) = 359750 + kgds(9) = 250 ; kgds(10) = 250 + kgds(11) = 64 ; kgds(12) = 0 + kgds(13) = 0 ; kgds(14) = 0 + kgds(15) = 0 ; kgds(16) = 0 + kgds(17) = 0 ; kgds(18) = 0 + kgds(19) = 0 ; kgds(20) = 255 + + write(*,980) kpds(1),kpds(2) + write(*,981) kpds(3),kpds(4) + write(*,982) kpds(5),kpds(6) + write(*,983) kpds(7),kpds(8) + write(*,984) kpds(9),kpds(10) + write(*,985) kpds(11),kpds(12) + write(*,986) kpds(13),kpds(14) + write(*,987) kpds(15),kpds(16) + write(*,988) kpds(17),kpds(18) + write(*,989) kpds(19),kpds(20) + write(*,990) kpds(21),kpds(22) + write(*,991) kpds(23),kpds(24) + write(*,992) kpds(25) + write(*,880) kgds(1),kgds(2) + write(*,881) kgds(3),kgds(4) + write(*,882) kgds(5),kgds(6) + write(*,883) kgds(7),kgds(8) + write(*,884) kgds(9),kgds(10) + write(*,885) kgds(11),kgds(12) + write(*,886) kgds(13),kgds(14) + write(*,887) kgds(15),kgds(16) + write(*,888) kgds(17),kgds(18) + write(*,889) kgds(19),kgds(20) + write(*,890) kgds(21),kgds(22) +c + 980 format('tmow kpds(1) = ',i7,' kpds(2) = ',i7) + 981 format('tmow kpds(3) = ',i7,' kpds(4) = ',i7) + 982 format('tmow kpds(5) = ',i7,' kpds(6) = ',i7) + 983 format('tmow kpds(7) = ',i7,' kpds(8) = ',i7) + 984 format('tmow kpds(9) = ',i7,' kpds(10) = ',i7) + 985 format('tmow kpds(11) = ',i7,' kpds(12) = ',i7) + 986 format('tmow kpds(13) = ',i7,' kpds(14) = ',i7) + 987 format('tmow kpds(15) = ',i7,' kpds(16) = ',i7) + 988 format('tmow kpds(17) = ',i7,' kpds(18) = ',i7) + 989 format('tmow kpds(19) = ',i7,' kpds(20) = ',i7) + 990 format('tmow kpds(21) = ',i7,' kpds(22) = ',i7) + 991 format('tmow kpds(23) = ',i7,' kpds(24) = ',i7) + 992 format('tmow kpds(25) = ',i7) + 880 format('tmow kgds(1) = ',i7,' kgds(2) = ',i7) + 881 format('tmow kgds(3) = ',i7,' kgds(4) = ',i7) + 882 format('tmow kgds(5) = ',i7,' kgds(6) = ',i7) + 883 format('tmow kgds(7) = ',i7,' kgds(8) = ',i7) + 884 format('tmow kgds(9) = ',i7,' kgds(10) = ',i7) + 885 format('tmow kgds(11) = ',i7,' kgds(12) = ',i7) + 886 format('tmow kgds(13) = ',i7,' kgds(14) = ',i7) + 887 format('tmow kgds(15) = ',i7,' kgds(16) = ',i7) + 888 format('tmow kgds(17) = ',i7,' kgds(18) = ',i7) + 889 format('tmow kgds(19) = ',i7,' kgds(20) = ',i7) + 890 format('tmow kgds(20) = ',i7,' kgds(22) = ',i7) +c + print *,'just before call to putgb, kf= ',kf + call putgb (77,kf,kpds,kgds,lb,xmask,ipret) + print *,'just after call to putgb, kf= ',kf + if (ipret == 0) then + print *,' ' + print *,'+++ IPRET = 0 after call to putgb' + print *,' ' + else + print *,' ' + print *,'!!!!!! ERROR: IPRET NE 0 AFTER CALL TO PUTGB !!!' + print *,' ' + endif +c +c bug fix for IBM: flush the output stream so it actually writes + flush(6) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_next_ges (fixlon,fixlat,ist,ifh,imax,jmax + & ,dx,dy,modelid,valid_pt,readflag,maxstorm,istmspd + & ,istmdir,ctype,trkrinfo,ignret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. It does this by using two different +c methods and averaging the results from those two. The +c first method is a simple linear extrapolation made by +c basically drawing a line from the previous position +c through the current fix position and assuming straight +c line motion. The second method is to do a barnes +c smoothing of u & v in the vicinity of the storm at 850, +c 700 & 500 mb to get an average environmental wind +c vector at each level, and then move the storm according +c to the vector at each level. Then a weighted average is +c taken of all these positions from methods 1 & 2 to get +c the consensus for the guess position. NOTE: For a +c regional model and a storm that is relatively close to +c the model boundary, there is a strong possibility that +c the barnes analysis subroutine will fail due to trying +c to access grid points beyond the model's lateral +c boundary. In this case, the redlm & ridlm are halved +c and barnes is called again. If it still fails, then +c just use the result from method 1 as a default. +c +c INPUT: +c fixlon Array with longitudes of fix positions +c fixlat Array with latitudes of fix positions +c ist Storm number currently being processed +c ifh Forecast hour currently being processed +c imax Max number of pts in x-direction for this grid +c jmax Max number of pts in y-direction for this grid +c dx grid-spacing of the model in the i-direction +c dy grid-spacing of the model in the j-direction +c modelid Integer indicating what model's data is being processed +c valid_pt Logical; bitmap indicating if valid data at that pt. +c readflag Logical; Tells whether or not a variable was read in +c for this model +c maxstorm Max # of storms that can be handled in this run +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, eventually +c in the barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c istmspd The speed that the storm would have to move to get from +c the current position to the next guess position +c istmdir The direction in which the storm would have to move to +c get from the current position to the next guess position +c +c LOCAL: +c dt Number of seconds between successive forecast times +c for this particular model. +c dtkm Distance in meters of 1 degree latitude +c icutmax Max number of times to cut the ridlm and redlm in half, +c for use in calling barnes. If you're using a regional +c model and on the first call to barnes you try to access +c a point that's outside the model grid boundary, we'll +c call barnes again, but not before cutting the redlm and +c ridlm in half. icutmax says how many times to allow +c this cutting in half before giving up and just going +c with the extrapolation method. At first writing, we'll +c set icutmax to 2, so that it will allow the ridlm to +c get down to 500 km (originally 2000 km) and the redlm +c to 125 km (originally 500 km). +c *** NOTE: After testing the system, it was found that if +c we cut these radii, the u and v values that are +c calculated from barnes are too strongly influenced by +c the near-storm environment and, especially for asymmetric +c systems, resulted in u and v values being much too strong. +c As such, we will not allow these values to be cut, and if +c we hit the boundaries in barnes, we'll just use the +c extrapolation method, which has seemed to work just fine. +c +c OTHER: (slonfg, slatfg & storm defined in module def_vitals) +c slonfg Array containing first guess longitude positions +c slatfg Array containing first guess latitude positions +c storm Contains tcvitals information +c + USE radii; USE def_vitals; USE set_max_parms; USE grid_bounds + USE tracked_parms; USE level_parms; USE trig_vals; USE trkrparms + USE gen_vitals + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer icutmax,istmspd,istmdir,bskip,ileadtime,ifcsthour + integer ifh,ist,npts,ilonfix,jlatfix,ibeg,jbeg,iend,jend + integer igiret,ignret,icut,iuret,ivret,ibarnct,n,ix1,ix2 + integer icount,imax,jmax,modelid,maxstorm + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real barneswt,extrapwt,dtkm,dt,ucomp,vcomp,xdist,ydist,ydeg + real extraplat,avglat,cosfac,xdeg,extraplon,ylatdegmove_last + real xlondegmove_last,xnumh_last,ylatdegmove_last_perhour + real xlondegmove_last_perhour,xnumh_next,yoldavglat + real yoldcosfac,xdistmove_last,xdistmove_last_perhour + real ynewavglat,ynewcosfac,xdegnew,dx,dy,re,ri,ubar,vbar + real wgttot,uavg,vavg,reold,riold,barnlat,barnlon,wt_total + real tmp_fix_lon_curr,tmp_fix_lon_prev + character*1 :: in_grid, extrap_flag, barnes_flag + character(*) ctype + logical(1) valid_pt(imax,jmax),readflag(14) +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c For updating the first guess, if Method 1 and Method 2 are both +c able to be done, give the following weights to the 2 methods. +c + data barneswt /0.50/, extrapwt /0.50/ +c +c ------------------------------- +c METHOD 1: LINEAR EXTRAPOLATION +c ------------------------------- +c First, just do a simple linear extrapolation from the previous +c fix position through the current fix position. If it's the +c first time (vt=0), then use the storm motion vector and storm +c speed information from the TC Vitals card. +c + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(ist)%tcv_stdir == -99 .or. + & storm(ist)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GET_NEXT_GES, at fcst hour = 0, either ' + print *,'!!! storm motion or storm speed = -99 on TCV card.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(ist)%tcv_stdir + print *,'!!! storm motion speed= ',storm(ist)%tcv_stspd + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + extrap_flag = 'n' + else + ucomp = sin(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + vcomp = cos(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(ist,ifh) + ydeg + avglat = 0.5 * (extraplat + fixlat(ist,ifh)) + if (avglat > 89.5) avglat = 89.0 + if (avglat < -89.5) avglat = -89.0 + cosfac = cos(avglat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(ist,ifh) + xdeg + endif + else + +c Do a simple linear extrapolation of the current motion of the +c storm. Follow a line from the fix position from the last fix +c through the current fix and extrapolate out. To figure out the +c new latitude, just see how many deg lat the storm moved since +c last time and add it to the current fix latitude. To calculate +c the new fix longitude, though, we need to see how many deg lon +c the storm moved since the last time, convert that to the +c distance (km) the storm travelled in the x-direction (at an +c average latitude between the current and previous latitudes), +c and then add that distance on to the current longitude and +c convert that distance to the num of degrees the storm has +c travelled in the x-direction (at an average latitude between +c the current and next(extrap) latitudes). +c +c UPDATE Feb 2009: To account for the possibility of using +c irregularly spaced forecast hours (e.g., 6,10,10.5,...etc), +c I had to modify this linear extrapolation. + + print *,' ' + print *,'xxxx get_next_ges, prev fix lon= ',fixlon(ist,ifh-1) + print *,'xxxx get_next_ges, curr fix lon= ',fixlon(ist,ifh) + print *,' ' + + if (fixlat(ist,ifh-1) > -900.0 .and. + & fixlon(ist,ifh-1) > -900.0) then + + ylatdegmove_last = fixlat(ist,ifh) - fixlat(ist,ifh-1) + + tmp_fix_lon_curr = fixlon(ist,ifh) + tmp_fix_lon_prev = fixlon(ist,ifh-1) + + if (tmp_fix_lon_prev < 0.0 .and. tmp_fix_lon_prev > -25.0) + & then + ! previous lon position is within 25 deg west of the GM + ! and is listed in negative degrees. + if (tmp_fix_lon_curr < 0.0 .and. tmp_fix_lon_curr > -25.0) + & then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 1 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both negative. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr < 25.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 2 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous time' + print *,' is negative, while lon for current time' + print *,' is positive, but within 0-25 deg East.' + print *,' All ok!' + endif + endif + + elseif (tmp_fix_lon_prev > 335.0 .and. + & tmp_fix_lon_prev <= 360.0) then + ! previous lon position is within 25 deg west of the GM + ! and is listed in positive degrees. + if (tmp_fix_lon_curr > 335.0 .and. + & tmp_fix_lon_curr <= 360.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 3 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both positive. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr <= 25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 4 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between 0 & 25. Current tmp_lon' + print *,' has been adjusted to be > 360 for the' + print *,' purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < 0.0 .and. + & tmp_fix_lon_curr >= -25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 5 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is west of the GM and' + print *,' is between 0 & -25. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < -335.0 .and. + & tmp_fix_lon_curr >= -360.0) then + tmp_fix_lon_curr = 720.0 - tmp_fix_lon_curr + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 6 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between -335 & -360. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + + endif + + endif + + xlondegmove_last = tmp_fix_lon_curr - tmp_fix_lon_prev + + xnumh_last = fhreal(ifh) - fhreal(ifh-1) + + ylatdegmove_last_perhour = ylatdegmove_last / xnumh_last + xlondegmove_last_perhour = xlondegmove_last / xnumh_last + + xnumh_next = fhreal(ifh+1) - fhreal(ifh) + + extraplat = fixlat(ist,ifh) + & + (ylatdegmove_last_perhour * xnumh_next) + + yoldavglat = 0.5 * (fixlat(ist,ifh) + fixlat(ist,ifh-1)) + yoldcosfac = cos (dtr * yoldavglat) + xdistmove_last = xlondegmove_last * dtk * yoldcosfac + + xdistmove_last_perhour = xdistmove_last / xnumh_last + + ynewavglat = 0.5 * (extraplat + fixlat(ist,ifh)) + ynewcosfac = cos(dtr * ynewavglat) + xdegnew = (xdistmove_last_perhour * xnumh_next) + & / (dtk * ynewcosfac) + extraplon = tmp_fix_lon_curr + xdegnew + + else + + if ( verb .ge. 3 ) then + print *,' ' + write(6,92) '!!! IN GET_NEXT_GES, at fcst hour = ' + & ,ifhours(ifh),ifclockmins(ifh) + print *,'!!! the lon and lat positions for the previous' + print *,'!!! forecast hour are -999, meaning that this is a' + print *,'!!! new storm, so we cannot use the extrap method.' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + 92 format (1x,a36,i4,':',i2.2) + + extrap_flag = 'n' + + endif + + endif + +c ------------------------------- +c METHOD 2: Barnes analysis +c ------------------------------- +c Do a barnes analysis on the u & v components of the wind near the +c storm to get an average u & v, then advect the storm according to +c the average wind vector obtained. The call to get_ij_bounds is +c needed in order to restrict the number of grid points that are +c searched in the barnes subroutine. See Abstract from this +c subroutine for further details. + + npts = ceiling(ridlm/(dtk*((dx+dy)/2))) + + call get_ij_bounds (npts,0,ridlm,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for ' + print *,'!!! storm number ',ist + endif + + ignret = 92 + return + endif + + if (verb >= 3) then + print *,' ' + print *,' +++ In get_next_ges after call to get_ij_bounds,' + print *,' getting bounds for the barnes analysis...' + print *,' glatmax= ',glatmax,' glatmin= ',glatmin + print *,' glonmax= ',glonmax,' glonmin= ',glonmin + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + print *,' ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + endif + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if ((dx+dy)/2 > 0.20) then + bskip = 1 + else if ((dx+dy)/2 > 0.10 .and. (dx+dy)/2 <= 0.20) then + bskip = 2 + else if ((dx+dy)/2 > 0.05 .and. (dx+dy)/2 <= 0.10) then + bskip = 3 + else if ((dx+dy)/2 > 0.03 .and. (dx+dy)/2 <= 0.05) then + bskip = 5 + else if ((dx+dy)/2 <= 0.03) then + bskip = 10 + endif + +c Calculate average wind at each level (currently: 850, 700 & 500) + + re = redlm + ri = ridlm + icut = 0 + + if (trkrinfo%type == 'midlat') then + icutmax = 2 + else + icutmax = 1 + endif + + radmaxloop: do while (icut <= icutmax .and. in_grid == 'n') + + ubar = 0.0; vbar = 0.0 + iuret = 0; ivret = 0 + wgttot = 0.0 + ibarnct = 0 + barnes_flag = 'n' + + levelloop: do n=1,nlevg + + select case (n) + case (1); ix1=3; ix2=4 ! For 850 mb readflags + case (2); ix1=5; ix2=6 ! For 700 mb readflags + case (3); ix1=12; ix2=13 ! For 500 mb readflags + end select + + if (readflag(ix1) .and. readflag(ix2)) then + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,u(1,1,n),valid_pt + & ,bskip,re,ri,uavg,icount,ctype,trkrinfo,iuret) + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,v(1,1,n),valid_pt + & ,bskip,re,ri,vavg,icount,ctype,trkrinfo,ivret) + + if (iuret /= 0 .or. ivret /= 0) then + +c ...barnes probably tried to access a pt outside the grid +c domain. So, reduce by half the distance from the center +c of the farthest pt that barnes tries to access, exit this +c loop, and try it again with the smaller re and ri. + + iuret = 96; ivret = 96 + reold = re + riold = ri + re = 0.5 * re + ri = 0.5 * ri + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: While attempting to use the barnes ' + print *,'method to update the first guess, the ' + print *,'algorithm tried to access a grid point that ' + print *,'does not have valid data, meaning that too ' + print *,'large a radius is being searched. So, the 2 ' + print *,'radii, re and ri, are being halved and, if the' + print *,'value of icutmax > 0, the algorithm will be ' + print *,'run again. Otherwise, if icutmax = 0, only ' + print *,'the extrapolation method will be used.' + print *,'iuret= ',iuret,' ivret= ',ivret,' icut= ',icut + print *,'Old re = ',reold,' New re = ',re + print *,'Old ri = ',riold,' New ri = ',ri + endif + + exit levelloop + + else + ubar = ubar + wgts(n) * uavg + vbar = vbar + wgts(n) * vavg + wgttot = wgttot + wgts(n) + ibarnct = ibarnct + 1 + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, ix1= ',ix1,' ix2= ',ix2 + print *,' uavg= ',uavg,' vavg= ',vavg + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' n= ',n,' wgts(n)= ',wgts(n),' wgttot= ' + & ,wgttot + print *,' ibarnct= ',ibarnct + print *,' ' + print *,' ' + endif + endif + + endif + + enddo levelloop + + if (ibarnct > 0 .and. wgttot > 0.0) then + barnes_flag = 'y' + in_grid = 'y' + ubar = ubar / wgttot + vbar = vbar / wgttot + barnlat = fixlat(ist,ifh) + (vbar * dt)/dtkm + cosfac = cos (dtr * 0.5 * (fixlat(ist,ifh) + barnlat)) + barnlon = fixlon(ist,ifh) + (ubar * dt)/(dtkm * cosfac) + + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, mean stats follow: ' + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' wgttot= ',wgttot + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' barnlon= ',barnlon,' barnlat= ',barnlat + print *,' dt= ',dt,' dtkm= ',dtkm,' cosfac= ',cosfac + endif + + +c This next if statement says that if we've had to reduce the +c size of the barnes analysis domain twice already, then we've +c only done the analysis on a much smaller area, and this +c doesn't give us as good a picture of the average winds in the +c area of the storm, so reduce the emphasis we place on the +c barnes method. + + if (icut >= 2) barneswt = barneswt / 2. + + else + barnes_flag = 'n' + endif + + icut = icut + 1 + + enddo radmaxloop + +c --------------------- +c Average the results +c --------------------- +c Now do a weighted average of the positions obtained from the +c linear extrapolation and the barnes analysis methods. + + if (extrap_flag == 'y' .and. barnes_flag == 'y') then + wt_total = barneswt + extrapwt + slatfg(ist,ifh+1) = (barneswt * barnlat + extrapwt * extraplat) + & / wt_total + + ! Note that in any of these statements just below, in order for + ! any of these to be > 360, the original fixlon must be close + ! to 360, i.e., in the far eastern part of the grid, as opposed + ! to being in the far western part (e.g., 0-2 deg East or so). + ! Conversely, for any of these to be < 0, the original fixlon + ! must be close to 0, i.e., in the far *western* part of the + ! grid. + +c yyyy + + if (fixlon(ist,ifh) > 330.0) then + + ! In this part of the IF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being 330+, to be consistent with the fixlon for + ! this time. + + if (extraplon > 330. .and. barnlon > 330.) then + + continue ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (extraplon > 330. .and. + & (barnlon >= 0.0 .and. barnlon < 30.)) then + + ! extraplon > 330, but barnlon is in the 0-30 range, so + ! we need to convert the barnlon value to be 360+ + + barnlon = barnlon + 360. + + elseif (extraplon > 330. .and. barnlon < 0.) then + + ! extraplon > 330, but barnlon is < 0, so + ! we need to convert the barnlon value to be positive... + + barnlon = barnlon + 360. + + elseif (barnlon > 330. .and. + & (extraplon >= 0.0 .and. extraplon < 30.)) then + + ! barnlon > 330, but extraplon is in the 0-30 range, so + ! we need to convert the extraplon value to be 360+ + + extraplon = extraplon + 360. + + elseif (barnlon > 330. .and. extraplon < 0.) then + + ! barnlon > 330, but extraplon is < 0, so + ! we need to convert the extraplon value to be positive... + + extraplon = extraplon + 360. + + endif + + elseif (fixlon(ist,ifh) >= 0. and. fixlon(ist,ifh) < 30.0) then + + ! In this part of the ELSEIF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being in the reference of >360 since that is what the + ! code below this is expecting with the computation of + ! slonfg for the next lead time. + + if ((extraplon >= 0. .and. extraplon < 60.) .and. + & (barnlon >= 0. .and. barnlon < 60.)) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon < 0. .and. extraplon > -60.) .and. + & (barnlon < 0. .and. barnlon > -60.)) then + + ! convert extraplon and barnlon to be positive + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon < 0.) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon < 0.) then + + extraplon = extraplon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon > 330.) then + + barnlon = barnlon + 360. + + elseif (barnlon >= 330. .and. extraplon < 60.) then + + extraplon = extraplon + 360. + + elseif (extraplon >= 330. .and. barnlon < 60.) then + + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon > 330.) then + + extraplon = extraplon + 360. + + endif + + else + + continue ! extraplon and barnlon do not need to be modified + ! since there should be no way that a storm + ! currently east of 30E and west of 30W could make + ! it to the Greenwich Mer in one forecast interval + + endif + + print *,' ' + print *,'+++ In get_next_ges, before averaging the 2 methods, ' + print *,' Raw (no conversion for GM wrap) barnlon= ' + & ,barnlon + print *,' Raw (no conversion for GM wrap) extraplon= ' + & ,extraplon + + slonfg(ist,ifh+1) = (barneswt * barnlon + extrapwt * extraplon) + & / wt_total + + if (slonfg(ist,ifh+1) > 360.) then + ! If we've GM-wrapped past 360, adjust it to be 0-360... + slonfg(ist,ifh+1) = slonfg(ist,ifh+1) - 360. + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'y' .and. barnes_flag == 'n') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_next_ges, barnes method was not ' + print *,'!!! done for updating the first guess for this ' + print *,'!!! storm. Only the linear extrapolation method ' + print *,'!!! was used.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + slatfg(ist,ifh+1) = extraplat + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 0.0,0.0,0.0 + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'n' .and. barnes_flag == 'y') then + slatfg(ist,ifh+1) = barnlat + if (barnlon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (barnlon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = barnlon + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 360.-barnlon,barnlat + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges, new position guess not' + print *,'!!! made. Could not get guess using either barnes' + print *,'!!! method or extrapolation method.' + print *,'!!! extrap_flag = ',extrap_flag + print *,'!!! barnes_flag = ',barnes_flag + print *,'!!! Storm number = ',ist,' ifh = ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + write (6,41) 0.0,0.0,0.0 + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 95 + endif + + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| Current fix & updated fix positions |' + print *,'-------------------------------------------------- ' + print *,'| In get_next_ges, current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',ist + print *,'| Return code from get_next_ges = ',ignret + print *,'| Storm Name = ',storm(ist)%tcv_storm_name + print *,'| Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + write (6,21) fixlat(ist,ifh) + write (6,23) 360.-fixlon(ist,ifh),fixlon(ist,ifh) + write (6,25) slatfg(ist,ifh+1) + write (6,27) 360.-slonfg(ist,ifh+1),slonfg(ist,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current fix lat is ',f7.2) + 23 format (' | Current fix lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') +c 41 format (' --- barnlon= ',f7.2,'W barnlat= ',f7.2) +c 43 format (' --- extraplon= ',f7.2,'W extraplat= ',f7.2) + + 41 format (' --- barnlon= ',f7.2,'E (',f7.2 + & ,'W) barnlat= ',f7.2) + 43 format (' --- extraplon= ',f7.2,'E (',f7.2 + & ,'W) extraplat= ',f7.2) + +c Now calculate the speed that the storm would have to move at in +c order to make it to the next forecast position. We will use +c this information in writing out the "gen_vitals" record, if this +c is requested. + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh) + & ,slonfg(ist,ifh+1),slatfg(ist,ifh+1),dist,degrees) + + ! convert distance from km to meters, then get speed in m/s. + + distm = dist * 1000. + stmspd = distm / dt + istmspd = int ((stmspd * 10) + 0.5) + + xincr = slonfg(ist,ifh+1) - fixlon(ist,ifh) + yincr = slatfg(ist,ifh+1) - fixlat(ist,ifh) + + if ( verb .ge. 3 ) then + print *,'iocheck, dist= ',dist,' distm= ',distm + print *,'iocheck, stmspd= ',stmspd,' istmspd= ',istmspd + print *,'iocheck, xincr= ',xincr,' yincr= ',yincr + endif + + if (xincr < 0.0 .and. slonfg(ist,ifh+1) < 30.0 .and. + & fixlon(ist,ifh) > 300.0) then + ! This means we have a storm moving east across the GM, and + ! so we are subtracting, for example, something like + ! 0.5 - 359.5, so redo xincr, but add 360 to slonfg first... + xincr = (slonfg(ist,ifh+1) + 360.0) - fixlon(ist,ifh) + else if (xincr > 300.0) then + ! This means we have a storm moving west across the GM, and + ! so we are subtracting, for example, something like + ! 359.5 - 0.5, so redo xincr, but add 360 to fixlon first... + xincr = slonfg(ist,ifh+1) - (fixlon(ist,ifh) + 360.0) + endif + + if (xincr == 0.0) then + if (yincr == 0.0) then + stmdir = 0.0 + else if (yincr > 0) then + stmdir = 360.0 + else if (yincr < 0) then + stmdir = 180.0 + endif + else if (xincr > 0.0) then + if (yincr == 0.0) then + stmdir = 90.0 + else + arct = atan(yincr/xincr) + stmdir = 90. - arct / dtr + endif + else if (xincr < 0.0) then + if (yincr == 0.0) then + stmdir = 270.0 + else + arct = atan(yincr/xincr) + stmdir = 270. - arct / dtr + endif + endif + + istmdir = int (stmdir + 0.5) + if (istmdir > 360) then + istmdir = 360 + else if (istmdir < 0) then + istmdir = 0 + endif + + if ( verb .ge. 3 ) then + print *,'iocheck, stmdir= ',stmdir,' istmdir= ',istmdir + endif + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine advect_tcvitals_from_hour0 (fixlon,fixlat,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. As of 11/2016, it is called only for the case in +c which we've got NetCDF data and no hour0 data, and so we want to +c simply take the TC Vitals data and advect the current position to +c a position at the next lead time. We can't use the code in +c subroutine get_next_ges because there are certain allocatable +c arrays in that subroutine that need to have been allocated first, +c and at this point prior to the first lead time in hour0, they +c haven't been allocated. +c +c INPUT: +c inctcv Index for storm number currently being processed +c ifh Forecast hour currently being processed +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c iatret Return code from this subroutine +c + USE def_vitals; USE trkrparms; USE tracked_parms + USE verbose_output; USE trig_vals; USE set_max_parms + USE gen_vitals + + type (trackstuff) trkrinfo + integer iatret,inctcv + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real ucomp,vcomp,xdist,ydist,ydeg,dt,extraplat + real cosfac + real dtkm +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c ------------------------------------------------------------------ +c Using the storm motion vector and storm translation speed as read +c from the TC Vitals card, do a simple linear extrapolation from the +c current observed (TC Vitals) position and advect the storm to a +c position at the next lead time. +c ------------------------------------------------------------------ + + iatret = 0 + + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(inctcv)%tcv_stdir == -99 .or. + & storm(inctcv)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In advect_tcvitals_from_hour0, at fcst hour= 0' + print *,'!!! either storm motion or storm speed = -99 on ' + print *,'!!! TCV card, ist= inctcv= ',inctcv,' ifh= ',ifh + print *,'!!! Storm name = ',storm(inctcv)%tcv_storm_name + print *,'!!! Storm ID = ',storm(inctcv)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(inctcv)%tcv_stdir + print *,'!!! storm motion speed= ',storm(inctcv)%tcv_stspd + print *,'... CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS ...' + print *,' ' + print *,'... Instead, we will simply use the current ' + print *,'... observed position from TC Vitals and hope that' + print *,'... it is close enough at the next lead time for ' + print *,'... the tracker to be able to still track it.' + print *,' ' + endif + extraplat = slatfg(inctcv,ifh) + extraplon = slonfg(inctcv,ifh) + else + ucomp = sin(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + vcomp = cos(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(inctcv,ifh) + ydeg + cosfac = cos(extraplat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(inctcv,ifh) + xdeg + endif + else + print *,' ' + print *,'!!! ERROR: In advect_tcvitals_from_hour0, the value of' + print *,' ifh is > 1, and this routine should only be called' + print *,' if ifh=1 (i.e., for hour0). STOPPING....' + print *,' ' + stop 95 + endif + + slatfg(inctcv,ifh+1) = extraplat + + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon >360 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon < 0 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + else + slonfg(inctcv,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| In advect_tcvitals_from_hour0, info on the ' + print *,'| positions for the current and next lead times ' + print *,'| follow: ' + print *,'-------------------------------------------------- ' + print *,'| current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',inctcv + print *,'| Return code from advect_tcvitals_from_hour0= ',iatret + print *,'| Storm Name = ',storm(inctcv)%tcv_storm_name + print *,'| Storm ID = ',storm(inctcv)%tcv_storm_id + write (6,420) gstorm(inctcv)%gv_gen_date + & ,gstorm(inctcv)%gv_gen_fhr + & ,gstorm(inctcv)%gv_gen_lat + & ,gstorm(inctcv)%gv_gen_latns,gstorm(inctcv)%gv_gen_lon + & ,gstorm(inctcv)%gv_gen_lonew,gstorm(inctcv)%gv_gen_type + write (6,21) fixlat(inctcv,ifh) + write (6,23) 360.-fixlon(inctcv,ifh),fixlon(inctcv,ifh) + write (6,25) slatfg(inctcv,ifh+1) + write (6,27) 360.-slonfg(inctcv,ifh+1),slonfg(inctcv,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current TC Vitals lat is ',f7.2) + 23 format (' | Current TC Vitals lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') + + + return + end +c +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getradii (xcenlon,xcenlat,imax,jmax,dx,dy,valid_pt + & ,cstormid,ifcsthr,vmaxwind,vradius,trkrinfo + & ,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) +c +c ABSTRACT: This subroutine looks through the wind data near an +c input storm center (fixlon,fixlat) and gets the radii of various +c surface winds in each of the 4 storm quadrants (NE,NW,SE,SW). +c The wind thresholds that are sought are gale force (34kt|17.5m/s), +c storm force (50kt|25.7m/s), and hurricane force (64kt|32.9m/s). +c This subroutine calls the Cray subroutine orders, which is a +c Cray-optimized sort routine. +c +c UPDATE (AUG 2001): The Cray subroutine orders was ported to the +c SP by NCEP personnel. On the SP version, some changes were +c apparently made so that the size of the arrays for calling +c arguments 2, 3 and 4 (iwork, dtemp and isortix in my calling +c routine) must be the same. This was not the case on the Crays, +c and this was causing the tracker to crash for cases far north +c on fine grids (GFDL 1/3 grid). +c +c UPDATE (AUG 2012): The call to the Cray subroutine orders was +c replaced with a call to qsort, which uses a quicksort sorting +c algorithm. While this is not the fastest sorting routine out +c there, we don't do a lot of sorting here, and qsort is simple +c and it is portable. +c +c UPDATE (April 2013): For the radii, we encountered a problem with +c radmax being too small. It was set at 650 km. Hurricane Sandy +c exceeded this in the models, so the values returned from getradii +c were close to the default radmax value of 650 km (350 nm), instead +c of much higher as they should have been. To fix it, we now use an +c iterative technique, where we start with radmax as a small value +c (450 km). If getradii returns a value for R34 in a quadrant that +c does not exceed 0.97*radmax, then that value is ok. If it does +c exceed 0.97*radmax, then we bump up radmax by 50 km and call +c getradii again, looking to diagnose radii only in those quadrants +c where the need_to_expand_r34 flag = 'n'. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c cstormid 3-character storm ATCF ID (e.g., 03L, 11E, etc) +c ifcsthr integer value for current forecast hour +c trkrinfo derived type containing various info on the storm +c need_to_expand_r34 1-character array that specifies which of the +c 4 quadrants still need to be expanded on this time +c through getradii in order to get an R34 value that is +c not right at the outermost boundary. +c vmaxwind max wind (in m/s) that was reported from the +c get_max_wind subroutine +c radmax input max radius (km) that will be used for this +c iteration of getradii. +c first_time_thru_getradii logical flag. It is used so that any +c checking for 50- or 64-kt radii is only done on the +c first time through getradii. Only the checking for +c 34-kt radii is done on multiple iterations. +c igrct integer that indicates what iteration of getradii this +c call is. +c +c OUTPUT: +c +c igrret return code from this subroutine +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c +c LOCAL: +c +c radmax the maximum radius to look for winds for the various +c thresholds. +c quadinfo This array contains the magnitude of the near-surface +c winds and the distance from the gridpoint to the fix +c position for each point in each quadrant that is within +c the maximum allowed radius, radmax. quadinfo is +c allocated within this subroutine, and is allocated as +c (quadrant, num_pts_in_quadrant, data_type), where +c data_type is either windspeed(1) or distance(2) from +c storm center to grid point. +c quadmax This array contains the max surface wind in each +c quadrant, plus the location of it and the distance from +c the storm center. This information is critical to +c identifying when this subroutine is malfunctioning. + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE level_parms + USE trkrparms + USE verbose_output + +c + type (trackstuff) trkrinfo +c + logical(1) valid_pt(imax,jmax) + logical(1) first_time_thru_getradii +c dimension iwork(257) + real, allocatable :: quadinfo(:,:,:),iwork(:) + real quadmax(4,4) + real exactdistnm,exactdistkm,radmax,degrees,cosarg + real rlonb,rlonc,rlatb,rlatc,vmaxwind + real pt_heading_rad,pt_heading,d + integer, allocatable :: isortix(:) + integer iwindix,ipoint,ifcsthr,igrct + integer quadct(4),vradius(3,4) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: dtemp(:) + real :: windthresh(3) = (/17.5,25.7,32.9/) + character cstormid*3 + character :: need_to_expand_r34(4)*1 + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *************************************************** ' + print *,' AT BEGINNING OF GETRADII, input radmax= ',radmax + print *,' *************************************************** ' + print *,' ' + print *,'xcenlon= ',xcenlon,' xcenlat= ',xcenlat + print *,'imax= ',imax,' jmax= ',jmax,' dx= ',dx,' dy= ',dy + endif + + igrret = 0 + +c ----------------------------------------------------------- +c PART 1: Define the maximum radius for which you'll search +c for the wind values, and then get the beginning and ending +c i and j points for that sub-region to search. Define this +c maximum radius (radmax) in terms of km. +c ----------------------------------------------------------- + +c radmax = 650.0 ! This value is in units of km. With April 2013 +c ! update, this is now defined in calling routine + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-A....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine getradii' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-B....' + stop 98 + endif + + igrret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmax is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmax/(dtk*dx))/cosfac) + numjpts = ceiling(radmax/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (ibeg < 1) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-C...' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jbeg < 1) jbeg = 1 + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in getradii calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Wind radii will not be calculated for this time.' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-D....' + stop 98 + endif + + igrret = 99 + return + endif + + if (iend > imax) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-E....' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getradii, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + +c ----------------------------------------------------------- +c PART 2: Within the area of grid points defined by jbeg, +c jend, ibeg and iend, (1) calculate all the wind speeds at +c each grid point, (2) calculate all of the distances from +c each grid point to the storm center, (3) assign each grid +c point to one of the 4 quadrants (NE,NW,SE,SW), (4) in each +c quadrant, sort the points, based on windspeed. +c ----------------------------------------------------------- + + jnum = jend - jbeg + 1 + inum = iend - ibeg + 1 +c numalloc = ((jnum * inum) / 2) + inum/2 + jnum/2 + numalloc = jnum * inum + inum/2 + jnum/2 + + if ( verb .ge. 3 ) then + print *,'in getradii, numalloc= ',numalloc,' radmax= ',radmax + endif + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + allocate (quadinfo(4,numalloc,2),stat=iqa) + + if (iqa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub getradii allocating quadinfo array.' + print *,'!!! iqa = ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-F....' + stop 98 + endif + + igrret = 94 + return + endif + + quadct = 0 + +c Calculate the distances and wind speeds at each grid point. If +c the distance is < radmax, include that wind info in the +c appropriate quadinfo array location for that quadrant. + + quadmax = 0.0 + + jloop: do j=jbeg,jend + iloop: do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point in question = ',i + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-G...' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub getradii' + print *,'!!! for a non-global grid. i= ',i + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-H....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + if (dist > radmax) cycle iloop + + if (valid_pt(ip,j)) then + + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + +cc print *,'i= ',i,' j= ',j,' dist= ',dist,' vmag= ',vmag + + ! Calculate the angle from the center point to this point + ! and then assign this point to the appropriate quadrant bin + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-xcenlon) * dtr + rlatb = xcenlat * dtr + d = degrees * dtr + +c write (6,59) 360.-xcenlon,xcenlat,360.-glon(ip),glat +c +c write (6,61) d/dtr,rlatc/dtr,360.-(rlonc/dtr),rlatb/dtr +c & ,360.-(rlonb/dtr),sin(rlatc),sin(rlatb),cos(d) +c & ,sin(d),cos(rlatb) +c +c +c 59 format (1x,'+++ gr, xcenlon= ',f8.3,'W xcenlat= ' +c & ,f8.3,' glon= ',f8.3,'W glat= ',f8.3) +c +c 61 format (1x,'+++ gr, d rlatc rlonc rlatb rlonb= ',5f9.4 +c & ,' sin(rlatc)= ',f8.6,' sin(rlatb)= ',f8.6 +c & ,' cos(d)= ',f8.6,' sin(d)= ',f8.6 +c & ,' cos(rlatb)= ',f8.6) + + if (d == 0.0) then + + pt_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d)) / + & (sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_heading_rad = acos(cosarg) + else + pt_heading_rad = 2*pi - acos(cosarg) + endif + + pt_heading = pt_heading_rad / dtr + + endif + + if (pt_heading >= 0.0 .and. pt_heading < 90.) then + ! NE quadrant + iq = 1 + else if (pt_heading >= 90.0 .and. pt_heading < 180.) then + ! SE quadrant + iq = 2 + else if (pt_heading >= 180.0 .and. pt_heading < 270.) then + ! SW quadrant + iq = 3 + else if (pt_heading >= 270.0 .and. pt_heading <= 360.) then + ! NW quadrant + iq = 4 + endif + +c write (6,73) xcenlat,360.-xcenlon,j,i,ip,glat(j) +c & ,360.-glon(ip),pt_heading,iq + + 73 format (1x,'+++ getradii clat clon: ',f6.2,' ',f7.2,'W',3i4 + & ,' plat plon: ',f6.2,' ',f7.2,'W Dir: ',f7.2 + & ,' Quad: ',i2) + + quadct(iq) = quadct(iq) + 1 + quadinfo(iq,quadct(iq),1) = vmag + quadinfo(iq,quadct(iq),2) = dist + if (vmag > quadmax(iq,4)) then + quadmax(iq,1) = glon(ip) + quadmax(iq,2) = glat(j) + quadmax(iq,3) = dist + quadmax(iq,4) = vmag + endif + + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After loop, quadct(1)= ',quadct(1),' quadct(2)= ' + & ,quadct(2) + print *,' quadct(3)= ',quadct(3),' quadct(4)= ' + & ,quadct(4) + print *,' ' + + write (6,110) cstormid,ifcsthr,'NE',quadmax(1,1),quadmax(1,2) + & ,quadmax(1,3)*0.539638,quadmax(1,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SE',quadmax(2,1),quadmax(2,2) + & ,quadmax(2,3)*0.539638,quadmax(2,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SW',quadmax(3,1),quadmax(3,2) + & ,quadmax(3,3)*0.539638,quadmax(3,4)*1.9427 + write (6,110) cstormid,ifcsthr,'NW',quadmax(4,1),quadmax(4,2) + & ,quadmax(4,3)*0.539638,quadmax(4,4)*1.9427 + print *,' ' + + 110 format (' quadmax: ',a3,1x,i3.3,1x,a2,1x,' lon: ',f6.2,'E',1x + & ,' lat: ',f6.2,' radius: ',f7.2,' nm',2x,' vmag: ' + & ,f6.2,' kts') + endif + +c Now go through each quadrant and put the wind speed distance info +c into a temporary array (dtemp), sort that array, and then scan +c through that array to find the various thresholds. + + quadrantloop: do k=1,4 + + if (need_to_expand_r34(k) == 'y') then + print *,'---> R34 search underway for quadrant ',k + & ,' radmax= ',radmax + continue + else + print *,'+ R34 okay for quadrant ',k,'... skipping...' + cycle quadrantloop + endif + + if (allocated(isortix)) deallocate (isortix) + if (allocated(dtemp)) deallocate (dtemp) + if (allocated(iwork)) deallocate (iwork) + allocate (isortix(quadct(k)),stat=iisa) + allocate (dtemp(quadct(k)),stat=idta) + allocate (iwork(quadct(k)),stat=iwa) + if (iisa /= 0 .or. idta /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii allocating isortix, dtemp' + print *,'!!! or iwork array for quadrant= ',k + print *,'!!! iisa = ',iisa,' idta= ',idta,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-I....' + stop 98 + endif + + itret = 94 + return + endif + +c ------------------- + + do m=1,quadct(k) + dtemp(m) = quadinfo(k,m,2) + enddo + + imode = 2 + isortix = 0 + + call qsort (dtemp,isortix,quadct(k)) + +ccccc call orders (imode,iwork,dtemp,isortix,quadct(k),1,8,1) +cccc call orders_4byte (imode,iwork,dtemp,isortix +cccc & ,quadct(k),1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' +c ************************************************************** +c--- mf 20100609 +c CAUSE OF SEG FAULT!!!!!!!! -- not sure still an issue if dtemp +c properly allocated +c + !print *,' dtemp(isortix(1)) = ',dtemp(isortix(1)) + print *,' dtemp(isortix(quadct(k)))= ' + & ,dtemp(isortix(quadct(k))) + print *,' isortix(1) = ',isortix(1) + print *,' isortix(quadct(k)) = ',isortix(quadct(k)) + endif + +c ! Uncomment these next lines to see a listing in the output of +c ! all wind values & distances in this quadrant less than radmax +c do iqq = 1,quadct(k) +c print *,' iqq= ',iqq,' vmag= ',quadinfo(k,isortix(iqq),1) +c & ,' dist= ',quadinfo(k,isortix(iqq),2) +c enddo + +c ------------------- + + if (quadct(k) < 2) then ! not enough members in array + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GETRADII, NOT ENOUGH MEMBERS IN ARRAY FOR' + print *,'!!! QUADRANT #',k,' .... # members = quadct(k)= ' + & ,quadct(k) + print *,'!!! SETTING ALL VRADII = 0 for quadrant = ',k + endif + + vradius(1,k) = 0 + vradius(2,k) = 0 + vradius(3,k) = 0 + cycle quadrantloop + endif + +c Within this quadrant, go through the sorted array of wind +c magnitudes and compare those wind values against the set +c wind thresholds to get the wind radii. The array has +c been sorted by distance from the storm center in order of +c closest (ipoint=1) to farthest (ipoint=quadct(k)). We +c analyze these wind values by starting at the farthest +c point and moving inward until we hit a point that has a +c wind value of at least 34-knot winds (17.5 m/s). When +c we find that point, we interpolate between that point and +c the next farthest out point to get the distance that would +c be for the exact 17.5 m/s value. We then continue searching +c through the wind values down closer to the storm center to +c see if we can find values for the 50- and 64-knot winds. + + iwindix = 1 + ipoint = quadct(k) + 1 + +c print *,'drp: quad= ',k,' quadct= ',quadct(k) + + threshloop: do while (iwindix <= 3 .and. ipoint > 1) + + if (iwindix > 1) then + if (first_time_thru_getradii) then + + ! We are only doing the wind radii for 50 and 64 kts on + ! the first time through subroutine getradii (we only + ! need to do the multiple call iterations for 34 kts). + ! + ! Make sure vmax for this lead time exceeds the radii + ! threshold being diagnosed. The check below avoids, + ! for example, reporting 50-kt wind radii when the max + ! wind diagnosed was only 44 kts. This can happen since + ! the radius for searching for radii is larger than the + ! radius for searching for the max wind. + if (vmaxwind >= windthresh(iwindix)) then + if (verb >= 3) then +c print *,' ' +c print *,' +++ vmaxwind of ',vmaxwind,' m/s exceeds' +c print *,' +++ threshold of ',windthresh(iwindix) +c print *,' +++ (m/s), so radii checking will continue' +c print *,' +++ for this threshold.' +c print *,' +++ igrct= ',igrct,' ipoint= ',ipoint +c & ,' iwindix= ',iwindix + continue + endif + continue + else + if (verb >= 3) then + print *,' ' + print *,' --- vmaxwind of ',vmaxwind,' m/s does NOT' + print *,' - - exceed threshold of ' + & ,windthresh(iwindix) + print *,' - - (m/s), so radii checking will NOT be ' + print *,' - - performed for this threshold.' + endif + iwindix = iwindix + 1 + cycle threshloop + endif + else + iwindix = iwindix + 1 + cycle threshloop + endif + endif + + ipoint = ipoint - 1 + + if (quadinfo(k,isortix(ipoint),1) < windthresh(iwindix)) then + cycle threshloop + else + if (ipoint == quadct(k)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In getradii, a max wind radius was' + print *,'!!! found at the maximum radius checked, so ' + print *,'!!! you may want to make sure that you are' + print *,'!!! checking at a far enough distance from ' + print *,'!!! the fix position, that is, you may want to' + print *,'!!! increase the value of radmax in subroutine' + print *,'!!! getradii. Currently, radmax (km) = ',radmax + print *,'!!! iwindix = ',iwindix,' quadrant= ',k + endif + + vradius(iwindix,k) = int( ((quadinfo(k,isortix(ipoint),2) + & * 0.5396) / 5.0) + 0.5) * 5 + else + +c Interpolate between the 2 closest distances to each wind +c threshold to get "exact" distance to that wind threshold +c radius, convert from km to nm, and then round to the +c nearest 5 nm (since TPC uses this precision). +c 7/23/98 UPDATE: Jim Gross has asked that values not be +c rounded to the nearest 5 nm, but rather only to the +c nearest 1 nm. + + exactdistkm = quadinfo(k,isortix(ipoint),2) + + & ( (quadinfo(k,isortix(ipoint),1) - windthresh(iwindix)) / + & (quadinfo(k,isortix(ipoint),1) - + & quadinfo(k,isortix(ipoint+1),1)) * + & ( (quadinfo(k,isortix(ipoint+1),2) - + & quadinfo(k,isortix(ipoint),2)) ) ) + + exactdistnm = exactdistkm * 0.5396 ! Convert km to nm + vradius(iwindix,k) = int(exactdistnm + 0.5) + +cc vradius(iwindix,k) = int( (exactdistnm / 5.0) + 0.5) * 5 + + + if ( verb .ge. 3 ) then + print *,'iwindix= ',iwindix,' exactdistnm = ' + & ,exactdistnm + print *,'vradius(iwindix,k) =',vradius(iwindix,k) + endif + + endif + +c The possibility exists, especially for coarse output +c grids, that there could be a jump over more than 1 wind- +c thresh category when going from 1 grid point to the next, so +c we need to account for this. For example, if 1 point has +c vmag = 15 m/s and the next point closer in has vmag = 28 +c m/s, then between those 2 points you have the thresholds +c for gale force AND storm force winds, so to be safe, we +c actually need to add 1 to ipoint and re-check the current +c point, if the wind value at that point is found to be +c greater than a wind threshold value (which it has if you've +c gotten to this point in threshloop). + + ipoint = ipoint + 1 + + iwindix = iwindix + 1 + + endif + + enddo threshloop + + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (idta /= 0 .or. iisa /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating isortix or' + print *,'!!! dtemp or work for quadrant= ',k + print *,'!!! idta= ',idta,' iisa= ',iisa,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-J....' + stop 98 + endif + + itret = 94 + return + endif + + enddo quadrantloop + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating quadinfo array.' + print *,'!!! iqa= ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-K....' + stop 98 + endif + + itret = 94 + return + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_max_wind (xcenlon,xcenlat,imax,jmax,dx,dy + & ,valid_pt,levsfc,vmax,trkrinfo,rmax,igmwret) +c +c ABSTRACT: This subroutine looks for the maximum near-surface wind +c near the storm center. This subroutine is only concerned with the +c value of the max wind, NOT where it's located radially with +c respect to the center. The value that's returned in vmax is the +c max wind speed in m/s, which are the units the data are stored in. +c However, when the max wind values are output in output_atcf, they +c will be converted from m/s to knots. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c levsfc integer holding the value of the array member that holds +c the near-surface winds in the u and v arrays (at orig +c writing, it's = 4). +c +c OUTPUT: +c +c vmax value of maximum near-surface wind near the storm ctr +c rmax radius of max winds +c igmwret return code from this subroutine +c +c LOCAL: +c +c radmaxwind the maximum radius to look for a max wind near the +c storm center. You have to allow this to be bigger for +c model grids with coarse resolution (ECMWF 2.5 degree). + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real radmaxwind,degrees,dx,dy,rmax + logical(1) valid_pt(imax,jmax) +c + igmwret = 0 + rmax = -99.0 + + if ((dx+dy)/2. <= 1.25) then + if ((dx+dy)/2. <= 0.25) then + radmaxwind = 300.0 + else + radmaxwind = 300.0 + endif + else + radmaxwind = 500.0 + endif + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmaxwind is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmaxwind/(dtk*dx))/cosfac) + numjpts = ceiling(radmaxwind/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_max_wind calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Value of vmax will be set to 0 for this time.' + endif + + vmax = 0.0 + igmwret = 99 + return + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + + print *,' ' + print *,'In get_max_wind, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + + vmax = 0.0 + do j=jbeg,jend + do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point = ',i + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + + if (dist > radmaxwind) cycle + + if (valid_pt(ip,j)) then + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + if (vmag > vmax) then + vmax = vmag + rmax = dist * 0.539638 ! convert from km to nm + endif + endif + + enddo + enddo + + if ( verb .ge. 3 ) then + print *,'At end of get_max_wind, vmax= ',vmax,' rmax= ',rmax + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine fixcenter (clon,clat,ist,ifh,calcparm,geslon,geslat + & ,inp,stderr,fixlon,fixlat,xvalues,maxstorm,ifret) +c +c ABSTRACT: This subroutine loops through the different parameters +c for the input storm number (ist) and calculates the +c center position of the storm by taking an average of +c the center positions obtained for those parameters. +c First we check to see which parameters are within a +c max error range (errmax), and we discard those that are +c not within that range. Of the remaining parms, we get +c a mean position, and then we re-calculate the position +c by giving more weight to those estimates that are closer +c to this mean first-guess position estimate. +c +c INPUT: +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c geslon Initial guess longitude for this storm at this fcst hour +c geslat Initial guess latitude for this storm at this fcst hour +c inp contains the input date and model number information +c xvalues The actual max or min data values for each parameter +c maxstorm max # of storms to be handled in this run +c +c INPUT/OUTPUT: +c stderr Standard deviation of the position "error" of the parms +c relative to the guess storm position. As long as the +c distance of a parm center to the guess center is <= +c errpmax, it is included in the std dev calculation. +c +c OUTPUT: +c fixlon Best approximation of storm center's longitude +c fixlat Best approximation of storm center's latitude +c ifret Return code from this subroutine +c +c LOCAL: +c storm Contains tcvitals info for the storms (def_vitals) +c trkerr_avg Sum/avg of the track errors for all parms for this +c fcst hour, regardless of whether or not the error was +c > errmax. It's used for getting the std deviation of +c the position error for this forecast time, to be used +c as part of the errmax calculation for the next fcst +c time. +c iclose Number of parameters whose position estimates are +c found to be within a distance errmax of the guess pos +c wtpos The weight given to each position estimate. It's +c based on the distance from the average position. +c errdist The "error" of the parameter center position relative +c to the storm's guess position. +c avgerr Average "error" of the parameter center positions +c relative to the storm's guess position. +c use4next Logical; If a parm center has been calculated but its +c distance from the guess position is > errmax, we don't +c use this center in calculating the new guess position, +c however we will use this position in calculating the +c standard deviation of the current time's guess +c positions, to be used in calculating the new errmax +c for the next forecast time. So in this subroutine, +c calcparm may be set to FALSE if errdist > errmax, but +c use4next will not be set to FALSE (Actually, it is +c only set to FALSE if errdist > errpmax, which is +c defined in error_parms and is roughly 600km). +c stderr_close Standard deviation of position errors for parms that +c have center estimates that are within a distance +c errmax of the guess position. +c clon_fguess These are the first-guess mean position estimates, +c clat_fguess which are the means of the position estimates that +c are within a distance errmax. These first-guess mean +c positions will be refined by giving more weight to +c individual parameter estimates that are closer to +c this first-guess mean position. +c dist_from_mean Contains the "error" distance of each parameter +c from the first-guess mean position (clon_fguess, +c clat_fguess). NOTE: If a parameter is not within +c a distance errmax of the guess position for this +c time (geslon,geslat), then there will be NO +c dist_from_mean calculated for that parm. +c + USE error_parms; USE set_max_parms; USE inparms; USE def_vitals + USE atcf; USE gen_vitals; USE tracked_parms + USE verbose_output + + type (datecard) inp + + real clon(maxstorm,maxtime,maxtp),temp_clon(maxtp) + real clat(maxstorm,maxtime,maxtp),temp_clat(maxtp) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real trkerr(maxtp),errdist(maxtp),xvalues(maxtp) + real stderr(maxstorm,maxtime),devia(maxtp),wtpos(maxtp) + real dist_from_mean(maxtp) + real degrees,errtmp + integer gt345_ct,lt15_ct + logical(1) calcparm(maxtp,maxstorm),use4next(maxtp) + character charparm(maxtp)*8,charmaxmin(maxtp)*8 +c + data charparm /'zeta 850','zeta 700','circ 850','NOT USED' + & ,'circ 700','NOT USED',' gph 850',' gph 700',' MSLP' + & ,'circ sfc','zeta sfc',' thk 5-8',' thk 2-5',' thk 2-8'/ + data charmaxmin /' Max ',' Max ',' Min ','NOT USED' + & ,' Min ','NOT USED',' Min ',' Min ',' Min ' + & ,' Min ',' Max ',' Max ',' Max ',' Max '/ +c + ifret=0 +c +c We need to judge whether each parameter position is reasonable, +c so we'll check to make sure that the dist from each parameter's +c estimate to the guess position is less than a maximum allowable +c error. If it's the first forecast time, use the initial error max +c (defined as errinit in error_parms) as errmax. Otherwise, the +c max error criterion is that the distance error must not exceed 3 +c times the previous forecast time's standard deviation (after a +c small growth factor has been applied). +c UPDATE 3/5/98: During testing, it was found that just using the +c previous time's stdev made errmax too "jumpy" (i.e., at vt=48h, +c errmax could = 380, and then at vt=54h, errmax could jump down +c to 190, so we've changed it so that it uses an average of the +c stdev's from the 3 previous forecast times to maintain some +c continuity between successive forecast times). +c + if (ifh == 1) then + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR' ) then + errmax = err_gfs_init + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errmax = err_ecm_max + errinit = err_ecm_max + else + errmax = err_reg_init + errinit = err_reg_init + endif + else + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR') then + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errinit = err_ecm_max + else + errinit = err_reg_max + endif + + if (ifh >= 4) then + xavg_stderr = (stderr(ist,ifh-3) + stderr(ist,ifh-2) + & + stderr(ist,ifh-1)) / 3.0 + else if (ifh == 3) then + xavg_stderr = (stderr(ist,ifh-2) + stderr(ist,ifh-1)) / 2.0 + else if (ifh == 2) then + xavg_stderr = stderr(ist,ifh-1) + endif + +c The following errmax statement was replaced by the ensuing 4 +c lines due to a compiler bug on some other platforms: +c errmax = amin1(amax1(3.0*xavg_stderr*errpgro,errinit) +c & ,errpmax) + + errtmp = 3.0*xavg_stderr*errpgro + errmax = max(errtmp,errinit) + errtmp = errpmax + errmax = min(errmax,errtmp) + + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (ifh > 1) then + print '(a42,f8.2,a15,f8.2)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = ' + & ,stderr(ist,ifh-1),' xavg_stderr= ',xavg_stderr + else + print '(a45,a18)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = N/A' + & ,' xavg_stderr= N/A' + endif + print *,'At beg of fixcenter, errpgro = ',errpgro + print *,'At beg of fixcenter, errinit = ',errinit + print *,'At beg of fixcenter, errpmax = ',errpmax + print *,'At beg of fixcenter, ifh= ',ifh,' errmax= ',errmax + endif + + trkerr_avg = 0.0 + iclose = 0; itot4next = 0 + clonsum = 0.0; clatsum = 0.0 + errdist = 0.0 + use4next = .FALSE. + gt345_ct = 0 + lt15_ct = 0 + +c For each parm, check to see if the estimated center is within +c distance errmax of the guess center. If it's within errmax, +c then use that parm for locating the center. If it's NOT +c within errmax, but IS within errpmax, then we still use this +c in calculating the standard deviation of the parameters for +c helping to determine the errmax for the next forecast hour. + +c OLD NOTE: For calculating the std dev to be used for the next +c OLD forecast hour, do NOT use vmag 850, vmag 700 or vmag sfc, since +c OLD those parms are always guaranteed to be within a short range of +c OLD the guess, due to the nature of the algorithm (see subroutine +c OLD get_uv_center for further details on that). + + do ip=1,maxtp + + if (ip == 4 .or. ip == 6) then ! Parms 4 & 6 not defined. + calcparm(ip,ist) = .FALSE. + cycle + endif + if (calcparm(ip,ist)) then + call calcdist (geslon,geslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + errdist(ip) = dist + if (dist <= errpmax) then + use4next(ip) = .TRUE. + trkerr_avg = trkerr_avg + dist + itot4next = itot4next + 1 + endif + if (dist <= errmax) then + iclose = iclose + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 + endif + clonsum = clonsum + clon(ist,ifh,ip) + clatsum = clatsum + clat(ist,ifh,ip) + else + calcparm(ip,ist) = .FALSE. + endif + endif + + enddo + + if (iclose > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (clonsum) + clon_fguess = (clonsum + (360.*float(lt15_ct)))/ iclose + else + clon_fguess = clonsum / float(iclose) + endif + if (clon_fguess >= 360.0) then + clon_fguess = clon_fguess - 360. + endif + clat_fguess = clatsum / float(iclose) + endif + +c Print out a table listing of the locations of the fixes for +c the individual parameters. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'--------------------------------------------------' + write (6,95) 'Individual fixes follow..., fhr= ',ifhours(ifh) + & ,ifclockmins(ifh),' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + write (6,97) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,'Model name = ',atcfname + print *,'Values of -99.99 indicate that a fix was unable to be' + print *,'made for that paramater. Parameters 4 & 6 are not' + print *,'used. Vorticity data values are scaled by 1e5.' + print *,'Circulation data values are scaled by 1e-6.' + print *,'errdist is the distance that the position estimate is' + print *,'from the guess position for this time. MSLP value ' + print *,'here may differ from that in the atcfunix file since ' + print *,'the one here is that derived from the area-averaged ' + print *,'barnes analysis, while that in the atcfunix file is ' + print *,'from a specific gridpoint.' + write (6,21) geslon,360.-geslon,geslat + write (6,*) ' ' + write (6,23) + write (6,25) + endif + + if (geslat > 0.0) then + charmaxmin(1) = ' Max ' + charmaxmin(2) = ' Max ' + charmaxmin(3) = ' Max ' + charmaxmin(5) = ' Max ' + charmaxmin(10) = ' Max ' + charmaxmin(11) = ' Max ' + else + charmaxmin(1) = ' Min ' + charmaxmin(2) = ' Min ' + charmaxmin(3) = ' Min ' + charmaxmin(5) = ' Min ' + charmaxmin(10) = ' Min ' + charmaxmin(11) = ' Min ' + endif + + do ip=1,maxtp + if (ip == 1 .or. ip == 2 .or. ip == 11) then + ! This IF block allows vorticity values to be + ! written out and scaled up by 1e5 ... + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + elseif (ip == 3 .or. ip == 5 .or. ip == 10) then + ! This IF block allows circulation values to be + ! written out and scaled down by 1e-6 ... + + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + else + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + endif + enddo + + 21 format (' Guess location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 23 format (' parm# parm Max/Min Lon_fix(E) Lon_fix(W)' + & ,' Lat_fix Max/Min_value calcparm errdist(km)') + 25 format (' ----- ---- ------- ---------- ----------' + & ,' ------- ------------- -------- ----------') + 27 format (2x,i2,4x,a8,2x,a8,3x,f7.2,5x,f7.2,4x,f7.2,7x,f9.2 + & ,6x,L2,7x,f7.2) + 95 format (1x,a33,1x,i4,':',i2.2,a2,a4,a1,a9) + 97 format (' Gen ID (if available): ',i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3) + + +c If number of parameter centers close enough (iclose) > 0, then +c calculate the center by taking an average of all the parameter +c center positions that are within distance errmax from the guess +c position (geslon,geslat). Get a first-guess mean position, and +c then re-calculate the position estimate by giving more weight +c to those positions that are closer to the first-guess mean +c position. + + dist_from_mean = 0.0 + + if (iclose > 0) then + +c Get distances from first-guess mean position.... + + do ip=1,maxtp + if (calcparm(ip,ist)) then + call calcdist (clon_fguess,clat_fguess,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + dist_from_mean(ip) = dist + endif + enddo + +c Get the mean distance of each parameter estimate from +c the first-guess mean position + + call avgcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,iaret) + + if (iaret == 0) then + + call stdevcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,stderr_close,isret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After stdevcalc, xmn_dist_from_mean= ' + & ,xmn_dist_from_mean,' stderr_close= ' + & ,stderr_close,' isret= ',isret + endif + + endif + if (iaret /= 0 .or. isret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER -- Error occurred in either' + print *,'!!! avgcalc or stdevcalc. Storm number = ',ist + print *,'!!! RCC from avgcalc = ',iaret + print *,'!!! RCC from stdevcalc = ',isret + print *,'!!! Center fix will NOT be made, and processing' + print *,'!!! for this storm is ending. The probable cause' + print *,'!!! is that no calcparms were valid for this storm' + print *,'!!! at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + if (calcparm(1,ist) .or. calcparm(2,ist) .or. calcparm(7,ist) + & .or. calcparm(8,ist) .or. calcparm(9,ist) + & .or. calcparm(11,ist) .or. calcparm(3,ist) + & .or. calcparm(10,ist) .or. calcparm(5,ist) + & .or. calcparm(12,ist) .or. calcparm(13,ist) + & .or. calcparm(14,ist)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In fixcenter, STOPPING PROCESSING for this' + print *,'!!! storm. The reason is that none of the fix' + print *,'!!! locations for parms z850, z700, zeta 850,' + print *,'!!! zeta 700, MSLP, wcirc_850, wcirc_700, ' + print *,'!!! wcirc_sfc, sfc zeta or the various levels ' + print *,'!!! of thicknesses were within a ' + print *,'!!! reasonable distance of the guess location.' + print *,'!!! ist= ',ist,' ifh= ',ifh + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'!!! Forecast hour: ',i4,':',i2.2) + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now re-calculate the mean position by giving more weight +c to those position estimates that are closer to the first +c guess mean position. Note that if stderr_close < 5.0, we +c force it to be 5.0; we do this to avoid getting very +c large numbers for devia values, which could make the +c weights (wtpos) equal to 0. This occurred during testing +c when only 2 parameters were valid, and so, of course, the +c standard deviation from the mean of those 2 parameters +c was close to 0, which gave devia values around 6000, and +c then wtpos values of 0, leading to a divide by 0 crash +c later on in subroutine wtavrg. + + kprm=0 + + if (stderr_close > 0.0) then + if (stderr_close < 5.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Since stderr_close had a value less than' + print *,'5, stderr_close has been forced to be equal' + print *,'to 5 in order to avoid dividing by zero later' + print *,'on in subroutine wtavrg.' + endif + + stderr_close = 5.0 + endif + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + devia(kprm) = dist_from_mean(ip) / stderr_close + wtpos(kprm) = exp(-devia(kprm)/3.) + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + + if ( verb .ge. 3 ) then + write (6,113) ip,kprm,dist_from_mean(ip),devia(kprm) + & ,wtpos(kprm),temp_clon(kprm) + & ,360.-temp_clon(kprm),temp_clat(kprm) + endif + + endif + enddo + 113 format (1x,'ip= ',i2,' kprm= ',i2,' dist_from_mean= ',f7.3 + & ,' devia= ',f7.3,' wtpos= ',f8.5,2x,3(2x,f7.2)) + else +c +c This next if statement is for the case in which only 1 +c parameter is valid, for which the stderr_close will = 0 +c (obviously), but as long as we have 1 valid parameter, +c continue processing, and set the weight for that parm = 1. +c The else portion is for the case in which stderr_close +c = 0 with NO parms being close. +c + if (iclose == 1) then + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + wtpos(kprm) = 1 + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + endif + enddo + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, stderr_close not > 0' + print *,'!!! stderr_close = ',stderr_close + print *,'!!! The probable cause is that no calcparms were' + print *,'!!! valid for this storm at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + endif +c + if (kprm > 0) then + call wtavrg_lon (temp_clon,wtpos,kprm,fixlon(ist,ifh),iwtret1) + call wtavrg (temp_clat,wtpos,kprm,fixlat(ist,ifh),iwtret2) + if (iwtret1 == 0 .and. iwtret2 == 0) then + if (verb .ge. 3) then + print *,' ' + write (6,173) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + 173 format ('At end of fixcenter: ',a4,' fhr= ',i4,':',i2.2 + & ,' Fix position= ',f7.2,'E (',f6.2,'W)',2x,f7.2) + print *,' ' + endif + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER in call to wtavrg.' + print *,'!!! Return Codes from wtavrg calls follow: ' + print *,'!!! RCC from wtavrg for long fix: ',iwtret1 + print *,'!!! RCC from wtavrg for lat fix: ',iwtret2 + print *,'!!! This means a divide by zero would have ' + print *,'!!! been attempted, which means that the ' + print *,'!!! weights in wtpos are not > 0. Check in' + print *,'!!! subroutine fixcenter where devia values' + print *,'!!! are calculated to see if something is ' + print *,'!!! wrong there. Values of wtpos array follow:' + print *,'!!! ',wtpos + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + print *,' ' + endif + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, kprm NOT > 0' + print *,'!!! This means that, for whatever reason, the ' + print *,'!!! calcparm logical flag was set to .FALSE. for' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: IN FIXCENTER, No storms are within errmax ' + print *,'!!! OR the calcparm logical flag was set to .FALSE. ' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now calculate the average error of all the parms that are within +c a radius errpmax (defined in error_parms, ~600km), and the std +c dev of those errors. This standard deviation will be used in +c calculating the maximum allowable error for the next forecast +c time. + + if (itot4next > 0 .and. ifret /= 95) then + trkerr_avg = trkerr_avg / float(itot4next) + call stdevcalc (errdist,maxtp,use4next,trkerr_avg + & ,stderr(ist,ifh),isret) + if (isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in FIXCENTER calculating std deviation ' + print *,'!!! for use in next forecast hours errmax.' + print *,'!!! ist= ',ist,' ifh= ',ifh,' itot4next= ' + & ,itot4next + endif + + ifret = 95 + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine avgcalc (xdat,kmax,valid,xavg,iaret) +c +c ABSTRACT: This subroutine just calculates a straight average of +c the parameters in the input array (xdat). The logical array +c (valid) indicates whether or not to include a particular array +c member or not in the calculation. + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) +c + iaret = 0 +c + xsum = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + xsum = xsum + xdat(i) + ict = ict + 1 + endif + enddo +c + if (ict > 0) then + xavg = xsum / float(ict) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in avgcalc, ict NOT > 0' + endif + + xavg = xdat(1) + iaret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg (xdat,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xdat) using the input weights +c in the input array (wt). It is used to calculate the center lat +c and lon fix positions. +c + USE verbose_output + + real xdat(kmax),wt(kmax) +c + iwtret = 0 +c + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xdat(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg, wtot NOT > 0' + endif + + iwtret = 95 + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg_lon (xlon,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xlon) using the input weights +c in the input array (wt). This subroutine is specifically used +c to find the center lon fix positions. It contains code to +c account for wrapping around the Greenwich Meridian. +c + + USE verbose_output + + real xlon(kmax),wt(kmax) + integer gt345_ct,lt15_ct +c + iwtret = 0 + gt345_ct = 0 + lt15_ct = 0 + +c First check to see if we have lons that are both to the left +c and the right of the greenwich meridian + + do i = 1,kmax + if (xlon(i) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (xlon(i) < 15.) then + lt15_ct = lt15_ct + 1 + endif + enddo + + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some lons that are in the 300's (west of the GM), and + ! some that are in the 0's (east of the GM). We need to + ! standardize these if we want to get a meaningful average. + do i = 1,kmax + if (xlon(i) < 15.) then + xlon(i) = xlon(i) + 360.0 + endif + enddo + endif + + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xlon(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg_lon, wtot NOT > 0' + endif + + iwtret = 95 + endif + + if (xwtavg >= 360.0) then + xwtavg = xwtavg - 360.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine stdevcalc (xdat,kmax,valid,xavg,stdx,isret) + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) + + isret = 0 + + stdx = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + stdx = stdx + (xdat(i) - xavg)**2 + ict = ict + 1 + endif + enddo + + if (ict > 0) then + stdx = sqrt(stdx/float(ict)) + if (stdx == 0.0) then +c This can happen if you have just 2 points; The mean position +c will be exactly in the middle of the 2 points and so the +c standard deviation around that mean point will be 0. And +c since the calling routine will quit if the returned standard +c deviation is 0, we must force it to be 1 so the program +c continues running. Theoretically, it could also happen with +c 3 or more points, but the likelihood of the distances working +c out to exactly equidistant for 3 points is not that good. + stdx = 1.0 + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in stdevcalc, ict NOT > 0' + endif + + isret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,fxval,trkrinfo + & ,cmodel_type,maxmin,igwcret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the wind circulation near the storm center. This center fix is +c done differently than for the other parms. With this fix, +c we limit the area that is searched. This subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the +c original guess position for this lead time and the 5 other parm +c fixes that have already been made for this lead time. That +c modified guess position is passed into this subroutine as uvgeslon +c and uvgeslat, and that's where the searching for the wind +c circulation is centered. +c +c This subroutine works by converting the winds to Vt and Vr at +c each grid point evaluated, relative to each candidate center point +c that is being evaluated at the time in the loop. We then compute +c the circulation at each of 24 azimuths surrounding the storm +c center, where circulation = Vt * (length of a 1/24 arc, in meters) +c This process is repeated for 7 successive radii and the results +c are summed up over all radii, approximating a solid disk +c circulation. The point at which the circulation is maximized +c (NHEM) or minimized (SHEM) is the center of circulation. +c +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c + + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + character(*) cmodel_type,maxmin + integer, parameter :: numdist=7,numazim=24 + integer imax,jmax,ist,level,igwcret,icvpret,idist,iazim + real rdist(numdist),vr(numazim,numdist),vt(numazim,numdist) + real vt_mean(numdist),circul_band(numdist) + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + real rads,ri,uvgeslon,uvgeslat,dx,dy,ctlon,ctlat,fxval + real temp_grid_minlon,temp_guesslon,rlatt,rlont,bear + real targlon,targlat,xintrp_u,xintrp_v,vt_azim_sum,degrees + real circ_diff,circ_diff_sum,hemisphere,wind_mag_ctr,dist + real xmin_circ_diff_mean,xmax_circ_diff_mean,tlon,tlat + real dell,fmax,fmin,grid_buffer,circ_diff_mean + real circumference,arclength + real circul_disk,xmax_circul_disk,xmin_circul_disk + integer ibiret1,ibiret2,igvtret,azimuth_ct,igiret,npts + integer igibret + integer circ_diff_ct,ir,nhalf,bskip1,bskip2,iskip,nlev + integer ilonfix,jlatfix,ibeg,iend,jbeg,jend,i,j,k,iix,jix + logical(1) cflag, valid_pt(imax,jmax) + +c---------------- +c + + print *,' ' + print *,'top of get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' cmodel_type= ',cmodel_type + print *,' maxmin= ',maxmin + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' cflag= ',cflag + print *,' ctlon= ',ctlon,' ctlat= ',ctlat + print *,' fxval= ',fxval + print *,' igwcret= ',igwcret + + igwcret = 0 + + grid_maxlat = glatmax + grid_minlat = glatmin + grid_maxlon = glonmax + grid_minlon = glonmin + + rads = rads_wind_circ + ri = ri_wind_circ + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of get_wind_circulation, rads= ',rads + & ,' ri= ',ri,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+15; fmin = 1.0e+15 + ctlon = 0.0; ctlat = 0.0 + +c Distances checked and the radial intervals are a function of +c the grid resolution.... + + if (dell > 0.50) then + rdist(1) = 50. + rdist(2) = 85. + rdist(3) = 120. + rdist(4) = 155. + rdist(5) = 190. + rdist(6) = 225. + rdist(7) = 260. + else + rdist(1) = 35. + rdist(2) = 65. + rdist(3) = 95. + rdist(4) = 125. + rdist(5) = 155. + rdist(6) = 185. + rdist(7) = 215. + endif + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + print *,' in get_wind_circulation, nlev= ',nlev + + if (uvgeslat >= 0.0) then + hemisphere = 1.0 + else + hemisphere = -1.0 + endif + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend + & ,igibret) + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (uvgeslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = uvgeslon - 360. + else + temp_guesslon = uvgeslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = uvgeslon + endif + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + +c For the wind circulation analysis, we will want to speed things +c up for finer resolution grids. We can do this by skipping some +c of the points in the wind circulation analysis. + + if (dell > 0.20) then + bskip1 = 1 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 3 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 5 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 8 + bskip2 = 3 + else if (dell <= 0.03) then + bskip1 = 10 + bskip2 = 4 + endif + +c bskip1 = 1 +c bskip2 = 1 + + jix = 0 + +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to first loop, ' + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop1: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = uvgeslat + dell*float(j) + + iix = 0 + + iloop1: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop1 + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT from call in ' + print *,'!!! get_wind_circulation: icvpret= ',icvpret + endif + cycle iloop1 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist,degrees) + if (dist .gt. rads) cycle iloop1 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop1: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop1: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + ! These calls to bilin_int_uneven pass a variable "level" + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop1 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop1 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop1 +cc and radiusloop1). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,' ' +cc print *,'1st run, wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'1st run, ir= ',ir,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +c print *,'1st run, circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'1st run, circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'1st run, xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif + + enddo iloop1 + + enddo jloop1 + + if (uvgeslat >= 0.0) then + write (6,61) 360.-ctlon,ctlat,xmax_circul_disk + else + write (6,63) 360.-ctlon,ctlat,xmin_circul_disk + endif + + 61 format (' After first run, Wind Circulation (NHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmax_circul_disk = ',f15.1) + 63 format (' After first run, Wind Circulation (SHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmin_circul_disk = ',f15.1) + +c If nhalf is specified as 0, then don't go through any more +c iterations of this routine, just exit with the value that we +c already got the first time through the loop, above. + + if (dell > 0.50) then + nhalf = 4 + else if (dell > 0.20 .and. dell <= 0.50) then + nhalf = 3 + else if (dell > 0.10 .and. dell <= 0.20) then + nhalf = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + nhalf = 1 + else if (dell <= 0.05) then +c nhalf = 0 + nhalf = 1 +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'In get_wind_circulation, dell is < 0.05 deg, so ' +c print *,'nhalf is set to 0 and only the first iteration of' +c print *,'the search loop is done.' +c print *,' dell= ',dell,' nhalf= ',nhalf +c endif + endif + + if (nhalf < 1) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +c npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only do this once +c for this grid-refinement (even though the grid is redefined +c nhalf times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). Cut the value of +c rads in half (only do this once) so that any points beyond +c rads/2 are not considered as potential centers. + + rads = 0.5 * rads + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igibret) + + if (igibret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_wind_circulation from call to ' + print *,'!!! get_ij_bounds just before nhalf loop. ' + print *,'!!! Stopping processing for storm number ',ist + endif + igwcret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + kloop: do k = 1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: get_wind_circ kloop, k= ',i2,' ' + & ,i2.2,':',i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'get_wind_circ nhalf loop, k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip,' rads= ',rads + endif + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to loop k= ',k + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist + & ,degrees) + if (dist .gt. rads) cycle iloop2 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop2: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop2: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop2 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop2 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop2 +cc and radiusloop2). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,'kloop k= ',k,' wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'kloop k= ',k,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +cc print *,'kloop k= ',k,' circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'kloop k=',k,' circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0.0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'kloop k= ',k,' xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean + + enddo iloop2 + + enddo jloop2 + + if ( verb .ge. 3 ) then + if (uvgeslat >= 0.0) then + print *,'---> xmax_circul_disk= ',xmax_circul_disk + write (6,71) k,360.-ctlon,ctlat,xmax_circul_disk + else + print *,'---> xmin_circul_disk= ',xmin_circul_disk + write (6,73) k,360.-ctlon,ctlat,xmin_circul_disk + endif + endif + + enddo kloop + + 71 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (NHEM: Max) = ' + & ,f15.1) + 73 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (SHEM: Min) = ' + & ,f15.1) + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,xval,trkrinfo,igucret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the minimum in the wind speed near the storm center. This center +c fix is done differently than for the other parms. With this fix, +c we severely limit the area that is searched, because we do not +c want to confuse a wind minimum out on the periphery of a storm +c with the center wind minimum. Therefore, this subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the guess +c position for this time and the 5 other parm fixes. That modified +c guess position is passed into this subroutine as uvgeslon and +c uvgeslat, and that's where the searching for the wind minimum +c is done. To get the wind minimum, the u and v data are first +c interpolated down to a fine grid (see details below for exact +c figures), and then a single-pass barnes analysis is done on that +c fine grid. The reason that we first interpolate the data (which +c is different from how we do the other parms) is that if we just +c use the original grid resolution, we may not be able to +c accurately pick out a minimum in the wind field at the center. +c + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real, allocatable :: uold(:,:),vold(:,:),unew(:,:),vnew(:,:) + real, allocatable :: rlonold(:),rlatold(:),rlonnew(:),rlatnew(:) + real, allocatable :: vmag(:,:) + real :: dx,dy + real :: grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + character*1 :: gotlat + logical(1) cflag, valid_pt(imax,jmax) + logical(1), allocatable :: lbi(:,:) +c + gotlat = 'n' +c +c ----------------------------------------------------------------- +c INTERPOLATE INPUT GRID TO SMALLER GRID +c ----------------------------------------------------------------- +c +c Get beginning and ending j points (on the input grid) for a +c smaller array that surrounds the storm. It is this smaller array +c that we will interpolate to a finer grid. +c +c Calculate number of pts to either side of this j to search +c + npts = ceiling(rads_vmag/(dtk*((dx+dy)/2.))) +c + call get_ij_bounds (npts,0,ritrk_vmag,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij D, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij D, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center from call to ' + print *,'!!! get_ij_bounds, stopping processing for ' + print *,'!!! storm number ',ist + endif + + igucret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, and our gridtype is NOT' + print *,'!!! global, so we are going to redefine ibeg to 1.' + print *,' ' + endif + + ibeg = 1 + endif + endif + + if (jbeg < 1) jbeg = 1 + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center calculating ibeg, iend, jbeg' + print *,'or jend. ibeg= ',ibeg,' iend= ',iend,' jbeg= ',jbeg + print *,'jend= ',jend + print *,'uv center will not be calculated for this time.' + endif + + igrret = 99 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, and our gridtype is' + print *,'!!! NOT global, so we will redefine iend to imax.' + print *,' ' + endif + + iend = imax + endif + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif +c + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the grid sizes for +c some of the typical grids that will be used: +c +c Original grid size # of interps Final grid size +c -------------------- ------------ --------------------- +c 1.00 deg (111.19 km) 3 0.125 deg (13.9 km) +c 1.25 deg (138.99 km) 3 0.156 deg (17.4 km) +c 2.50 deg (277.99 km) 4 0.156 deg (17.4 km) + + if ((dx+dy)/2. > 1.2) then + numinterp = 4 + else if ((dx+dy)/2. > 0.50 .and. (dx+dy)/2. <= 1.2) then + numinterp = 3 + else if ((dx+dy)/2. > 0.25 .and. (dx+dy)/2. <= 0.50) then + numinterp = 2 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.25) then + numinterp = 1 + else if ((dx+dy)/2. <= 0.10) then + numinterp = 0 + endif + + dell = (dx+dy)/2. + imxold = iend - ibeg + 1 + jmxold = jend - jbeg + 1 + +c -------------------------------------------------------------- +c Before interpolating, make sure that all the original +c points have valid data. If they don't then exit the +c subroutine. NOTE: This is NOT checking to see if ALL the pts +c on the complete & full input grid have valid data; it only +c checks those points that are within the box returned from +c get_ij_bounds. + + do i=ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_uv_center, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! PROCESSING WILL STOP. ' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + stop 94 + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_uv_center' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + do j=jbeg,jend + if (.not. valid_pt(ip,j)) goto 975 + enddo + + enddo + +c ------------------------------------ +c Now begin the interpolation process + + allocate (uold(imxold,jmxold),stat=iuo) + allocate (vold(imxold,jmxold),stat=ivo) + allocate (rlonold(imxold),stat=iloo) + allocate (rlatold(jmxold),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. iloo /= 0 .or. ilao /= 0) goto 970 + + do intnum = 1,numinterp + + if (intnum == 1) then + + do i=ibeg,iend + + ik = i + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ik = i + imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i < 1' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i + endif + + igucret = 92 + return + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ik = i - imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i > imax' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i,' imax= ',imax + endif + + igucret = 92 + return + endif + endif + + rlonold(i-ibeg+1) = glon(ik) + do j=jbeg,jend + uold(i-ibeg+1,j-jbeg+1) = u(ik,j,nlev) + vold(i-ibeg+1,j-jbeg+1) = v(ik,j,nlev) + if (gotlat == 'n') then + rlatold(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatold once + enddo + + else + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate (rlatold) + allocate (uold(imxnew,jmxnew),stat=iuo) + allocate (vold(imxnew,jmxnew),stat=ivo) + allocate (rlonold(imxnew),stat=iloo) + allocate (rlatold(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 970 + + gotlat = 'n' + do i=1,imxnew + rlonold(i) = rlonnew(i) + do j=1,jmxnew + uold(i,j) = unew(i,j) + vold(i,j) = vnew(i,j) + if (gotlat == 'n') then + rlatold(j) = rlatnew(j) + endif + enddo + gotlat = 'y' + enddo + + imxold = imxnew + jmxold = jmxnew + deallocate (unew); deallocate (vnew) + deallocate (rlonnew); deallocate (rlatnew) + + endif + + dell = 0.5 * dell + imxnew = 2 * imxold - 1 + jmxnew = 2 * jmxold - 1 + + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + + call bilin_int_even (imxold,jmxold,uold + & ,imxnew,jmxnew,unew,ibiret) + call bilin_int_even (imxold,jmxold,vold + & ,imxnew,jmxnew,vnew,ibiret) +c call lin_int (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int_lon (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int (jmxold,jmxnew,rlatold,rlatnew,iliret) + + chk_lonspc_old = rlonold(imxold) - rlonold(imxold - 1) + chk_latspc_old = rlatold(jmxold) - rlatold(jmxold - 1) + chk_lonspc_new = rlonnew(imxnew) - rlonnew(imxnew - 1) + chk_latspc_new = rlatnew(jmxnew) - rlatnew(jmxnew - 1) + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, intnum= ',intnum + print *,'imxold= ',imxold,' imxnew= ',imxnew + print *,'jmxold= ',jmxold,' jmxnew= ',jmxnew + print *,'Grid boundaries of modified uv grid: ' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ' + & ,grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ' + & ,grid_minlon + endif + + enddo + +c ------------------ + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate(rlatold) + + if (numinterp == 0) then + + ! No interpolations were done for this fine mesh grid, but we + ! need to fill some of these arrays and define variables for + ! subsequent subroutine calls just below here that require + ! the variables imxnew, jmxnew, and the arrays unew and vnew. + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional ' + print *,'grid; iend should not > imax here !!!' + endif + + igucret = 99 + return + endif + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional' + print *,'grid; ibeg should not < 1 here !!!' + endif + + igucret = 99 + return + endif + endif + + imxnew = iend - ibeg + 1 + jmxnew = jend - jbeg + 1 + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + gotlat = 'n' + + do i=ibeg,iend + + ip = i + + if (i > imax) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i - imax ! Wrapping past GM + endif + + if (i < 1) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i + imax ! Wrapping past GM + endif + + rlonnew(i-ibeg+1) = glon(ip) + do j=jbeg,jend + unew(i-ibeg+1,j-jbeg+1) = u(i,j,nlev) + vnew(i-ibeg+1,j-jbeg+1) = v(i,j,nlev) + if (gotlat == 'n') then + rlatnew(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatnew once + enddo + + endif + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,'Grid boundaries of modified uv grid in get_uv_center:' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ',grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ',grid_minlon + endif + + allocate (vmag(imxnew,jmxnew),stat=ivm) + allocate (lbi(imxnew,jmxnew),stat=ilb) + if (ivm /= 0 .or. ilb /= 0) goto 972 + call calc_vmag (unew,vnew,imxnew,jmxnew,vmag,icvret) + deallocate (unew); deallocate (vnew) + + lbi = .TRUE. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to find_maxmin, imxnew= ',imxnew + & ,'jmxnew= ',jmxnew,' ist= ',ist + write (6,171) dell,uvgeslon,360.-uvgeslon,uvgeslat + 171 format (' dell= ',f7.3,' uvgeslon= ',f8.3,'E (',f8.3,'W)' + & ,' uvgeslat= ',f8.3) + endif + +c Note that in the next call, I pass the 'global' argument to +c find_maxmin. This defines what type of grid it is, so that the +c proper grid_buffer can be chosen. This grid_buffer is designed +c to avoid having a center be chosen too close to the grid +c boundary. However, in the case of vmag here, we are only using +c a small subgrid, and we want to make sure we use *all* points +c in that subgrid for searching, and that will occur if we set that +c calling argument to 'global' as opposed to 'regional'. + + call find_maxmin (imxnew,jmxnew,dell,dell,'vmag' + & ,vmag,'min',ist,uvgeslon,uvgeslat,rlonnew,rlatnew,lbi + & ,trkrinfo,cflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,'global',ifmret) + deallocate (vmag); deallocate (lbi) + deallocate (rlonnew); deallocate (rlatnew) +c + if (ifmret == 0) then + goto 995 + else + igucret = ifmret + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center in call to find_maxmin' + print *,'!!! storm num = ',ist,' igucret = ',igucret + endif + + goto 998 + endif +c + 970 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either uold, vold,' + print *,'!!! rlonold or rlatold in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 971 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either unew, vnew,' + print *,'!!! rlonnew or rlatnew in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 972 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either vmag or lbi in ' + print *,'!!! subroutine get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! ivm= ',ivm,' ilb= ',ilb + endif + + igucret = 97 + goto 998 + + 975 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Inside get_uv_center, at least one of the points' + print *,'!!! is not a valid data point. This point may be ' + print *,'!!! outside the valid data bounds of a regional grid' + print *,'!!! i= ',i,' j= ',j + print *,'!!! Storm number = ',ist + endif + + igucret = 98 + goto 998 +c + 995 continue + igucret = 0 +c + 998 continue + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_guess (guesslon,guesslat,clon,clat + & ,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) +c +c ABSTRACT: The purpose of this subroutine is to get a modified +c first guess lat/lon position before searching for the +c minimum in the wind field. The reason for doing this is +c to better refine the guess and avoid picking up a wind +c wind minimum far away from the center. So, use the +c first guess position (and give it strong weighting), and +c then also use the fix positions for the current time +c (give the vorticity centers stronger weighting as well), +c and then take the average of these positions. +c +c INPUT: +c guesslon guess longitude for this forecast time +c guesslat guess latitude for this forecast time +c clon array with center longitude fixes for the various parms +c clat array with center latitude fixes for the various parms +c calcparm logical; tells whether or not a parm has a valid fix +c at this forecast hour +c ist index for current storm +c ifh index for current forecast hour +c maxstorm max # of storms that can be handled +c +c OUTPUT: +c uvgeslon contains modified guess longitude position at which to +c look for the wind minimum +c uvgeslat contains modified guess latitude position at which to +c look for the wind minimum +c igugret return code for this subroutine (0=normal) +c---- +c + USE set_max_parms; USE level_parms; USE error_parms + USE verbose_output + + logical(1) calcparm(maxtp,maxstorm) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real uvgeslon, uvgeslat + real guesslon,guesslat,degrees + integer gt345_ct,lt15_ct + + sumlon = 0.0 + sumlat = 0.0 + ict = 0 + gt345_ct = 0 + lt15_ct = 0 + +c NOTE: We need to be careful in this routine when averaging +c the longitudes together, in case we cross the greenwich +c meridian, because then we may be averaging 345+ lons with +c lons that are less than 15, giving incorrect results. +c Therefore, check for this, and if it occurs, add 360 onto +c any of the <15 lons (add it twice for those lons being +c counted twice (guesslon and the vorticity centers)). + +c Weight the uv guess position by counting the storm's guess +c position twice. + + sumlon = sumlon + 2.*guesslon + sumlat = sumlat + 2.*guesslat + ict = ict + 2 + + if (guesslon > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (guesslon < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct.... + endif + + do ip = 1,maxtp + if ((ip > 2 .and. ip < 7) .or. ip == 10) then + cycle ! because 3-6 are for 850 & 700 u & v and 10 is + ! for surface wind magnitude. + else + if (calcparm(ip,ist)) then + call calcdist (guesslon,guesslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + + if (dist < uverrmax) then +c +c Give the vorticity centers 2x weighting as well +c + if (ip == 1 .or. ip == 2 .or. ip == 11) then + sumlon = sumlon + 2.*clon(ist,ifh,ip) + sumlat = sumlat + 2.*clat(ist,ifh,ip) + ict = ict + 2 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct... + endif + else + sumlon = sumlon + clon(ist,ifh,ip) + sumlat = sumlat + clat(ist,ifh,ip) + ict = ict + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 ! Only 1 for non-zeta parms + endif + endif + + endif + + endif + endif + enddo +c + if (ict > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (sumlon) + uvgeslon = (sumlon + (360.*float(lt15_ct)))/ ict + else + uvgeslon = sumlon / ict + endif + if (uvgeslon >= 360.0) then + uvgeslon = uvgeslon - 360. + endif + uvgeslat = sumlat / ict + igugret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_guess, ict not > 0, ict= ',ict + print *,'!!! vmag center will not be calculated for this' + print *,'!!! storm -- at least not at this level' + print *,'!!! Storm number = ',ist + endif + + igugret = 91 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calc_vmag (xu,xv,imx,jmx,wspeed,icvret) +c +c ABSTRACT: This subroutine calculates the magnitude of the wind +c speed for an array of points, given real u and real v arrays. +c + real xu(imx,jmx),xv(imx,jmx),wspeed(imx,jmx) +c + do i=1,imx + do j=1,jmx + wspeed(i,j) = sqrt( xu(i,j)*xu(i,j) + xv(i,j)*xv(i,j) ) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_even (imxold,jmxold,xold + & ,imxnew,jmxnew,xnew,ibiret) +c +c ABSTRACT: This subroutine does a bilinear interpolation on a +c grid of evenly spaced data. Do NOT attempt to use this subroutine +c with data that are not evenly spaced or you will get unpredictable +c results. +c + real xold(imxold,jmxold), xnew(imxnew,jmxnew) +c +c +c --------------------------------------------------------------------- +c Latitude ----> | +c | +c L O e O e O e O e O | O: original point from input array +c o | +c n e 1 2 1 2 1 2 1 e | 1: interpolated, primary inter. pt +c g | +c i O 2 O 2 O 2 O 2 O | e: interpolated edge point +c t | +c u e 1 2 1 2 1 2 1 e | 2: interpolated, secondary inter. pt +c d | +c e O 2 O 2 O 2 O 2 O | Interpolations are done in the order +c | as indicated above; First, the input +c | e 1 2 1 2 1 2 1 e | 'O' pts are placed onto the new, +c | | larger grid. From that, the '1' pts +c | O 2 O 2 O 2 O 2 O | can be interpolated. Next, the edge +c | | (e) pts are interpolated using an +c v e 1 2 1 2 1 2 1 e | interpolation of two 'O' pts and one +c | '1' pt. Finally, the '2' pts are +c O e O e O e O e O | done using the 2 surrounding '0' and +c | '1' pts. Bilinear interpolation is +c | made incredibly easier by the fact +c | that the grid is evenly spaced. +c --------------------------------------------------------------------- +c NOTE: Remember that the arrays that are read in are indexed as +c (lon,lat), so that in the diagram above, pt (1,1) is at the upper +c left and pt (imax,jmax) is at the lower right, and each column is +c a new latitude and each row is a new longitude. +c +c ----------------------------------------------------------------- +c Put original (O) values from input array into new, expanded array +c ----------------------------------------------------------------- +c + do i=1,imxold + do j=1,jmxold + xnew(2*i-1,2*j-1) = xold(i,j) + enddo + enddo +c +c ---------------------------------------------- +c Interpolate to get primary interior (1) points +c ---------------------------------------------- +c + do i=1,imxold-1 + do j=1,jmxold-1 + xnew(2*i,2*j) = 0.25 * (xnew(2*i-1,2*j-1) + xnew(2*i+1,2*j-1) + & + xnew(2*i+1,2*j+1) + xnew(2*i-1,2*j+1)) + enddo + enddo +c +c --------------------------- +c Interpolate edge (e) points +c --------------------------- +c +c ... Northernmost 'e' points ... +c + j=1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,2)) + enddo +c +c ... Southernmost 'e' points ... +c + j = 2*jmxold - 1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,j-1)) + enddo +c +c ... Westernmost 'e' points ... +c + i=1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(2,2*j)) + enddo +c +c ... Easternmost 'e' points ... +c + i = 2*imxold - 1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(i-1,2*j)) + enddo +c +c ------------------------------------------------ +c Interpolate to get secondary interior (2) points +c ------------------------------------------------ +c + do j=2,2*jmxold-2 + istep = mod(j+1,2) + do i=istep+2,2*imxold-2,2 + xnew(i,j) = 0.25 * (xnew(i-1,j) + xnew(i,j-1) + xnew(i+1,j) + & + xnew(i,j+1)) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points +c + do i=1,ioldmax-1 + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int_lon (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. This particular +c routine is specifically used for interpolating +c longitudes, and it factors in the possibility of +c interpolating across the greenwich meridian. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points, and make the +c necessary adjustment when interpolating a longitude between, +c for example, 359.5 and 0.0. +c + do i=1,ioldmax-1 + if (xnew(2*i-1) > 350. .and. xnew(2*i+1) < 10.) then + xnew(2*i) = 0.5 * (xnew(2*i-1) + (360. + xnew(2*i+1))) + else + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + endif + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) +c +c ABSTRACT: This subroutine finds the maximum and mean zeta values +c at 850 & 700 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms + USE trkrparms; USE level_parms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + + logical(1) readflag(14),valid_pt(imax,jmax),compflag + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanzeta,dx,dy,re,ri,parmlon,parmlat + integer igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) + integer n,ix1,ix2,ilev,npts,imax,jmax,igzvret,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_zeta_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_zeta_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max zeta values at 850 and 700 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_zeta_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_zeta_loop: do n=1,2 + + gridpoint_maxmin = -99.0 + xmeanzeta = -99.0 + compflag = .true. + + select case (n) + case (1); ilev=850 ! For 850 mb + case (2); ilev=700 ! For 700 mb + end select + + if (zeta(ilonfix,jlatfix,n) > -9990.0) then + + ! ------------------------------------------- + ! We have valid zeta data for this level, so + ! we first call barnes now to get the mean zeta + ! surrounding our found center position. + ! ------------------------------------------- + + if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,n),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanzeta + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds + imeanzeta(n) = int ((xmeanzeta * 1e6) + 0.5) + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_zeta_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for zeta values will not be done.') + exit report_zeta_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + exit report_zeta_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) n,ilev,xmeanzeta,imeanzeta(n) + 621 format (1x,'+++ RPT_MEAN_ZETA: n= ',i2,' lev= ',i4 + & ,' xmeanzeta= ',f9.6,' imeanzeta (*1e6)= ',i8) + write (6,*) ' --- mean zeta raw = ',xmeanzeta + endif + + ! ----------------------------------------------- + ! Call fix_latlon_to_ij to get the nearest actual + ! raw (grid) zeta data value, not the mean value. + ! ----------------------------------------------- + + call fix_latlon_to_ij (imax,jmax,dx,dy + & ,zeta(1,1,n),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanzeta,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + igridzeta(n) = int ((gridpoint_maxmin * 1e6) + 0.5) + else + igridzeta(n) = -99 + endif + + if ( verb .ge. 3 ) then + write (6,623) n,ilev,gridpoint_maxmin,igridzeta(n),ifilret + 623 format (1x,'+++ RPT_GRID_ZETA: n= ',i2,' lev= ',i4 + & ,' grid zeta= ',f9.6,' igrid zeta (*1e6)= ',i8 + & ,' ifilret= ',i3) + write (6,*) ' --- grid zeta raw= ',gridpoint_maxmin + endif + + enddo report_zeta_loop + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get 850 & 700 zeta for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine find_maxmin (imax,jmax,dx,dy,cparm,fxy,maxmin,ist + & ,guesslon,guesslat,rlonv,rlatv,valid_pt,trkrinfo + & ,compflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,cmodel_type,ifmret) +c +c This routine finds the location (clon,clat) of and value of the +c the max or min of fxy in the vicinity of slon,slat. The value of +c the input flag maxmin determines whether to look for a max or a +c min value. The max/min is determined by finding the point which +c gives the max/min value of a single point barnes analysis of fxy +c with e-folding radius re (km) and influence radius ri (km). The +c initial search is restricted to a radius rads around the point +c (slon,slat) on a grid with lon,lat spacing dx and dy. The location +c is refined by reducing the spacing of the search grid by a factor +c of two, nhalf times. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c maxmin Char string indicating whether to search for a max or min +c ist Number of the storm being processed +c guesslon Guess longitude of the storm +c guesslat Guess latitude of the storm +c rlonv Array containing longitude values of input grid points +c rlatv Array containing latitude values of input grid points +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c trkrinfo derived type detailing user-specified grid info +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c +c INPUT/OUTPUT: +c compflag Logical; continue processing this storm or not (would be +c set to FALSE if, for example, the guess position is +c outside the domain of a regional grid) +c +c OUTPUT: +c ctlon Center longitude of storm found for this parameter +c ctlat Center latitude of storm found for this parameter +c xval Max or Min value found at the (ctlon,ctlat) +c ifmret Return code from this subroutine +c +c UPDATE DEC 2009: For the HFIP HRH testing, it was found that +c due to the very limited domain size of some of the models, the +c barnes scheme was allowing points close to the grid boundaries +c to erroneously be selected as the center point. We add in a +c buffer (grid_buffer) here to prevent this from occurring. + + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + character(*) maxmin,cparm,cmodel_type + logical(1) compflag, valid_pt(imax,jmax) + real fxy(imax,jmax),rlonv(imax),rlatv(jmax) + real ctlon,ctlat,degrees,dx,dy,guesslon,guesslat,xval + real rads,re,ri,dell,fmax,fmin,rlatt,rlont,dist,ftemp,ritmp + real vmag_latmax,vmag_latmin,vmag_lonmax,vmag_lonmin,retmp + real tlon,tlat,grid_buffer,temp_grid_minlon,temp_guesslon + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + integer imax,jmax,ist,bskip1,bskip2,iskip,ifmret,npts,maxvgrid + integer ibeg,iend,jbeg,jend,ilonfix,jlatfix,igiret,icount,iret + integer ibct,ibarnes_loopct,i,j,k,iix,jix,jvlatfix,ivlonfix + integer nhalf,icvpret + integer date_time(8) + character (len=10) big_ben(3) +c + ifmret = 0 + nhalf = 5 +c +c ----------------------------------------------------------- +c Set initial parms for use in find_maxmin. +c Different radii used for V magnitude than for other parms, +c see discussion in module radii for more details. +c + if (cparm == 'vmag') then + +c NOTE: The maxvgrid variable determines what size grid to send +c to subroutine barnes. e.g., maxvgrid = 8 means send an +c 8x8 grid; maxvgrid = 12 means send a 12x12 grid. For +c ultra-fine mesh grids (finer than 0.04 deg, or 1/25 deg), +c we expand to 12 in order to sample a few more points +c around each grid point. + + if ((dx+dy)/2. > 0.04) then + maxvgrid = 8 + else + maxvgrid = 12 + endif + + rads = rads_vmag; re = retrk_vmag; ri = ritrk_vmag + re = (float(maxvgrid)/4) * ((dx+dy)/2. * dtk) ! Basically, this +c sets re equal to half the distance from the gridpoint +c in question to the farthest point that will be +c sampled when the (maxvgrid x maxvgrid) grid is passed +c on to subroutine barnes. Thus, just ignore the +c parameter retrk_vmag, and use this instead. + else if ((dx+dy)/2. < 1.26 .and. (dx+dy)/2. >= 0.40) then + rads = rads_most; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.40 .and. (dx+dy)/2. >= 0.10) then + rads = rads_fine; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.10) then + rads = rads_hres; re = retrk_hres; ri = ritrk_most + else + rads = rads_coarse; re = retrk_coarse; ri = ritrk_coarse + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of find_maxmin, rads= ',rads,' re= ',re + & ,' ri= ',ri,' cparm= ',cparm,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+12; fmin = 1.0e+12 + ctlon = 0.0; ctlat = 0.0 + + if (npts == 0) npts = 1 + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if (dell > 0.20) then + bskip1 = 2 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 4 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 6 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 10 + bskip2 = 5 + else if (dell <= 0.03) then + bskip1 = 15 + bskip2 = 5 + endif + + if (cparm == 'vmag') then + bskip1 = 1 + bskip2 = 1 + endif + +c If input parm is vmag, we've already done the minimizing by +c interpolating to the fine mesh grid, so we'll simply send the +c bounds that were input to this subroutine to barnes +c as boundaries for the array to search. For all other parms, +c however, no minimizing has been done yet, so we need to call +c get_ij_bounds to set the boundaries for a much smaller grid that +c surrounds the storm (as opposed to having subroutine barnes +c search the entire global grid). + + if (cparm == 'vmag') then + + if ( verb .ge. 3 ) then + print *,'In find_maxmin, jmax= ',jmax,' imax= ',imax + endif + + ibeg=1; jbeg=1; iend=imax; jend=jmax + vmag_latmax = rlatv(1) ! N-most lat of vmag subgrid + vmag_latmin = rlatv(jmax) ! S-most lat of vmag subgrid + vmag_lonmin = rlonv(1) ! W-most lon of vmag subgrid + vmag_lonmax = rlonv(imax) ! E-most lon of vmag subgrid + + if ( verb .ge. 3 ) then + write (6,44) vmag_latmax,vmag_lonmin,360.-vmag_lonmin + & ,imax,jmax + write (6,46) vmag_latmin,vmag_lonmax,360.-vmag_lonmax + endif + + 44 format (' vmag_latmax= ',f8.3,' vmag_lonmin= ',f8.3 + & ,'E (',f8.3,'W) imax= ',i4,' jmax= ',i4) + 46 format (' vmag_latmin= ',f8.3,' vmag_lonmax= ',f8.3 + & ,'E (',f8.3,'W)') + + if (vmag_lonmin > 330. .and. vmag_lonmax < 30.) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: For a case of find_maxmin, our vmag' + print *,'!!! subgrid is straddling the GM. The code should' + print *,'!!! be able to handle this, but if strange errors' + print *,'!!! are occurring, check into the code either here' + print *,'!!! in find_maxmin or get_uv_ctr.' + print *,' ' + endif + endif + + npts = ceiling(rads/(dtk*dell)) + + else + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,guesslon,guesslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to ' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + ifmret = 92 + return + endif + + endif + +c +c --------------------------------------------------------------- +c + if ( verb .ge. 3 ) then + print *,' ' + write (6,39) guesslon,360.-guesslon,guesslat + 39 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + if (cparm == 'vmag') then + print *,'ilonfix= (unused) jlatfix= (unused)' + & ,' npts= ',npts + print *,'ilonfix and jlatfix are meaningless for computing' + print *,'vmag, so ignore the large values you see for them.' + else + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + endif + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + + if ( verb .ge. 3 ) then + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: find_maxmin 1 ',i2.2,':',i2.2,':',i2.2) + endif + + ibct=0 + ibarnes_loopct = 0 + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (guesslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = guesslon - 360. + else + temp_guesslon = guesslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = guesslon + endif + + jix = 0 + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + jloop: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = guesslat + dell*float(j) + + iix = 0 + +c vlat(jix) = rlatt + + iloop: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c if (cparm == 'vmag') then +c print *,' ' +c print '(a16,i6,a4,i6,2(a8,f8.3),a12,f8.3)' +c & ,'in find_max, i= ',i +c & ,' j= ',j,' rlatt= ',rlatt,' rlont= ',rlont +c & ,' 360-rlont= ',360.-rlont +c endif + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT: icvpret= ',icvpret + endif + + cycle iloop + endif + + call calcdist(rlont,rlatt,temp_guesslon,guesslat,dist,degrees) + if (dist .gt. rads) cycle iloop + + if (cparm == 'vmag') then + +c This next bit of code gets the ij coordinates for an 8x8 +c box around the current point under consideration. These ij +c coordinates are sent to barnes so that barnes only loops +c 64 times, as opposed to nearly 10,000 if the whole 97x97 +c array were sent. So, fix rlatt to the grid point just +c northward of rlatt and fix rlont to the grid point just +c eastward of rlont. Note that this makes for a modified +c barnes analysis in that we're sort of specifying ahead of +c time exactly which grid points will be included and we'll +c be excluding some points that would be near the periphery +c of each (rlont,rlatt)'s range, but as long as we're consis- +c tent and do it this way for each point, it's well worth the +c trade-off in cpu time. Parameter maxvgrid determines what +c size array to send to barnes (maxvgrid=8 means 8x8) + + jvlatfix = int((vmag_latmax - rlatt)/dy + 1.) + ivlonfix = int((rlont - temp_grid_minlon)/dx + 2.) +c ivlonfix = int((rlont - vmag_lonmin)/dx + 2.) + + ibeg = ivlonfix - (maxvgrid/2) + iend = ivlonfix + (maxvgrid/2 - 1) + jbeg = jvlatfix - (maxvgrid/2 - 1) + jend = jvlatfix + (maxvgrid/2) + + if (ibeg < 1 .or. jbeg < 1 .or. + & iend > imax .or. jend > jmax) then + + ! DO NOT quit if we find a boundary outside the grid + ! bounds. Rather, just set the J violating bound(s) to + ! the min or max limit, and for I bounds, allow the + ! program to continue down to subsequent code below, + ! provided it's a global grid. + +c print *,'!!! ' +c print *,'!!! Before vmag adjustments, boundaries are: ' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt,' dx= ',dx +c print *,'!!! temp_grid_minlon= ',temp_grid_minlon +c print *,'!!! vmag_latmax= ',vmag_latmax +c print *,'!!! ivlonfix = ',ivlonfix,' jvlatfix = ',jvlatfix +c print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax +c print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Vmag will not be computed for' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Vmag will not be computed for ' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,'!!! ' + print *,'!!! *AFTER* vmag adjustments, boundaries are: ' + print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax + print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + endif + + endif + + endif + + if (cparm == 'vmag') then + ri = re * 3 +c print '(a36,f10.4,a6,f10.4)' +c & ,' + before call to vmag barnes, re= ',re,' ri= ',ri + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,bskip1,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes...' + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After 1st findmax loop, # calls to barnes = ',ibct + print *,'Total # of barnes loop iterations = ',ibarnes_loopct + endif + +c + 55 format ('i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ',f7.3 + & ,' barnval= ',f11.5) + 56 format ('k= ',i3,' i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ' + & ,f7.3,' barnval= ',f11.5) + + if (ctlon < 0.) then + ! We have grid-wrapped to find the ctlon, which was found to be + ! < 0, so for reporting purposes and for the start of the next + ! loop, set ctlon to positive degress east. + ctlon = ctlon + 360. + endif + + if (cparm == 'zeta') then + + if ( verb .ge. 3 ) then + print *,'!!! Zeta check, fmax= ',fmax,' fmin= ',fmin + write (6,61) 360.-ctlon,ctlat,fmax*100000.,fmin*100000. + endif + + else + + if ( verb .ge. 3 ) then + write (6,63) 360.-ctlon,ctlat,fmax,fmin + endif + + endif + 61 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 63 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax = ',e16.3,' fmin = ',e16.3) + 111 format (i2,' rlont= ',f7.2,'W rlatt= ',f7.2,' zeta= ',f13.8) + +c Through interpolation, the grid for vmag has already been +c minimized considerably, we don't need to go through the 2nd part +c of this subroutine, which halves the grid spacing. + + if (nhalf < 1 .or. cparm == 'vmag') then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c ------------------------------------------------------------- +c If the grid spacing is +c fine enough (I've chosen 0.2-deg as a min threshold), there is +c no need to halve the grid more than 3 times, as halving a +c 0.2-deg grid 3 times gives a resolution of 0.025-deg (2.7 km), +c or a max error in the position estimate of 2.7/2 = 1.35 km. + + if ((dx+dy)/2. <= 0.2) then + if ((dx+dy)/2. <= 0.05) then + nhalf = 1 + else + nhalf = 2 + endif + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +ctpm npts = 3 + npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only +c do this once for this grid-refinement (even though the grid is +c redefined 3 times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to get_ij_bounds' + print *,'!!! just before nhalf loop. Stopping processing' + print *,'!!! for storm number ',ist + endif + + ifmret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + + if ( verb .ge. 3 ) then + print *,' ' + endif + + if ((dx+dy)/2. <= 1.25 .and. ri >= 300 .and. re >= 150) then + retmp = re + ritmp = ri + re = re * 0.5 + ri = ri * 0.5 + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re has been reduced' + print *,'from ',retmp,' to ',re,', and ri has been reduced ' + print *,'from ',ritmp,' to ',ri + endif + + else + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re and ri have NOT ' + print *,'been changed. re = ',re,' ri = ',ri + endif + + endif + + ibct=0 + ibarnes_loopct = 0 + do k=1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: find_maxmin kloop, k= ',i2,' ',i2.2,':' + & ,i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat + fmax = -1.0e+15; fmin = 1.0e+15 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'find_maxmin nhalf loop, cparm= ',cparm,' k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,iskip,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes, k= ',k + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop2 + enddo jloop2 + + if ( verb .ge. 3 ) then + if (cparm == 'zeta') then + write (6,71) k,360.-ctlon,ctlat,fmax*100000.,fmin*100000. + else + write (6,73) k,360.-ctlon,ctlat,fmax,fmin + endif + endif + + enddo + + 71 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 73 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax = ',e16.3,' fmin = ',e16.3) + + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ppp after 2nd findmax loop, # calls to barnes = ' + & ,ibct + print *,'ppp Total # of barnes loop iterations = ' + & ,ibarnes_loopct + endif + + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine barnes(flon,flat,rlon,rlat,iimax,jjmax,iibeg,jjbeg + & ,iiend,jjend,fxy,defined_pt,bskip,re,ri,favg,icount,ctype + & ,trkrinfo,iret) +c +c ABSTRACT: This routine performs a single-pass barnes anaylsis +c of fxy at the point (flon,flat). The e-folding radius (km) +c and influence radius (km) are re and ri, respectively. +c +c NOTE: The input grid that is searched in this subroutine is most +c likely NOT the model's full, original grid. Instead, a smaller +c subgrid of the original grid is searched. The upper left and +c lower right grid point indices are passed into this subroutine +c (iibeg, jjbeg, iiend, jjend) for this subgrid. These indices are +c determined in the subroutine get_ij_bounds, and the purpose of +c doing it this way is to limit the number of points for which the +c subroutine has to calculate distances (for a global 1 deg grid, +c the number of loop iterations is reduced from 65160 to somewhere +c around 600). +c +c NOTE: This subroutine will immediately exit with a non-zero +c return code if it tries to access a grid point that does not have +c valid data. This would happen in the case of a regional grid, if +c you try to access a point near the edge of the grid (remember that +c because of the interpolation for the regional grids, there will be +c areas around the edges that have no valid data). +c +c INPUT: +c flon Lon value for center point about which barnes anl is done +c flat Lat value for center point about which barnes anl is done +c rlon Array of lon values for each grid point +c rlat Array of lat values for each grid point +c iimax Max number of pts in x-direction on input grid +c jjmax Max number of pts in y-direction on input grid +c iibeg i index for grid point to start barnes anlysis (upp left) +c jjbeg j index for grid point to start barnes anlysis (upp left) +c iiend i index for last grid point in barnes anlysis (low right) +c jjend j index for last grid point in barnes anlysis (low right) +c fxy Real array of data on which to perform barnes analysis +c defined_pt Logical; bitmap array used for regional grids +c bskip integer to indicate number of grid points to skip during +c a barnes loop, in order to speed processing +c re input e-folding radius for barnes analysis +c ri input influence radius for searching for min/max +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, in +c this barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c favg Average value about the point (flon,flat) +c iret Return code from this subroutine +c + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real fxy(iimax,jjmax), rlon(iimax), rlat(jjmax) + real degrees + integer bskip + logical(1) defined_pt(iimax,jjmax) + character(*) ctype + +c -------------------------- + + res = re*re + wts = 0.0 + favg = 0.0 + + icount = 0 + + do jix=jjbeg,jjend,bskip + do iix=iibeg,iiend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > iimax) then + if (trkrinfo%gridtype == 'global') then + i = iix - iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i,' imax= ',iimax + print *,' ' + endif + + stop 97 + endif + endif + + icount = icount + 1 + + call calcdist(flon,flat,rlon(i),rlat(j),dist,degrees) + + if (dist .gt. ri) cycle + + if (defined_pt(i,j)) then + if (fxy(i,j) >-999.01 .and. fxy(i,j) <-998.99) then + ! This is a patch. Even though this (i,j) is a valid + ! point, its zeta value has been set to -999 because a + ! neighboring point in subroutine rvcal was found + ! to be out of the grid boundaries. + cycle + endif + wt = exp(-1.0*dist*dist/res) + wts = wts + wt + favg = favg + wt*fxy(i,j) + else + if (ctype == 'vitals') then + continue + else +carw print *,' ' +carw print *,'!!! UNDEFINED PT OUTSIDE OF GRID IN BARNES....' +carw print *,'!!! i= ',i,' j= ',j +carw print *,'!!! flon= ',flon,' flat= ',flat +carw print *,'!!! rlon= ',rlon(i),' rlat= ',rlat(j) +carw print *,'!!! re= ',re,' ri= ',ri +carw print *,'!!! EXITING BARNES....' +carw print *,' ' +carw iret = 95 +carw return + endif + endif + + enddo + enddo + + if (wts > 1.0E-5) then + favg = favg/wts + else + favg = 0.0 + endif + iret = 0 +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,rglatmax,rglatmin,rglonmax,rglonmin,geslon,geslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) +c +c ----------------------------------------------------------- +c ABSTRACT: This subroutine figures out, based on ri, dx and dy and +c the guess latitude and longitude positions, the farthest reaching +c grid points that are searchable by an analysis subroutine. The +c purpose is to return indices for a subgrid that is much smaller +c than the original, full grid. This smaller subgrid can then be +c passed to a subsequent analysis or interpolation subroutine, and +c work can be done on this smaller array. This can help save time, +c especially in the barnes analysis subroutine, as work will only +c be done on, say, a 20 x 20 array (400 pts) instead of on a +c 360 x 181 array (65160 pts). It's crucial, however, to make sure +c that the ibeg, jbeg, iend and jend are extended far enough out to +c fully encompass any search that would be done. Below is a +c diagram showing the different grids.... +c +c Full Global or Regional Model Grid (Grid F) -----------> +c ---------------------------------------------------------------- +c | | (ibeg,jbeg) | +c | | x = ij position that the | (Grid R) | +c | | geslat/geslon is fixed to. ._______________. | +c | | | | | +c | | Even though only the points | (Grid B) | | +c | | within Grid B will be checked | . . . . k | | +c v | later on for a max/min (in the | . . . . . | | +c | case of a subsequent call to | . . x . e | | +c | find_maxmin), the barnes anal- | . . . . . | | +c | ysis will include all pts sur- | . . . . . | | +c | rounding these Grid B points | | | +c | that are within a radius of ri. ._______________. | +c | So in the case of pt. k, that ri | +c | radius may extend all the way to the Grid R | | +c | boundary, thus we need to include those (iend,jend) | +c | points within our ibeg-jbeg-iend-jend bounds. | +c | | +c ---------------------------------------------------------------- +c +c Remember that the grids we deal with start north and increase +c south, so the northernmost latitude on the input grid will have +c a j index of 1. +c +c INPUT: +c npts Num pts from x to edge of max/min search grid (Grid B) +c (i.e., You define the size of Grid B by the value of +c npts that you pass into this subroutine). +c nhalf Number of times the grid spacing will be halved +c ri Radius of influence (for use in barnes analysis) +c imax Number of points in x-direction on original grid +c jmax Number of points in y-direction on original grid +c dx Input grid spacing in i-direction on original grid +c dy Input grid spacing in j-direction on original grid +c rglatmax Value of northern-most latitude on original grid +c rglatmin Value of southern-most latitude on original grid +c rglonmax Value of eastern-most longitude on original grid +c rglonmin Value of western-most longitude on original grid +c geslat Value of latitude of guess position of storm +c geslon Value of longitude of guess position of storm +c +c OUTPUT: +c ilonfix i index on full, input grid that storm is fixed to +c jlatfix j index on full, input grid that storm is fixed to +c ibeg i index for top left of sub-array (Grid R) of input grid +c iend i index for bot right of sub-array (Grid R) of input grid +c jbeg j index for top left of sub-array (Grid R) of input grid +c jend j index for bot right of sub-array (Grid R) of input grid +c igiret Return code from this subroutine +c + USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + real tmpangle +c + igiret = 0 +c +c -------------------------------------- +c GET BEGINNING AND ENDING J POINTS.... +c +c (1) Calculate number of searchable, max/min pts, that is, the pts +c from x to the edge of Grid B. +c (2) Calculate number of pts beyond the last search point in Grid +c B, but are within the bounds of Grid R and thus can be +c included in the barnes analysis. +c (3) Add (1) and (2) to get the max number of pts to subtract/add +c to x to get jbeg and jend. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Beginning of get_ij_bounds...' + print *,' geslat= ',geslat,' geslon= ',geslon + print *,' ' + endif + + +c If nhalf > 0: This occurs in the case of a call from fmax, when +c the grid spacing is halved nhalf times. In this case, we have to +c do extra work to figure out the maximum possible grid point. For +c this case: +c jhlatpts = # of grid pts to last possible search pt (from x to +c edge of Grid B in above diagram), plus a cushion. +c jripts = # of searchable grid points within radius ri of last +c possible search pt (num pts between edge of Grid B +c and edge of Grid R in above diagram), plus a cushion +c jbmaxlatpts = # of pts (in j direction) from x to the edge of +c Grid R to include in this subgrid. +c +c If nhalf = 0: In this case, the grid spacing will not be reduced, +c so the number of pts in j direction from x to the edge of Grid +c B will be the input parameter npts, and just multiply it by 2, +c and add 2 for a cushion to get jmaxlatpts. Typically, this sub +c is called from find_maxmin, and in that sub, the first time that +c this sub is called, nhalf will = 0. Then, after a first-shot +c center is found, the grid spacing will be cut in order to rerun +c barnes on a smaller grid, and that's when nhalf will be sent +c here as 3. +c + if (nhalf > 0) then + rdeg = 0.0 + do i = 1,nhalf + rdeg = rdeg + float(npts) * (1./(float(i)*2)) * (dx+dy)/2 + enddo + jhlatpts = ceiling(rdeg/dy) + 1 + jripts = ceiling((ri + 1.)/(dtk*dx)) + 1 + jbmaxlatpts = jhlatpts + jripts + else + jbmaxlatpts = npts * 2 + 2 + endif +c +c +c Roughly fix geslat to the grid point just poleward of geslat. +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' +++ Near top of get_ij_bounds, ' + print *,' +++ geslat= ',geslat,' geslon= ',geslon + print *,' +++ rglatmax= ',rglatmax,' rglatmin= ',rglatmin + print *,' +++ rglonmax= ',rglonmax,' rglonmin= ',rglonmin + print *,' +++ imax= ',imax,' jmax= ',jmax + print *,' +++ dx= ',dx,' dy= ',dy,' nhalf= ',nhalf + print *,' +++ npts= ',npts + if(nhalf>0) then + print *,' +++ jhlatpts= ',jhlatpts,' jripts= ',jripts + else + print *,' +++ nhalf<=0 so jhlatpts and jripts unused' + endif + print *,' +++ jbmaxlatpts= ',jbmaxlatpts + endif + + if (geslat >= 0.0) then + jlatfix = int((rglatmax - geslat)/dy + 1.) + else + jlatfix = ceiling((rglatmax - geslat)/dy + 1.) + endif + + if ( verb .ge. 3 ) then + print *,' +++ jlatfix= ',jlatfix + endif + + jbeg = jlatfix - jbmaxlatpts + jend = jlatfix + jbmaxlatpts + if (jbeg > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jbeg > jmax' + print *,'!!! jbeg = ',jbeg,' jmax= ',jmax + endif + + igiret = igiret + 1 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jend < 1, jend = ',jend + endif + + igiret = igiret + 1 + return + endif + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' +++ jbeg= ',jbeg,' jend= ',jend + endif + + ! If using a global grid, avoid using the pole points, or else + ! you'll get a cosfac = 0 and then a divide by zero!!! + + if (jend == jmax .and. rglatmin == -90.0) then + jend = jmax - 2 + endif + if (jbeg == 1 .and. rglatmax == 90.0) then + jbeg = 3 + endif + +c ----------------------------------------- +c NOW GET BEGINNING AND ENDING I POINTS.... +c +c Using the map factor (cos lat), figure out, based on ri, the +c max distance beyond the last search point in x-direction (in +c degrees) that could be searched at this guess latitude (geslat) +c (i.e., in the diagram above, the max num pts from pt. e eastward +c to the edge of Grid R). Calculate how many grid points that is, +c add 2 to it for a cushion, & add the number of points (npts) +c within the defined search grid (Grid B) to get ibmaxlonpts. +c +c April, 2007: A min statement was put on the calculation to +c derive dlon, since with that cosine in there, the values of +c of dlon could get pretty ridiculous as you approach the poles. +c Also, the cosine factor (cosfac) used to be computed at the +c most poleward latitude possible given the jend here. For +c similar concerns with cosines near the poles, I've scrapped +c this to instead compute the cosine factor at the input +c guess latitude. - tpm + + cosfac = cos (geslat * dtr) + tmpangle = cosfac * dtk + dlon = min((ri /tmpangle ),20.0) +c dlon = min((ri / (cosfac * dtk)),20.0) +c + if (nhalf > 0) then + ihlonpts = ceiling(rdeg/dx) + 1 + ibmaxlonpts = ihlonpts + ceiling(dlon/dx) + 2 + else + ibmaxlonpts = npts + ceiling(dlon/dx) + 2 + endif + + if ( verb .ge. 3 ) then + if(nhalf>0) then + print *,' +++ rdeg= ',rdeg,' ri= ',ri,' cosfac= ',cosfac + print *,' +++ dtr= ',dtr,' dtk= ',dtk,' dlon= ',dlon + else + print*,' +++ nhalf<=0 so rdeg,ri,cosfac,dtr,dtk,dlon unused' + endif + print *,' +++ ibmaxlonpts= ',ibmaxlonpts,' dx= ',dx,' dy= ',dy + endif + +c Roughly fix geslon to the grid point just EASTward of geslon. + + ilonfix = int((geslon - rglonmin)/dx + 2.) + + ibeg = ilonfix - ibmaxlonpts + iend = ilonfix + ibmaxlonpts + + if ( verb .ge. 3 ) then + print *,' +++ (orig) ilonfix= ',ilonfix + print *,' +++ (orig) ibeg= ',ibeg,' iend= ',iend + print *,' +++ ' + endif + + if (ibeg > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 1 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, ibeg > imax' + print *,'!!! for a non-global grid' + print *,'!!! ibeg = ',ibeg,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + ! For a regional grid, just set iend to be imax + iend = imax + endif + endif + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + ! For a regional grid, just set ibeg to be 1 + ibeg = 1 + endif + endif + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + + if ( verb .ge. 3 ) then + print *,'!!! ERROR in get_ij_bounds, iend < 1' + print *,'!!! for a non-global grid' + print *,'!!! iend = ',iend,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine check_bounds (guesslon,guesslat,ist,ifh,trkrinfo + & ,icbret) +c +c ABSTRACT: This subroutine checks to make sure that the requested +c storm is in fact within the model's grid boundaries; +c this is only a concern for the regional models. +c + USE def_vitals; USE grid_bounds; USE set_max_parms + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + if (trkrinfo%gridtype == 'regional') then + if (guesslon > glonmax .or. guesslon < glonmin .or. + & guesslat > glatmax .or. guesslat < glatmin) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is outside of grid' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + goto 125 + else + icbret = 0 + endif + endif + + ! We have encountered problems with global grids where we + ! continue tracking almost the whole way to the pole. While + ! that's nice to do that, it creates problems for array + ! indices, especially in subroutine getradii. So we will cut + ! our losses and eliminate tracking of storms within + ! 5 degrees of the pole for global grids. + + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'global')then + if (guesslat > 85.0 .or. guesslat < -85.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is too close to the' + print *,'!!! N or S Pole for global tracking.' + print *,'!!! STOPPING TRACKING FOR THIS STORM DUE TO POLE' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + else + icbret = 0 + endif + endif + + 125 continue +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calcdist(rlonb,rlatb,rlonc,rlatc,xdist,degrees) +c +c ABSTRACT: This subroutine computes the distance between two +c lat/lon points by using spherical coordinates to +c calculate the great circle distance between the points. +c Figure out the angle (a) between pt.B and pt.C, +c N. Pole then figure out how much of a % of a great +c x circle distance that angle represents. +c / \ +c b/ \ cos(a) = (cos b)(cos c) + (sin b)(sin c)(cos A) +c / \ +c pt./<--A-->\c NOTE: The latitude arguments passed to the +c B / \ subr are the actual lat vals, but in +c \ the calculation we use 90-lat. +c a \ +c \pt. NOTE: You may get strange results if you: +c C (1) use positive values for SH lats AND +c you try computing distances across the +c equator, or (2) use lon values of 0 to +c -180 for WH lons AND you try computing +c distances across the 180E meridian. +c +c NOTE: In the diagram above, (a) is the angle between pt. B and +c pt. C (with pt. x as the vertex), and (A) is the difference in +c longitude (in degrees, absolute value) between pt. B and pt. C. +c +c !!! NOTE !!! -- THE PARAMETER ecircum IS DEFINED (AS OF THE +c ORIGINAL WRITING OF THIS SYSTEM) IN KM, NOT M, SO BE AWARE THAT +c THE DISTANCE RETURNED FROM THIS SUBROUTINE IS ALSO IN KM. +c + USE trig_vals + + real degrees +c + if (rlatb < 0.0 .or. rlatc < 0.0) then + pole = -90. + else + pole = 90. + endif +c + distlatb = (pole - rlatb) * dtr + distlatc = (pole - rlatc) * dtr + difflon = abs( (rlonb - rlonc)*dtr ) +c + cosanga = ( cos(distlatb) * cos(distlatc) + + & sin(distlatb) * sin(distlatc) * cos(difflon)) + +c This next check of cosanga is needed since I have had ACOS crash +c when calculating the distance between 2 identical points (should +c = 0), but the input for ACOS was just slightly over 1 +c (e.g., 1.00000000007), due to (I'm guessing) rounding errors. + + if (cosanga > 1.0) then + cosanga = 1.0 + endif +cPENG added bug fixed on 04/28/2016-------------------------- + if (cosanga < -1.0) then + cosanga = -1.0 + endif +cPENG added on 04/28/2016-------------------------- + + degrees = acos(cosanga) / dtr + circ_fract = degrees / 360. + xdist = circ_fract * ecircum +c +c NOTE: whether this subroutine returns the value of the distance +c in km or m depends on the scale of the parameter ecircum. +c At the original writing of this subroutine (7/97), ecircum +c was given in km. +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine subtract_cor (imax,jmax,dy,level) +c +c ABSTRACT: This subroutine subtracts out the coriolis parameter +c from the vorticity values. It is needed because at the original +c writing of this system, all of the forecast centers who included +c vorticity included only absolute vorticity. +c + USE tracked_parms; USE trig_vals; USE grid_bounds + + implicit none + + integer :: i,j,imax,jmax,level + real :: dy,coriolis,rlat +c + do j=1,jmax + rlat = glatmax - ((j-1) * dy) + coriolis = 2. * omega * sin(rlat*dtr) + do i=1,imax + zeta(i,j,level) = zeta(i,j,level) - coriolis + enddo + enddo +c + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_grib_file_name (ifh,gfilename,ifilename) + +c ABSTRACT: This subroutine uses various input regarding the model +c and forecast hour and generates the name of the input grib file +c for this particular forecast hour. Remember that the lead time +c is in minutes and that 5 spaces must be reserved for the lead +c time (e.g., f00360). File name should be something that looks +c like either, e.g., "gfdl.6thdeg.katrina12l.2005082818.f00720", +c or "gfdl.6thdeg.2005082818.f00720" (the part in there with the +c storm name & ID is optional). The grib index file name should +c be exactly the same as the grib data file itself, but with the +c character string ".ix" added onto the end of the name. +c +c NOTE: Array iftotalmins is brought in via module tracked_parms. +c +C INPUT: +c ifh integer array index for current lead time +c +c OUTPUT: +c gfilename GRIB file name +c ifilename GRIB index file name + + USE gfilename_info; USE tracked_parms; USE atcf + USE verbose_output + + implicit none + + character(*) gfilename,ifilename + character cfmin*5,cymdh*10 + integer ifh,nlen1,nlen2,nlen3,nlen4,nlen5 + +c Convert integer minutes to 5-position character, with +c leading zeroes, and convert 10-digit integer date into +c 10-position character. Then trim the various input variables +c and combine all into the file name. + + write (cfmin,'(i5.5)') iftotalmins(ifh) + write (cymdh,'(i10.10)') atcfymdh + + nlen1 = len_trim(gmodname) + gfilename = trim(gmodname(1:nlen1)) + + nlen2 = len_trim(rundescr) + + gfilename = trim(gfilename(1:nlen1))//'.'//trim(rundescr(1:nlen2)) + + nlen3 = len_trim(atcfdescr) + nlen4 = len_trim(gfilename) + +c If an extension to the name with the ATCF or storm name descriptor +c was included, then add it to the name now. Otherwise, just add +c the starting date and the lead time in minutes. + + if (nlen3 > 0) then + gfilename = trim(gfilename(1:nlen4))//'.' + & //trim(atcfdescr(1:nlen3))//'.'//cymdh//'.f'//cfmin + else + gfilename = trim(gfilename(1:nlen4))//'.'//cymdh//'.f'//cfmin + endif + +c Create the name for the grib index file, which is just the name of +c the grib file, with "ix" added to the end of it. + + nlen5 = len_trim(gfilename) + ifilename = trim(gfilename(1:nlen5))//'.ix' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,72) 'gfilename',gfilename + write (6,72) 'ifilename',ifilename + endif + + 72 format (1x,'In get_grib_file_name, file name for ',a9 + & ,' is ',a120) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) +c +c ABSTRACT: This subroutine reads the input GRIB file for the +c tracked parameters. It then calls subroutines to convert the +c data from a 1-d array into a 2-d array if the read was successful. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature (I jerry-rigged this by storing +c the data as being at the 401 mb level.) +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +c +c INPUT: +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c inp of a derived type, contains user-input info +c lugb integer unit number of input grib file +c lugi integer unit number of input grib index file +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE verbose_output; USE params; USE grib_mod; USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + type (datecard) inp + type (gribfield) :: gfld,prevfld,holdgfld +c + integer, parameter :: jf=40000000 + integer, parameter :: nreadparms=17 + real, allocatable :: f(:) + real :: dmin,dmax,firstval,lastval + logical(1), allocatable :: lb(:) + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1) file_open + logical :: unpack=.true. + logical :: open_grb=.false. + character*1 :: lbrdflag + character*8 :: chparm(nreadparms) + CHARACTER(len=8) :: pabbrev + character (len=10) big_ben(3) + integer date_time(8) + integer,dimension(200) :: jids,jpdt,jgdt + integer :: listsec1(13), enable_timing + integer, intent(in) :: imax,jmax + integer igparm(nreadparms),iglev(nreadparms) + integer iglevtyp(nreadparms) + integer ig2_parm_cat(nreadparms),ig2_parm_num(nreadparms) + integer ig2_lev_val(nreadparms),ig2_lev_typ(nreadparms) +cPENG 04/18/2018 for CMC Det. and CMC ensemble data + integer ig2_lev_11_cmc(nreadparms),ig2_lev_val_cmc(nreadparms) + integer ig2_lev_11_cmcd(nreadparms),ig2_lev_val_cmcd(nreadparms) + + integer cpsig2_parm_cat(nlevs_cps),cpsig2_parm_num(nlevs_cps) +c integer cpsig2_lev_typ(nlevs_cps),cpsig2_lev_val(nlevs_cps) +cPENG------- + integer cpsig2_lev_10(nlevs_cps) + integer cpsig2_lev_11(nlevs_cps),cpsig2_lev_12(nlevs_cps) + + integer ec_igparm(nreadparms),ec_iglev(nreadparms) + integer ec_iglevtyp(nreadparms) + integer cpsgparm(nlevs_cps),cpsglev(nlevs_cps) + integer cpsglevtyp(nlevs_cps) + integer ec_cpsgparm(nlevs_cps) + integer jpds(200),jgds(200),kpds(200),kgds(200) + integer igvret,ifa,ila,ip,ifh,i,j,k,kj,iret,kf,lugb,lugi + integer jskp,jdisc,np + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer pdt_4p0_vert_level,pdt_4p0_vtime + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 +c + lbrdflag = 'n' + enable_timing=trkrinfo%enable_timing +c The following data statements contain the parameters that will be +c used to search the grib files. The first 9 parameters will all be +c used to locate the storm position. The last 4 parameters (500 mb +c u- and v-components and 10 m u- and v- components) will not be +c used for tracking, but only for helping to estimate the next first +c guess position (500 mb winds) and for estimating the max near- +c surface wind speeds in the vicinity of the storm (10 m winds). +c +c ** NOTE: iglevtyp(12 & 13) and iglev(12 & 13) are initialized to +c 0 just to satisfy the IBM xlf compiler, which barks about +c there being too few initial values in the list when I +c only had 11 values there -- even though the real +c initialization for these variables is done just about +c 10 lines below. +c +c ** NOTE: The new ECMWF hi-res data uses the ECMWF GRIB parameter +c ID table, which has different values than the NCEP +c table. Therefore, we needed to add the variables and +c data values for ec_igparm, ec_iglevtyp and ec_iglev. +c +c July 2007: Read statements added for GP height for cyclone +c phase space (CPS) algorithm. + +c data igparm /41,41,33,34,33,34,7,7,1,33,34,33,34,11,7,7,81/ + data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7,81/ + data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 + & ,100,100,100,1/ + data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500,401 + & ,500,200,0/ + + data cpsgparm /13*7/ + data ec_cpsgparm /13*156/ + data cpsglevtyp /13*100/ + data cpsglev /900,850,800,750,700,650,600,550,500,450,400 + & ,350,300/ + + data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 + & ,131,132,130,156,156,999/ + data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 + & ,100,100,100,999/ + data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 + & ,401,500,200,999/ + + data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' + & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' + & ,'vgrid','temp','gphgt','gphgt','lmask'/ + + data ig2_parm_cat /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2/ + data ig2_parm_num /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8/ + data ig2_lev_typ /100,100,100,100,100,100,100,100,101,103,103 + & ,100,100,100,100,100,-9999/ + data ig2_lev_val /850,700,850,850,700,700,850,700,0,10,10,500,500 + & ,401,500,200,-9999/ +cPENG 04/18/2018 for CMC Det. and CMC ensemble data + data ig2_lev_11_cmc /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0/ + data ig2_lev_val_cmc/85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999/ + + data ig2_lev_11_cmcd /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0/ + data ig2_lev_val_cmcd /85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999/ + + data cpsig2_parm_cat /13*3/ + data cpsig2_parm_num /13*5/ +c data cpsig2_lev_typ /13*100/ +c data cpsig2_lev_val /900,850,800,750,700,650,600,550,500,450,400 +c & ,350,300/ +cPENG---------------------------------- + data cpsig2_lev_10 /13*100/ + data cpsig2_lev_11 /13*0/ + data cpsig2_lev_12 /90000,85000,80000,75000,70000,65000 + & ,60000,55000,50000,45000,40000 + & ,35000,30000/ + +c Model numbers used: (1) AVN, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) Early Eta, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble, +c (13) SREF Ensemble, +c (14) NCEP Ensemble (from ensstat mean fields), +c (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) NCEP Ensemble RELOCATION +c (21) UKMET hi-res (from NHC) +c (23) FNMOC Ensemble +c (24) HWRF Basin-scale + + if (trkrinfo%gribver == 2) then + +c For GRIB2, we will check to see if the MSLP being searched for +c is the standard MSLP (MSLP parm ID = 1) or if it is the +c so-called "Eta" or "Membrane" MSLP reduction that is included +c in the output for some models (like GFS and GDAS). Note that +c for 10m winds, with GRIB2, so far with all of the GRIB2 model +c data we've seen to this point, they all have the same IDs for +c 10m winds for all models, so no need to break out by model +c like we do for GRIB v1 in the else portion of this if statement. + + ig2_parm_num(9) = trkrinfo%g2_mslp_parm_id ! 1 = standard MSLP + ! reduction, 192 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB2 read, MSLP ID = ig2_parm_num(9) = ' + & ,ig2_parm_num(9) + + endif + + else + +c For GRIB1, do the same check as done just above in the IF part +c of this IF statement, but note that we need to also check to +c see what the GRIB1 parm IDs are for the sfc wind level type +c and value. Most models list the level type as 105 (which means +c height above the ground) and then a level value of 10. But +c ECMWF and UKMET use a level type of 1 (which means ground or +c water surface) and a level value of 0. + + igparm(9) = trkrinfo%g1_mslp_parm_id ! 102 = standard MSLP + ! reduction, 130 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglev(10) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + iglev(11) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + + ec_iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglev(10) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + ec_iglev(11) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB1 read, MSLP ID = igparm(9) = ' + & ,igparm(9) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev type = ' + & ,iglevtyp(10) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev value = ' + & ,iglev(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev type = ' + & ,ec_iglevtyp(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev value = ' + & ,ec_iglev(10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata. A return' + print *,'code (iret) not equal to zero indicates that ' + print *,'subroutine getgb was unable to find the requested ' + print *,'parameter. This could be simply because the parm is ' + print *,'not included in the grib file (this is likely for ' + print *,'ECMWF data, as they limit what they send us), or it ' + print *,'could indicate a problem with the grib index file.' + endif + + + if (allocated(f)) deallocate(f) + if (allocated(lb)) deallocate(lb) + allocate (f(imax*jmax),stat=ifa) + allocate (lb(imax*jmax),stat=ila) + if (ifa /= 0 .or. ila /= 0) then + print *,' ' + print *,'!!! ERROR in getdata allocating f or lb array.' + print *,'!!! ifa = ',ifa,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + if (trkrinfo%gribver == 2) then + + ! Reading from a GRIB v2 file.... + + grib2_standard_parm_read_loop: do ip = 1,nreadparms + + if (ip == 17) then + if (trkrinfo%use_land_mask == 'y' .or. + & trkrinfo%use_land_mask == 'Y') then + continue + else + if (verb .ge. 3) then + print *,' ' + print *,'The use_land_mask flag has not been set to ' + print *,'y or Y, so we will not try to read it in... ' + print *,' ' + cycle grib2_standard_parm_read_loop + endif + endif + endif + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + if (ip == 17) then + jdisc=1 ! hydrological products. At this point, used only + ! for the land-sea mask. + else + jdisc=0 ! meteorological products + endif + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for input parameter by production template 4.0. This +c tave program is used primarily for temperature, but still we +c will leave that as a variable and not-hard wire it in case we +c choose to average something else in the future. + + ! We are looking for Temperature or GP Height here. This + ! block of code, or even the smaller subset block of code that + ! contains the JPDT(1) and JPDT(2) assignments, can of course + ! be modified if this program is to be used for interpolating + ! other variables.... + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = ig2_parm_cat(ip) + JPDT(2) = ig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + + JPDT(10) = ig2_lev_typ(ip) +cPENG 04/18/2018 CMC Det. and CMC ensemble data + if (inp%model == 16 ) then + JPDT(11) = ig2_lev_11_cmc(ip) + JPDT(12) = ig2_lev_val_cmc(ip) + elseif (inp%model == 15 ) then + JPDT(11) = ig2_lev_11_cmcd(ip) + JPDT(12) = ig2_lev_val_cmcd(ip) + else + JPDT(11) = 0 + if (JPDT(10) == 100) then ! isobaric surface + JPDT(12) = ig2_lev_val(ip) * 100 ! GRIB2 levels are in Pa + else + JPDT(12) = ig2_lev_val(ip) ! This is going to be either mslp + & ! or 10m winds. + endif + endif + + if ( verb_g2 .ge. 1 ) then + print *,'before getgb2 call, value of unpack = ',unpack + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is CLOSED' + endif + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is CLOSED' + endif + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,531) date_time(5),date_time(6),date_time(7) + 531 format (1x,'TIMING: before getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,532) date_time(5),date_time(6),date_time(7) + 532 format (1x,'TIMING: after getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call, value of unpacked = ' + & ,gfld%unpacked + print *,'after getgb2 call, gfld%ngrdpts = ',gfld%ngrdpts + print *,'after getgb2 call, gfld%ibmap = ',gfld%ibmap + endif + + if ( iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb2 found parm: ',chparm(ip) + print *,'+++ at level = ',ig2_lev_val(ip) + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + select case (chparm(ip)) + case ('absv') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpdt(12) == 20000 .or. jpdt(12) == 2) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb2 could not find parm: ' + & ,chparm(ip) + print *,'!!! at level = ',ig2_lev_val(ip) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif +c J.Peng---07/26/2019 to free the memory in reading GRIB2 data + call gf_free (gfld) + + enddo grib2_standard_parm_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c This is the GRIB2 reading section. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + grib2_cps_parm_lev_loop: do ip = 1,nlevs_cps + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + + jpds = -1 + jgds = -1 + j=0 + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = cpsig2_parm_cat(ip) + JPDT(2) = cpsig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + +c JPDT(10) = cpsig2_lev_typ(ip) + JPDT(10) = cpsig2_lev_10(ip) +cPENG-------------------------------------------- + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + JPDT(11) = cpsig2_lev_11(ip) + JPDT(12) = cpsig2_lev_12(ip) + endif + +c if (JPDT(10) == 100) then ! isobaric surface +c JPDT(12) = cpsig2_lev_val(ip) * 100 ! GRIB2 levels +c ! are in Pa +c else +c if (verb .ge. 3) then +c print *,' ' +c print *,'ERROR in getdata: JPDT(10) array value' +c print *,'should only be 100 in this CPS section' +c print *,'for GRIB2 data.' +c endif +c endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,731) date_time(5),date_time(6),date_time(7) + 731 format (1x,'TIMING: before getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn + & ,jgdt,unpack,krec,gfld,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,732) date_time(5),date_time(6),date_time(7) + 732 format (1x,'TIMING: after getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 (PHASE) in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call(PHASE),' + & ,' value of unpacked = ',gfld%unpacked + print *,'after getgb2 (PHASE) call, gfld%ngrdpts = ' + & ,gfld%ngrdpts + print *,'after getgb2 (PHASE) call, gfld%ibmap = ' + & ,gfld%ibmap + endif + + if (verb .ge. 3) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + +c Determine packing information from GRIB2 file. +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) + & then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ' + & ,gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ' + & ,gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned + ! from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do +c this once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) + & then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.' + & ,gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ' + & ,gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ' + & ,gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.' + & ,gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline + & ,gfld%ipdtmpl(1),gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,231) + 231 format (' rec# param level byy bmm bdd ' + & ,'bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin + & ,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo grib2_cps_parm_lev_loop + + endif + + endif + + else + + !---------------------------------- + ! Reading from a GRIB v1 file.... + !---------------------------------- + + grib1_read_loop: do ip = 1,nreadparms + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then ! ECMWF hi-res data uses ECMWF table + print *,' ' + print *,'WARNING: From the namelist, inp%model is set to a' + print *,' value of 4, which is for ECMWF, so in routine' + print *,' getdata_grib, the input jpds(5,6,7) parms are' + print *,' going to have values that are specific for' + print *,' ECMWF GRIB1 data.' + print *,' ' + jpds(5) = ec_igparm(ip) + jpds(6) = ec_iglevtyp(ip) + jpds(7) = ec_iglev(ip) + else ! All other models use NCEP-standard GRIB table + jpds(5) = igparm(ip) + jpds(6) = iglevtyp(ip) + jpds(7) = iglev(ip) + endif + + print *,' ' + print *,' --- Before getgb, jpds(5)= ',jpds(5) + print *,' --- , jpds(6)= ',jpds(6) + print *,' --- , jpds(7)= ',jpds(7) + + if (jpds(5) == 999) then + cycle + endif + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,831) date_time(5),date_time(6),date_time(7) + 831 format (1x,'TIMING: before getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + + if (enable_timing /= 0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,832) date_time(5),date_time(6),date_time(7) + 832 format (1x,'TIMING: after getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb call, j= ',j,' k= ',k + & ,' iftotalmins= ' + & ,iftotalmins(ifh),' parm # (ip) = ',ip,' iret= ',iret + else + print *,'After getgb call, j= ',j,' k= ',k,' ifhours= ' + & ,ifhours(ifh),' parm # (ip) = ',ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb found parm: ',chparm(ip) + print *,'+++ at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,29) + else + write (6,31) + endif + 29 format (' rec# parm# levt lev byy bmm bdd bhh fmin' + & ,' npts minval maxval') + 31 format (' rec# parm# levt lev byy bmm bdd bhh fhr ' + & ,' npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + + select case (chparm(ip)) + case ('absv') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpds(7) == 200) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb could not find parm: ',chparm(ip) + print *,'!!! at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib1_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + cps_grib1_lev_loop: do ip = 1,nlevs_cps + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then + ! Use different grib parm id for ECMWF GP height + jpds(5) = ec_cpsgparm(ip) + else + jpds(5) = cpsgparm(ip) + endif + jpds(6) = cpsglevtyp(ip) + jpds(7) = cpsglev(ip) + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,841) date_time(5),date_time(6),date_time(7) + 841 format (1x,'TIMING: before getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,842) date_time(5),date_time(6),date_time(7) + 842 format (1x,'TIMING: after getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,39) + else + write (6,41) + endif + 39 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fmin npts minval maxval') + 41 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fhr npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo cps_grib1_lev_loop + + endif + + endif + + endif +c + deallocate (f) + deallocate (lb) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) +c +c ABSTRACT: This subroutine reads the input NetCDF file for the +c tracked parameters for one lead time. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +c +c If the user has requested to check the cyclone phase space for +c this run (phaseflag set to 'y' and phasescheme set to 'cps'), +c then we need to have gp height data for 900-300 mb at every 50 +c mb. Some of those levels for gp height data were already read +c in with the read of the initial 17 parameters, but we will be +c sure to read in the others, if requested. +c +c INPUT: +c ncfile_id integer ID associated with the NetCDF file +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file +c itself in subroutine read_netcdf_fhours. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE netcdf_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo + integer, parameter :: nreadparms=17,nreadparms_cps=13 + real, allocatable :: f(:) + real :: dmin,dmax,xmissing_value,xfill_value + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + character*1 :: lbrdflag,match_check + character*30 :: chparm(nreadparms) + character*30 :: chparm_cps(nreadparms_cps) + integer, intent(in) :: ncfile_id,imax,jmax + integer :: igvret,ifa,ip,ifh,i,j,k,m,n,ncfile_tmax,nf_get_att_real + integer :: nf_get_att_double,nf_inq_attlen,imvlen,ifvlen + integer :: usertime,ncix,missing_val_length,nf_status + integer :: nf_inq_varid,varid +c + lbrdflag = 'n' + +cnc data cpsgparm /13*7/ +cnc data cpsglevtyp /13*100/ +cnc data cpsglev /900,850,800,750,700,650,600,550,500,450,400 +cnc & ,350,300/ + +c data chparm /'vort850','vort700' +c & ,'u850','v850','u700','v700' +c & ,'h850','h700','slp','u_ref','v_ref' +c & ,'u500','v500','tm'/ + +c Load the names of the NetCDF variables for the standard +c variables into the chparm array... + + chparm(1) = netcdfinfo%rv850name + chparm(2) = netcdfinfo%rv700name + chparm(3) = netcdfinfo%u850name + chparm(4) = netcdfinfo%v850name + chparm(5) = netcdfinfo%u700name + chparm(6) = netcdfinfo%v700name + chparm(7) = netcdfinfo%z850name + chparm(8) = netcdfinfo%z700name + chparm(9) = netcdfinfo%mslpname + chparm(10) = netcdfinfo%usfcname + chparm(11) = netcdfinfo%vsfcname + chparm(12) = netcdfinfo%u500name + chparm(13) = netcdfinfo%v500name + chparm(14) = netcdfinfo%tmean_300_500_name + chparm(15) = netcdfinfo%z500name + chparm(16) = netcdfinfo%z200name + chparm(17) = netcdfinfo%lmaskname + + + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata_netcdf.' + endif + + if (allocated(f)) deallocate(f) + allocate (f(imax*jmax),stat=ifa) + if (ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getdata_netcdf allocating f data array.' + print *,'!!! ifa = ',ifa + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + !--------------------------------------------------------------- + ! First go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match up + ! the lead times that were read in with the lead times that + ! we read in directly from the NetCDF file. Get the index from + ! the NetCDF file for that lead time and use that in the call to + ! the read routine (get_var3_tlev_double). + !--------------------------------------------------------------- + + usertime = iftotalmins(ifh) + + match_check = 'n' + + find_index_loop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + ncix = m + if (verb .ge. 1) then + print *,'+++ Time match in getdata_netcdf for usertime= ' + & ,usertime,' netcdf file index= ncix= ',ncix + endif + match_check = 'y' + exit find_index_loop + endif + + enddo find_index_loop + + if (match_check == 'n') then + print *,' ' + print *,'!!! ERROR in getdata_netcdf: ' + print *,' For a NetCDF file, the user has ' + print *,' requested to process a lead time, and that lead' + print *,' time does not exist in the NetCDF list of time' + print *,' values. ' + print *,' ifh= ',ifh + print *,' usertime= iftotalmins(ifh)= ',iftotalmins(ifh) + print *,' STOPPING....' + stop 99 + endif + + !--------------------------------------------------------------- + ! Now go through the read loop for the list of parameters + !--------------------------------------------------------------- + + netcdf_standard_parm_read_loop: do ip = 1,nreadparms + + if (chparm(ip) == 'X' .or. chparm(ip) == 'x') then + if (verb .ge. 3) then + print *,' ' + print *,'!!! NetCDF read NOT requested for parm # ',ip + endif + cycle netcdf_standard_parm_read_loop + else + if (verb .ge. 3) then + print *,' ' + print *,'+++ NetCDF read requested for parm # ',ip + & ,' ... parm= ',chparm(ip) + endif + endif + + ! Note that I am sending a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which we + ! want), depending on the model & grid, we may need to flip the + ! grid in the north-south direction. I already have a routine + ! for converting data from a 1-d to a 2-d array, and it has + ! the functionality for flipping a grid, so I programmed it as + ! getting a 1-d array from the netcdf read routine and send that + ! 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm(ip),imax,jmax,ncix + & ,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + + ! Need to get the value of the "missing_value" attribute for + ! this variable from the list of attributes in the NetCDF + ! file. Only do this for the first lead time, since the + ! value of the "missing_value" obviously will not change + ! with lead time. + +c nf_status = nf_inq_attlen (ncfile_id,varid,"missing_value" +c & ,imvlen) +c nf_status = nf_inq_attlen (ncfile_id,varid,"_FillValue" +c & ,ifvlen) + + ! These next two nf function calls retrieve the value of the + ! "missing_value" attribute from the list of attributes for + ! the given variable being read in. This is needed in order + ! to know if a non-valid point is being accessed, as for a + ! regional grid, like the nested fvGFS. In GRIB1/GRIB2 files, + ! such regions would be bitmapped out, but in a NetCDF file, + ! no such bitmap exists, so we have to check for missing + ! values. In case it's a moving grid, we need to do this + ! for every lead time, since the "map of missing values" + ! will shift with lead time. Once we have those missing + ! values, we can loop through them and fill the valid_pt + ! logical array so that, in the end, we will have the same + ! logical bitmap for masking out missing data that we have + ! with GRIB1/GRIB2 data. + + nf_status = nf_inq_varid (ncfile_id,chparm(ip),varid) + + print *,'nf_status from nf_inq_varid call = ',nf_status + + nf_status = nf_get_att_real (ncfile_id,varid,"missing_value" + & ,xmissing_value) + + print *,'nf_status from nf_get_att_real call = ',nf_status + +c nf_status = nf_get_att_real (ncfile_id,varid,"_FillValue" +c & ,xfill_value) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"missing_value",len=imvlen) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"_FillValue",len=ifvlen) +c +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + if (verb .ge. 3) then + write (6,31) + 31 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,33) ifhours(ifh),ifclockmins(ifh),ip,chparm(ip) + & ,dmin,dmax + 33 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,35) chparm(ip),xmissing_value + 35 format (' --- ',a30,' missing value = ',g12.4) + endif + + ! This call to conv1d2d_logic_netcdf creates + ! a logical bitmap, so that in case we have + ! regional (non-global) data and an irregular grid (e.g., + ! the FV3 nested grid), we can mask out grid points that + ! have missing values as their data values. There is not + ! actually a native logical bitmap in NetCDF, so we will + ! create one by examining the real data values and masking + ! out grid points that have missing values. + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic_netcdf (imax,jmax,f,valid_pt + & ,xmissing_value,need_to_flip_lats) + lbrdflag = 'y' + endif + + if (ip == 1) then ! 850 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else if (ip == 2) then ! 700 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + else if (ip == 3) then ! 850 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (ip == 4) then ! 850 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (ip == 5) then ! 700 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (ip == 6) then ! 700 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (ip == 7) then ! 850 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (ip == 8) then ! 700 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (ip == 9) then ! MSLP + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + else if (ip == 10) then ! Near-sfc (10m) u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + else if (ip == 11) then ! Near-sfc (10m) v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + else if (ip == 12) then ! 500 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else if (ip == 13) then ! 500 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else if (ip == 14) then ! 300-500 mb mean Temp + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + else if (ip == 15) then ! 500 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (ip == 16) then ! 200 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + else if (ip == 17) then ! Land-sea mask + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + else + + print *,'!!! NOTE: Parm not recognized. ' + print *,'!!! ip is > 17.... ip= ',ip + print *,'!!! Forecast time level = ',ifh + + endif + + endif + + enddo netcdf_standard_parm_read_loop + +c *--------------------------------------------------------------* +c If we are attempting to determine the cyclone structure using +c Hart's cyclone phase space, then read in data now that will +c allow us to do that. If we are instead just using the +c mid-level (300-500 mb) mean temperature to do that with a +c simple warm-core check, then that mean temperature field was +c already read in above in the read loop for the standard +c variables. The variables needed here for CPS are pretty +c straightforward: gp height every 50 mb from 300 to 900 mb. +c keep in mind that we have already read in a few of these +c gp height records for selected levels above. +c *--------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + chparm_cps(1) = netcdfinfo%z900name + chparm_cps(2) = netcdfinfo%z850name + chparm_cps(3) = netcdfinfo%z800name + chparm_cps(4) = netcdfinfo%z750name + chparm_cps(5) = netcdfinfo%z700name + chparm_cps(6) = netcdfinfo%z650name + chparm_cps(7) = netcdfinfo%z600name + chparm_cps(8) = netcdfinfo%z550name + chparm_cps(9) = netcdfinfo%z500name + chparm_cps(10) = netcdfinfo%z450name + chparm_cps(11) = netcdfinfo%z400name + chparm_cps(12) = netcdfinfo%z350name + chparm_cps(13) = netcdfinfo%z300name + + ! Read in GP Height levels for cyclone phase space... + + if (verb .ge. 3) then + print *,' ' + print *,'--- Reads for CPS parms follow...' + print *,' ' + endif + + netcdf_cps_parm_read_loop: do ip = 1,nreadparms_cps + + if (chparm_cps(ip) == 'X' .or. chparm_cps(ip) == 'x') then + if (verb .ge. 3) then + print *,'!!! ERROR: NetCDF read NOT requested for' + print *,'!!! CPS parm # ',ip + print *,'!!! You must have an error in your namelist.' + print *,'!!! You have requested to do cyclone phase' + print *,'!!! checking, so you need to include the ' + print *,'!!! NetCDF names for ALL requested gp height' + print *,'!!! variables from 900 to 300 mb, every 50 ' + print *,'!!! mb,in the namelist.' + print *,'!!! phaseflag is being set to NO (n), and ' + print *,'!!! phase-checking will NOT take place.' + print *,'!!! If you want to run again and just do ' + print *,'!!! phase-checking with a simple warm-core' + print *,'!!! check, then in the namelist set phaseflag' + print *,'!!! to y and set phasescheme to vtt.' + phaseflag = 'n' + endif + exit netcdf_cps_parm_read_loop + else + if (verb .ge. 3) then + print *,'+++ NetCDF read requested for cps parm # ',ip + & ,' ... parm= ',chparm_cps(ip) + endif + endif + + ! As above, we send a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which + ! we want), depending on the model & grid, we may need to + ! flip the grid in the north-south direction. I already + ! have a routine for converting data from a 1-d to a 2-d + ! array, and it has the functionality for flipping a grid, + ! so I programmed it as getting a 1-d array from the netcdf + ! read routine and send that 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm_cps(ip),imax + & ,jmax,ncix,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm_cps(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + nf_status = nf_inq_varid (ncfile_id,chparm_cps(ip),varid) + nf_status = nf_get_att_real (ncfile_id,varid + & ,"missing_value",xmissing_value) + + if (verb .ge. 3) then + write (6,231) + 231 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,233) ifhours(ifh),ifclockmins(ifh),ip + & ,chparm_cps(ip),dmin,dmax + 233 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,235) chparm_cps(ip),xmissing_value + 235 format (' --- ',a30,' missing value = ',g12.4) + endif + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo netcdf_cps_parm_read_loop + + endif + + endif + + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_ncdim1 (ncid,var1_name,nmax) +c +c ABSTRACT: This routine queries a netcdf file to get the +c value of a requested file dimension (e.g., imax, jmax) +c + implicit none + + include "netcdf.inc" + + integer, intent(in) :: ncid + character*(*), intent(in) :: var1_name + integer, intent(out) :: nmax + integer :: status, var1id + + status = nf_inq_dimid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + status = nf_inq_dimlen (ncid,var1id,nmax) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_ncdim1 +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_var1_double (ncid,var1_name,nmax,var1) +c +c ABSTRACT: This routine reads a netcdf file in order to return +c a 1-dimensional array of data. + + implicit none + + include "netcdf.inc" + + integer, intent(in):: ncid + character*(*), intent(in):: var1_name + integer, intent(in):: nmax + real, intent(out):: var1(nmax) + + integer :: status, var1id + + status = nf_inq_varid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) +! write(*,*) 'Got var1id', var1id + + status = nf_get_var_real (ncid,var1id,var1) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var1_double +c +c--------------------------------------------------------- +c +c--------------------------------------------------------- + subroutine get_var3_tlev_double (ncid,var3_name,imax,jmax,ncix + & ,var3,igvret) +c +c ABSTRACT: This routine reads a netcdf file and returns a +c 2-dimensional synoptic variable at a particular lead time. +c The lead time is specified by the ltix array, which is +c included in module tracked_parms and defined in subroutine +c read_fhours. +c +c PARAMETERS +c +c INPUT: +c ncid integer that contains the NetCDF file ID +c var3_name character name of NetCDF input file +c imax integer x-dimension of input data +c jmax integer y-dimension of input data +c ncix integer index of time level for where this time level +c actually is inside the NetCDF data. Do NOT confuse this +c with the index of where this forecast hour is in the +c user's list of input forecast hours, as they may be +c different. For example, the user may request times that +c are every 6 hours, but the NetCDF file might have times +c that are every hour, so the indices for those two arrays +c will be different. Be sure to use the one (ncix) that +c indicates where the data actually starts in the +c NetCDF file. +c +c OUTPUT: +c var3 real array with real values returned from NetCDF read +c igvret integer return code from this routine + + USE tracked_parms; USE verbose_output; USE netcdf_parms + + implicit none + + include "netcdf.inc" +c + integer, intent(in) :: ncid,ncix + character*(*), intent(in) :: var3_name + integer, intent(in) :: imax,jmax + real, intent(out) :: var3(imax,jmax) + integer :: istart(3),ilength(3) + integer :: status,var3id,igvret + + if (verb .ge. 3) then + print *,' ' + print *,'In get_var3_tlev_double, ncix= ',ncix + print *,' nctotalmins(ncix)= ',nctotalmins(ncix) + endif + + istart(1) = 1 + istart(2) = 1 + istart(3) = ncix + + ilength(1) = imax + ilength(2) = jmax + ilength(3) = 1 + + igvret = 0 + + status = nf_inq_varid (ncid,var3_name,var3id) + + if (status /= NF_NOERR) then + print *,' ' + print *,'NOTE: Could not find variable ',var3_name,' at time' + & ,' index ncix= ',ncix + & ,' nctotalmins(ncix)= ',nctotalmins(ncix) + + igvret = 92 + return + endif + + status = nf_get_vara_real (ncid,var3id,istart,ilength,var3) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var3_tlev_double +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine handle_netcdf_err (status) +c +c ABSTRACT: This subroutine is an error handling routine for NetCDF- +c related functions. + + implicit none + + include "netcdf.inc" + + integer status +c + if (status /= nf_noerr) then + print *,' ' + print *,'Tracker NetCDF error: ' + print *, nf_strerror(status) + stop 'Stopped' + endif + + end subroutine handle_netcdf_err +c +c------------------------------------------------------------------- +c +c------------------------------------------------------------------- + subroutine bitmapchk (n,ld,d,dmin,dmax) +c +c This subroutine checks the bitmap for non-existent data values. +c Since the data from the regional models have been interpolated +c from either a polar stereographic or lambert conformal grid +c onto a lat/lon grid, there will be some gridpoints around the +c edges of this lat/lon grid that have no data; these grid +c points have been bitmapped out by Mark Iredell's interpolater. +c To provide another means of checking for invalid data points +c later in the program, set these bitmapped data values to a +c value of -999.0. The min and max of this array are also +c returned if a user wants to check for reasonable values. +c + logical(1) ld + dimension ld(n),d(n) +c + dmin=1.E15 + dmax=-1.E15 +c + do i=1,n + if (ld(i)) then + dmin=min(dmin,d(i)) + dmax=max(dmax,d(i)) + else + d(i) = -999.0 + endif + enddo +c + return + end +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic (imax,jmax,lb1d,lb2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of logical data (lb1d) into a 2-dimensional output +c array (dimension imax,jmax) of logical data (lb2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c lb1d 1-d array containing logical bitmap values +c iscanflag This is kgds(11), an integer value in the GDS, +c which holds the scanning mode for the data values +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + logical(1) lb1d(imax*jmax),lb2d(imax,jmax) + logical(1) :: need_to_flip_lats + integer :: ilat,ilatix,ilon,imax,jmax +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + lb2d(ilon,ilatix) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + lb2d(ilon,ilat) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic_netcdf (imax,jmax,dat1d,lb2d + & ,xmissing_val,need_to_flip_lats) +c +c ABSTRACT: The purpose of this routine is to create a 2-d logical +c bitmap to be used for masking out regions with missing data, +c such as for a regional grid with irregular boundaries (such as +c we've seen for the regional / nested FV3). This bitmap will +c have the same functionality as a GRIB1/GRIB2 bitmap. The trick +c is that NetCDF does not have a logical bitmap within its +c definition, so we need to make one. We do this by reading in +c the "missing_value" attribute for any variable, then here we +c scan through all the data values retrieved from the NetCDF read, +c and then for all grid points with missing values we set the +c valid_pt flag to .false. +c +c Note the use of the need_to_flip_lats flag. This is in order to +c handle grids that are flipped. Most grids -- NCEP, UKMET, ECMWF +c -- have point (1,1) as the uppermost left point on the grid, and +c the data goes from north to south. Some grids -- GFDL and the +c new NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the need_to_flip_lats flag was set to TRUE in getgridinfo, meaning +c that we have northward scanning data, we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d array containing floating point data values +c xmissing_val real value of missing value for the given variable +c that was read in for the calling routine +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + USE verbose_output + + implicit none + + logical(1) lb2d(imax,jmax) + logical(1) need_to_flip_lats + integer ilat,ilatix,ilon,imax,jmax,tct,fct,mct + real dat1d(imax*jmax) + real xmissing_val +c + tct = 0 + fct = 0 + mct = 0 + + if (verb >= 3) then + print *,' ' + print *,'TOP of conv1d2d_logic_netcdf, xmissing_val= ' + & ,xmissing_val + print *,' ' + endif +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then + lb2d(ilon,ilatix) = .false. +c print *,'LBSF FLIP: ilon= ',ilon,' ilatix= ',ilatix +c fct = fct + 1 + else + lb2d(ilon,ilatix) = .true. +c tct = tct + 1 + endif + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then +c print *,'LBSF no-flip: ilon= ',ilon,' ilat= ',ilat + lb2d(ilon,ilat) = .false. +c fct = fct + 1 + else + lb2d(ilon,ilat) = .true. +c tct = tct + 1 + endif + enddo + enddo + + endif + +c print *,' ' +c print *,' LB STATS: tct= ',tct,' fct= ',fct,' mct= ',mct +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine conv1d2d_real (imax,jmax,dat1d,dat2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of real data (dat1d) into a 2-dimensional output +c array (dimension imax,jmax) of real data (dat2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d real array of data +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c dat2d 2-d real array of data +c + logical(1) :: need_to_flip_lats + real dat1d(imax*jmax),dat2d(imax,jmax) +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + dat2d(ilon,ilatix) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + dat2d(ilon,ilat) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_nlists (inp,trkrinfo,netcdfinfo) +c +c ABSTRACT: This subroutine simply reads in the namelists that are +c created in the shell script. Namelist datein contains the +c starting date information, plus the model identifier. Namelist +c stswitch contains the flags for processing for each storm. +c + USE inparms; USE set_max_parms; USE atcf; USE trkrparms; USE phase + USE structure; USE gfilename_info + USE verbose_output; USE waitfor_parms; USE netcdf_parms + USE tracking_parm_prefs + + implicit none + + integer ifh + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo +c + namelist/datein/inp + namelist/atcfinfo/atcfnum,atcfname,atcfymdh,atcffreq + namelist/trackerinfo/trkrinfo + namelist/phaseinfo/phaseflag,phasescheme,wcore_depth + namelist/structinfo/structflag,ikeflag + namelist/fnameinfo/gmodname,rundescr,atcfdescr + namelist/verbose/verb,verb_g2 + namelist/waitinfo/use_waitfor,wait_min_age,wait_min_size + & ,wait_max_wait,wait_sleeptime + & ,use_per_fcst_command,per_fcst_command + namelist/netcdflist/netcdfinfo + namelist/parmpreflist/user_wants_to_track_zeta850 + & ,user_wants_to_track_zeta700,user_wants_to_track_wcirc850 + & ,user_wants_to_track_wcirc700,user_wants_to_track_gph850 + & ,user_wants_to_track_gph700,user_wants_to_track_mslp + & ,user_wants_to_track_wcircsfc,user_wants_to_track_zetasfc + & ,user_wants_to_track_thick500850 + & ,user_wants_to_track_thick200500 + & ,user_wants_to_track_thick200850 + +c Set namelist default values: + use_per_fcst_command='t' + per_fcst_command=' ' + atcffreq=600 + trkrinfo%enable_timing=1 + trkrinfo%want_oci=.false. + trkrinfo%gribver=1 ! Set to GRIB1 as default, can be set to + ! something else in the namelist input. + + read (5,NML=datein,END=801) + 801 continue + read (5,NML=atcfinfo,END=807) + 807 continue + print *,'just before trackerinfo read namelist' + read (5,NML=trackerinfo,END=809) + 809 continue + print *,'just after trackerinfo read namelist' + read (5,NML=phaseinfo,END=811) + 811 continue + read (5,NML=structinfo,END=815) + 815 continue + read (5,NML=fnameinfo,END=817) + 817 continue + read (5,NML=waitinfo,END=821) + 821 continue + read (5,NML=netcdflist,END=823) + 823 continue + read (5,NML=parmpreflist,END=825) + 825 continue + read (5,NML=verbose,END=819,ERR=833) + 819 continue + goto 837 + 833 continue + verb = 1 + 837 continue + + print *,'in read_nlists, verb= ',verb + + if ( verb .ge. 0 ) then + print *,' ' + print *,'After datein namelist in trak.f, namelist ' + & ,'parms follow:' + print *,'Forecast initial year = byy = ',inp%byy + print *,'Forecast initial month = bmm = ',inp%bmm + print *,'Forecast initial day = bdd = ',inp%bdd + print *,'Forecast initial hour = bhh = ',inp%bhh + print *,'Forecast model identifier = model= ',inp%model + print *,'Forecast model type = modtyp= ',inp%modtyp + print *,'Forecast model data lead time units= lt_units= ' + & ,inp%lt_units + print *,'Forecast model data sequencing setup= file_seq= ' + & ,inp%file_seq + print *,'Forecast model nest type = ',inp%nesttyp +c + print *,' ' + print *,'Values read in from atcfinfo namelist: ' + write (6,89) atcfnum,atcfname + write (6,90) atcfymdh + write (6,92) atcffreq + 89 format ('ATCF ID = ',i2,' ATCF Name = ',a4) + 90 format ('ATCF date (initial date on output atcf records) = ' + & ,i10) + 92 format ('ATCF output frequency (in hours*100) = atcffreq = ',i6) +c + print *,' ' + print *,'Values read in from trackerinfo namelist follow: ' + write (6,101) ' western boundary = westbd = ',trkrinfo%westbd + write (6,101) ' eastern boundary = eastbd = ',trkrinfo%eastbd + write (6,101) ' northern boundary = northbd = ',trkrinfo%northbd + write (6,101) ' southern boundary = southbd = ',trkrinfo%southbd + write (6,102) ' tracker type = ',trkrinfo%type + write (6,103) ' mslp threshold = mslpthresh = ' + & ,trkrinfo%mslpthresh + write (6,120) ' Flag for using backup mslp gradient check= ' + & ,'use_backup_mslp_grad_check = ' + & ,trkrinfo%use_backup_mslp_grad_check + write (6,103) ' v850 threshold = v850thresh = ' + & ,trkrinfo%v850thresh + write (6,122) ' Flag for using backup 850 mb Vt check= ' + & ,'use_backup_850_vt_check = ' + & ,trkrinfo%use_backup_850_vt_check + write (6,123) ' Max allowable distance between the ' + & ,'tracker-found fixes for mslp and 850 zeta = ' + & ,trkrinfo%max_mslp_850 + write (6,104) ' model grid type = ',trkrinfo%gridtype + write (6,101) ' Contour interval to be used = ',trkrinfo%contint + write (6,106) ' Flag for whether or not roci will be computed' + & ,' and written out for tracker-type case = ' + & ,trkrinfo%want_oci + write (6,105) ' Flag for whether or not vitals will be written ' + & ,'out = ',trkrinfo%out_vit + write (6,109) ' Flag for whether or not a land mask will be ' + & ,'used for tcgen candidate low filtering = ' + & ,trkrinfo%use_land_mask + write (6,110) ' Flag for input data type (grib or netcdf) = ' + & ,trkrinfo%inp_data_type + write (6,107) ' Flag for which GRIB version (1 or 2) the input' + & ,' data will be in = ',trkrinfo%gribver + write (6,108) ' Flag for input GRIB2 JPDTN (0 or 1) = ' + & ,trkrinfo%g2_jpdtn + write (6,112) ' Flag for input GRIB2 MSLP ID (1 or 192) = ' + & ,trkrinfo%g2_mslp_parm_id + write (6,114) ' Flag for input GRIB1 MSLP ID (102 or 130) = ' + & ,trkrinfo%g1_mslp_parm_id + write (6,116) ' Flag for input GRIB1 sfcwind level type ' + & ,'(PDS Octet 10... should be 1 or 105) = ' + & ,trkrinfo%g1_sfcwind_lev_typ + write (6,118) ' Flag for input GRIB1 sfcwind level value ' + & ,'(PDS Octets 11 & 12... usually 0 or 10) = ' + & ,trkrinfo%g1_sfcwind_lev_val + + 101 format (a31,f7.2) + 102 format (a16,a7) + 103 format (a31,f7.4) + 104 format (a19,a8) + 106 format (a46,a41,L1) + 105 format (a48,a6,a1) + 109 format (a45,a41,a1) + 110 format (a45,a6) + 107 format (a47,a19,i1) + 108 format (a39,i2) + 112 format (a43,i4) + 114 format (a45,i4) + 116 format (a41,a39,i4) + 118 format (a42,a43,i4) + 120 format (a44,a29,a1) + 122 format (a40,a26,a1) + 123 format (a36,a44,f7.1) + + print *,' ' + print *,' ' + print *,'Values read in from netcdflist namelist: ' + print *,' ' + write (6,300) netcdfinfo%num_netcdf_vars ! Total *possible* + ! number of input NetCDF variables, + ! including those that are included + ! in the input file and those that + ! are not. + write (6,370) netcdfinfo%netcdf_filename ! full path filename + write (6,301) + write (6,302) netcdfinfo%rv850name ! 850 mb rel vort + write (6,304) netcdfinfo%rv700name ! 700 mb rel vort + write (6,306) netcdfinfo%u850name ! 850 mb u-comp + write (6,308) netcdfinfo%v850name ! 850 mb v-comp + write (6,310) netcdfinfo%u700name ! 700 mb u-comp + write (6,312) netcdfinfo%v700name ! 700 mb v-comp + write (6,314) netcdfinfo%z850name ! 850 mb gp height + write (6,316) netcdfinfo%z700name ! 700 mb gp height + write (6,318) netcdfinfo%mslpname ! mslp + write (6,320) netcdfinfo%usfcname ! near-sfc u-comp + write (6,322) netcdfinfo%vsfcname ! near-sfc v-comp + write (6,324) netcdfinfo%u500name ! 500 mb u-comp + write (6,326) netcdfinfo%v500name ! 500 mb v-comp + write (6,328) netcdfinfo%tmean_300_500_name !Mean T @ 300-500 mb + write (6,330) netcdfinfo%z500name ! 500 mb gp height + write (6,332) netcdfinfo%z200name ! 200 mb gp height + write (6,334) netcdfinfo%lmaskname ! Land mask + write (6,336) netcdfinfo%z900name ! 900 mb gp height + write (6,338) netcdfinfo%z800name ! 800 mb gp height + write (6,340) netcdfinfo%z750name ! 750 mb gp height + write (6,342) netcdfinfo%z650name ! 650 mb gp height + write (6,344) netcdfinfo%z600name ! 600 mb gp height + write (6,346) netcdfinfo%z550name ! 550 mb gp height + write (6,348) netcdfinfo%z450name ! 450 mb gp height + write (6,350) netcdfinfo%z400name ! 400 mb gp height + write (6,352) netcdfinfo%z350name ! 350 mb gp height + write (6,354) netcdfinfo%z300name ! 300 mb gp height + write (6,355) netcdfinfo%time_name ! Name of time variable + ! (usually it is "time") + write (6,356) netcdfinfo%lon_name ! longitudes + write (6,358) netcdfinfo%lat_name ! latitudes + write (6,359) netcdfinfo%time_units ! This will be either "days" + ! or "hours". If it's "hours", + ! then all the time data values + ! are for hours since the initial + ! time. Same thing for "days", + ! however if it is "days", then + ! know that a value of 0.25 will + ! be the same as a 6-hour lead + ! time. + + 300 format ('Total *possible* number of input NetCDF variables,' + & ,/,' including those that are included in the input' + & ,/,' NetCDF file and those that are not = ',i4) + 370 format ('Input NetCDF filename = ',a180) + 301 format (' ',/ + & ,'List of NetCDF variables follows. A value of X ',/ + & ,'indicates the variable is not included in the ',/ + & ,'input file and no attempt will be made to read in ',/ + & ,'that variable: ',/,' ') + 302 format ('NetCDF variable name for 850 mb vort = ',a30) + 304 format ('NetCDF variable name for 700 mb vort = ',a30) + 306 format ('NetCDF variable name for 850 mb u-comp = ',a30) + 308 format ('NetCDF variable name for 850 mb v-comp = ',a30) + 310 format ('NetCDF variable name for 700 mb u-comp = ',a30) + 312 format ('NetCDF variable name for 700 mb v-comp = ',a30) + 314 format ('NetCDF variable name for 850 mb gp height = ',a30) + 316 format ('NetCDF variable name for 700 mb gp height = ',a30) + 318 format ('NetCDF variable name for MSLP = ',a30) + 320 format ('NetCDF variable name for near-sfc u-comp = ',a30) + 322 format ('NetCDF variable name for near-sfc v-comp = ',a30) + 324 format ('NetCDF variable name for 500 mb u-comp = ',a30) + 326 format ('NetCDF variable name for 500 mb v-comp = ',a30) + 328 format ('NetCDF variable name for 300-500 mb Mean T = ',a30) + 330 format ('NetCDF variable name for 500 mb gp height = ',a30) + 332 format ('NetCDF variable name for 200 mb gp height = ',a30) + 334 format ('NetCDF variable name for land-sea mask = ',a30) + 336 format ('NetCDF variable name for 900 mb gp height = ',a30) + 338 format ('NetCDF variable name for 800 mb gp height = ',a30) + 340 format ('NetCDF variable name for 750 mb gp height = ',a30) + 342 format ('NetCDF variable name for 650 mb gp height = ',a30) + 344 format ('NetCDF variable name for 600 mb gp height = ',a30) + 346 format ('NetCDF variable name for 550 mb gp height = ',a30) + 348 format ('NetCDF variable name for 450 mb gp height = ',a30) + 350 format ('NetCDF variable name for 400 mb gp height = ',a30) + 352 format ('NetCDF variable name for 350 mb gp height = ',a30) + 354 format ('NetCDF variable name for 300 mb gp height = ',a30) + 355 format ('NetCDF variable name for time = ',a30) + 356 format ('NetCDF variable name for longitudes = ',a30) + 358 format ('NetCDF variable name for latitudes = ',a30) + 359 format ('NetCDF time value (hours|days) = ',a30) + + print *,' ' + print *,' ' + print *,'Values read in from parmpreflist namelist: ' + print *,' ' + write (6,402) user_wants_to_track_zeta850 + write (6,404) user_wants_to_track_zeta700 + write (6,406) user_wants_to_track_wcirc850 + write (6,408) user_wants_to_track_wcirc700 + write (6,410) user_wants_to_track_gph850 + write (6,412) user_wants_to_track_gph700 + write (6,414) user_wants_to_track_mslp + write (6,416) user_wants_to_track_wcircsfc + write (6,418) user_wants_to_track_zetasfc + write (6,420) user_wants_to_track_thick500850 + write (6,422) user_wants_to_track_thick200500 + write (6,424) user_wants_to_track_thick200850 + + 402 format ('user_wants_to_track_zeta850= ',a2) + 404 format ('user_wants_to_track_zeta700= ',a2) + 406 format ('user_wants_to_track_wcirc850= ',a2) + 408 format ('user_wants_to_track_wcirc700= ',a2) + 410 format ('user_wants_to_track_gph850= ',a2) + 412 format ('user_wants_to_track_gph700= ',a2) + 414 format ('user_wants_to_track_mslp= ',a2) + 416 format ('user_wants_to_track_wcircsfc= ',a2) + 418 format ('user_wants_to_track_zetasfc= ',a2) + 420 format ('user_wants_to_track_thick500850= ',a2) + 422 format ('user_wants_to_track_thick200500= ',a2) + 424 format ('user_wants_to_track_thick200850= ',a2) + + print *,' ' + print *,'Values read in from phaseinfo namelist: ' + write (6,211) phaseflag,phasescheme + write (6,212) wcore_depth + 211 format ('Storm phase flag = ',a1,' Phase scheme = ',a4) + 212 format ('Storm phase, warm core depth (wcore_depth) = ',f7.2) + + print *,' ' + print *,'Values read in from structinfo namelist: ' + write (6,93) structflag + write (6,95) ikeflag + 93 format ('Structure flag = ',a1) + 95 format ('IKE flag = ',a1) + + print *,' ' + print *,'Values read in for grib file name from fnameinfo' + & ,' namelist: ' + write (6,131) gmodname + write (6,133) rundescr + write (6,135) atcfdescr + 131 format ('Model name description = gmodname = ',a4) + 133 format ('Forecast run description = rundescr = ',a40) + 135 format ('Optional ATCF / Storm name description = atcfdescr = ' + & ,a40) + + print *,' ' + print *,'Value read in for verbose output for most output:' + write (6,141) verb + 141 format ('Value read in for verbose flag = verb = ',i2) + + print *,' ' + print *,'Value read in for verbose output for grib2 output:' + write (6,142) verb_g2 + 142 format ('Value read in for GRIB2 verbose flag = verb_g2 = ',i2) + + print *,' ' + print *,'Values read in from waitinfo namelist:' + write (6,151) use_waitfor + write (6,152) wait_min_age + write (6,153) wait_min_size + write (6,154) wait_max_wait + write (6,155) wait_sleeptime + if(len_trim(per_fcst_command)>0) then + write (6,156) trim(per_fcst_command) + else +c No command specified, so disable the feature + use_per_fcst_command='n' + endif + 151 format ('Flag for input file waiting = use_waitfor = ',a1) + 152 format ('min age (time in seconds since last mod) = ' + & ,'wait_min_age = ',i8) + 153 format ('min file size in bytes = wait_min_size = ',i12) + 154 format ('max number of seconds to wait for each file = ' + & ,'wait_max_wait = ',i6) + 155 format ('number of seconds to sleep between checks = ' + & ,'wait_sleeptime = ',i6) + 156 format ('command to run after every forecast time = "',A,'"') +c + if (use_waitfor == 'y') then + if (inp%file_seq == 'multi') then + continue + else + print *,' ' + print *,'!!! ERROR: The use_waitfor flag is set to "y".' + print *,' This requires that the inp%file_seq flag be' + print *,' set to "multi", but you have specified ' + print *,' something else. ' + print *,' inp%file_seq = ',inp%file_seq + print *,' STOPPING....' + print *,' ' + STOP 95 + endif + endif +c + endif + return + end +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_fhours (ifhmax) +c +c ABSTRACT: This subroutine reads in a text file that contains the +c forecast times that will be read in. The format of the file is +c in "MMMMM", i.e., minutes, for example, for a forecast going out +c to 120h, the file would look like this: +c +c For reference, here +c are the times that +c match up with the +c minutes on the left: +c +c 1 0 0:00 +c 2 240 4:00 +c 3 270 4:30 +c 4 300 5:00 +c 5 330 5:30 +c 6 360 6:00 +c 7 600 10:00 +c 8 630 10:30 +c 9 660 11:00 +c 10 690 11:30 +c 11 720 12:00 +c 12 960 16:00 +c 13 990 16:30 +c . . . +c . . . +c . . . +c 87 7200 120:00 +c +c Note that we are now allowing for sub-hourly time intervals. +c + USE tracked_parms + USE verbose_output + + implicit none +c + integer, parameter :: iunit_fh=15 + integer itmphrs(750),itmpmins(750),input_mins(750),itmpltix(750) + integer ifhmax,inphr,inpmin,ict,i,ifa,ifma,icma,ira,inpltix,ila + real xminfract + + itmphrs = -99 + itmpmins = -99 + + if (allocated(ifhours)) deallocate (ifhours) + if (allocated(iftotalmins)) deallocate (iftotalmins) + if (allocated(ifclockmins)) deallocate (ifclockmins) + if (allocated(fhreal)) deallocate (fhreal) + if (allocated(ltix)) deallocate (ltix) + + ict = 0 + do while (.true.) + + if ( verb .ge. 3 ) then + print *,'Top of while loop in read_fhours' + endif + + read (iunit_fh,85,end=130) inpltix,inpmin + write (6,85) inpltix,inpmin + + if (inpmin >= 0 .and. inpmin < 150000) then + ict = ict + 1 + itmpltix(ict) = inpltix + itmphrs(ict) = inpmin / 60 + itmpmins(ict) = mod(inpmin,60) + input_mins(ict) = inpmin + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Input minutes not between 0 and 150000' + print *,'!!! inpmin= ',inpmin + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + if ( verb .ge. 3 ) then + print *,'readloop, ict= ',ict,' inpmin= ',inpmin + endif + + enddo + + 130 continue + + ifhmax = ict + + 85 format (i4,1x,i5) + + if ( verb .ge. 3 ) then + print *,' ' + endif + + allocate (ifhours(ifhmax),stat=ifa) + allocate (iftotalmins(ifhmax),stat=ifma) + allocate (ifclockmins(ifhmax),stat=icma) + allocate (fhreal(ifhmax),stat=ira) + allocate (ltix(ifhmax),stat=ila) + if (ifa /= 0 .or. ifma /= 0 .or. icma /= 0 .or. ira /= 0 .or. + & ila /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_fhours allocating either ifhours,' + print *,'!!! iftotalmins, ifclockmins or fhreal.' + print *,'!!! ifa = ',ifa,' ifma= ',ifma,' ira= ',ira + print *,'!!! icma= ',icma,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + do i = 1,ifhmax + + ltix(i) = itmpltix(i) + xminfract = float(itmpmins(i)) / 60. + fhreal(i) = float(itmphrs(i)) + xminfract + ifhours(i) = itmphrs(i) + ifclockmins(i) = itmpmins(i) + iftotalmins(i) = input_mins(i) + + if (i > 1) then + if (fhreal(i) > fhreal(i-1)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: In read_fhours, the time read in ' + print *,'!!! is not greater than the previous time.' + print *,'!!! i= ',i + print *,'!!! fhreal(i)= ',fhreal(i) + print *,'!!! fhreal(i-1)= ',fhreal(i-1) + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + endif + + if ( verb .ge. 3 ) then + write (6,87) i,ltix(i),iftotalmins(i),fhreal(i),ifhours(i) + & ,ifclockmins(i) + endif + + enddo + + 87 format (1x,'i= ',i3,' input lead time index= ',i4,' minutes= ' + & ,i5,' real_lead_time= ',f6.2,' clock_lead_time= ',i3,':' + & ,i2) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_tcv_card1 (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + logical(1) :: vit_file_exists + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + + ! Check to see if the TC Vitals file exists. If so, then open it + ! using the unit specified in lucard. + + inquire (file="tcvit_rsmc_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for existing, RSMC-numbered' + & ,' storms exists and will be opened with ' + & ,' unit= lucard= ',lucard + endif + + open (unit=lucard,file="tcvit_rsmc_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_rsmc_storms.txt has ' + print *,' been opened with unit= lucard= ',lucard + endif + + else + + if (trkrinfo%type == 'tracker') then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card. The fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. This TC Vitals file is needed for' + print *,'!!! a tracker case. Check to see that the ' + print *,'!!! TC Vitals file exists in this directory and' + print *,'!!! is named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING....' + print *,'!!! ' + iret=99 + return + endif + + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! NOTE: In read_tcv_card, the fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. While this TC Vitals file is ' + print *,'!!! needed for tracker cases, you are running' + print *,'!!! either a midlat or tcgen case here, and so ' + print *,'!!! that file is not needed... although you can ' + print *,'!!! run with using tc vitals for those genesis' + print *,'!!! cases if you want to. You may want to check' + print *,'!!! and make sure this is what you intend. If ' + print *,'!!! you do want to use it, the TC Vitals file ' + print *,'!!! should be in this directory and it should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! ' + endif + + endif + + endif + + ii=1 + + if (vit_file_exists) then + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + 801 continue + endif + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + + maxstorm = numtcv + + if (maxstorm > 0) then + continue + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING...' + print *,'!!! ' + iret=99 + return + endif + endif + + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! Stopping....' + print *,' ' + endif + + iret = 99 + return + + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card opening rsmc TC vitals' + print *,'!!! file named tcvit_rsmc_storms.txt. A file with' + print *,'!!! that name needs to be in your working directory.' + print *,'!!! It should contain the input TC vitals for ' + print *,'!!! already-existing storms that have rsmc-issued' + print *,'!!! storm IDs.' + endif + + iret = 97 + return + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine read_gen_vitals1(lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + logical(1) :: vit_file_exists + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + + ! Check to see if the genesis TC Vitals file exists. If so, then + ! open it using the unit specified in lgvcard. + + inquire (file="tcvit_genesis_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for genesis' + & ,' storms exists and will be opened with ' + & ,' unit= lgvcard= ',lgvcard + endif + + open (unit=lgvcard,file="tcvit_genesis_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_genesis_storms.txt has ' + print *,' been opened with unit= lgvcard= ',lgvcard + endif + + endif + + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + + ii = numtcv + 1 + + if (vit_file_exists) then + + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1 + & ,1x,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + endif + + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals opening genesis vitals' + print *,'!!! file named tcvit_genesis_storms.txt' + endif + + iret = 97 + return + + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just read the +c grib file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE tracked_parms; USE inparms + USE verbose_output; USE params; USE grib_mod + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + + type(gribfield) :: gfld,prevfld,holdgfld + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1), allocatable :: lb(:) + logical :: unpack=.true. + logical :: open_grb=.false. + CHARACTER(len=8) :: pabbrev + integer,dimension(200) :: jids,jpdt,jgdt + integer, parameter :: jf=40000000 + integer :: listsec1(13) + integer pdt_4p0_vert_level,pdt_4p0_vtime + real xhold,xlondiff,xlatdiff,temp,firstval,lastval + real, allocatable :: f(:) + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer jpds(200),jgds(200),igetpds(200),igetgds(200) + integer, intent(in) :: ifh + integer, intent(out) :: imax,jmax + integer iia,ija,ila,midi,midj,i,j,iix,jix,ifa,iret + integer iscanflag,iggret,kf,k,lugb,lugi,jskp,jdisc + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 + + iggret = 0 + + allocate (lb(jf),stat=ila); allocate (f(jf),stat=ifa) + if (ila /= 0 .or. ifa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating either lb or f' + print *,'!!! ila = ',ila,' ifa= ',ifa + endif + iggret = 97 + return + endif + + if (trkrinfo%gribver == 2) then + + ! Search for a record from a GRIB2 file + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 ! meteorological products + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for Temperature or GP Height by production template.... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + ! Request a record on a lat/lon grid. + + jgdtn = 0 + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpdt(8) = 0 + jpdt(9) = iftotalmins(ifh) + else + jpdt(8) = 1 + jpdt(9) = ifhours(ifh) + endif + + if (verb >= 3) then + print *,'before getgb2 call, lugb= ',lugb,' lugi= ',lugi + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + if ( iret.ne.0) then + print *,' ' + print *,' ERROR: getgb2 error in getgridinfo = ',iret + print *,' FATAL ERROR: cannot proceed without info ' + print *,' from getgridinfo. STOPPING....' + stop 95 + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if ( verb_g2 .ge. 1 ) then + print *,' ' + print *,' -- BEGIN getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'PDT num= gfld%ipdtnum= ',gfld%ipdtnum + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + imax = gfld%igdtmpl(8) + jmax = gfld%igdtmpl(9) + dx = float(gfld%igdtmpl(17))/1.e6 + dy = float(gfld%igdtmpl(17))/1.e6 + kf = gfld%ngrdpts + + holdgfld = gfld + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + + endif + + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' -- END getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' ' + print *,' ' + + endif + + need_to_flip_lons = .false. + + iscanflag = gfld%igdtmpl(19) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(gfld%igdtmpl(12))/1.e6 + glatmax = float(gfld%igdtmpl(15))/1.e6 + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(gfld%igdtmpl(15))/1.e6 + glatmax = float(gfld%igdtmpl(12))/1.e6 + need_to_flip_lats = .false. + endif + + glonmin = float(gfld%igdtmpl(13))/1.e6 + glonmax = float(gfld%igdtmpl(16))/1.e6 + + if (verb .ge. 3) then + print *,'In getgridinfo: glatmin= ',glatmin + print *,' glatmax= ',glatmax + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + + else + + !------------------------------------------ + ! Search for a record from a GRIB1 file + !------------------------------------------ + + jpds = -1 + jgds = -1 + + jgds(1) = 0 ! Request a record that's on a lat/lon grid + + if ( verb .ge. 3 ) then + print *,'before getgb in getgridinfo, ifh= ',ifh + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* Forecast hour: ',i4,':',i2.2) + print *,' ifhours(ifh)= ',ifhours(ifh) + print *,' iftotalmins(ifh)= ',iftotalmins(ifh) + endif + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + j=0 + +c jpds(14) = 0 ! test +c + write(*,980) jpds(1),jpds(2) + write(*,981) jpds(3),jpds(4) + write(*,982) jpds(5),jpds(6) + write(*,983) jpds(7),jpds(8) + write(*,984) jpds(9),jpds(10) + write(*,985) jpds(11),jpds(12) + write(*,986) jpds(13),jpds(14) + write(*,987) jpds(15),jpds(16) + write(*,988) jpds(17),jpds(18) + write(*,989) jpds(19),jpds(20) + write(*,990) jpds(21),jpds(22) + write(*,991) jpds(23),jpds(24) + write(*,992) jpds(25) + write(*,880) jgds(1),jgds(2) + write(*,881) jgds(3),jgds(4) + write(*,882) jgds(5),jgds(6) + write(*,883) jgds(7),jgds(8) + write(*,884) jgds(9),jgds(10) + write(*,885) jgds(11),jgds(12) + write(*,886) jgds(13),jgds(14) + write(*,887) jgds(15),jgds(16) + write(*,888) jgds(17),jgds(18) + write(*,889) jgds(19),jgds(20) + write(*,890) jgds(21),jgds(22) + + 980 format(' jpds(1) = ',i7,' jpds(2) = ',i7) + 981 format(' jpds(3) = ',i7,' jpds(4) = ',i7) + 982 format(' jpds(5) = ',i7,' jpds(6) = ',i7) + 983 format(' jpds(7) = ',i7,' jpds(8) = ',i7) + 984 format(' jpds(9) = ',i7,' jpds(10) = ',i7) + 985 format(' jpds(11) = ',i7,' jpds(12) = ',i7) + 986 format(' jpds(13) = ',i7,' jpds(14) = ',i7) + 987 format(' jpds(15) = ',i7,' jpds(16) = ',i7) + 988 format(' jpds(17) = ',i7,' jpds(18) = ',i7) + 989 format(' jpds(19) = ',i7,' jpds(20) = ',i7) + 990 format(' jpds(21) = ',i7,' jpds(22) = ',i7) + 991 format(' jpds(23) = ',i7,' jpds(24) = ',i7) + 992 format(' jpds(25) = ',i7) + 880 format(' jgds(1) = ',i7,' jgds(2) = ',i7) + 881 format(' jgds(3) = ',i7,' jgds(4) = ',i7) + 882 format(' jgds(5) = ',i7,' jgds(6) = ',i7) + 883 format(' jgds(7) = ',i7,' jgds(8) = ',i7) + 884 format(' jgds(9) = ',i7,' jgds(10) = ',i7) + 885 format(' jgds(11) = ',i7,' jgds(12) = ',i7) + 886 format(' jgds(13) = ',i7,' jgds(14) = ',i7) + 887 format(' jgds(15) = ',i7,' jgds(16) = ',i7) + 888 format(' jgds(17) = ',i7,' jgds(18) = ',i7) + 889 format(' jgds(19) = ',i7,' jgds(20) = ',i7) + 890 format(' jgds(20) = ',i7,' jgds(22) = ',i7) + + print *,'lugb= ',lugb,' lugi= ',lugi + print *,'before ggi getgb jpds(14) = ',jpds(14) + print *,'before ggi getgb jgds(1) = ',jgds(1) + + call getgb(lugb,lugi,jf,j,jpds,jgds, + & kf,k,igetpds,igetgds,lb,f,iret) + + if (iret.ne.0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo calling getgb' + print *,'!!! Return code from getgb = iret = ',iret + endif + + iggret = iret + else + iggret=0 + imax = igetgds(2) + jmax = igetgds(3) + dx = float(igetgds(9))/1000. + dy = float(igetgds(10))/1000. + endif + +c write(*,780) igetpds(1),igetpds(2) +c write(*,781) igetpds(3),igetpds(4) +c write(*,782) igetpds(5),igetpds(6) +c write(*,783) igetpds(7),igetpds(8) +c write(*,784) igetpds(9),igetpds(10) +c write(*,785) igetpds(11),igetpds(12) +c write(*,786) igetpds(13),igetpds(14) +c write(*,787) igetpds(15),igetpds(16) +c write(*,788) igetpds(17),igetpds(18) +c write(*,789) igetpds(19),igetpds(20) +c write(*,790) igetpds(21),igetpds(22) +c write(*,791) igetpds(23),igetpds(24) +c write(*,792) igetpds(25) +c write(*,680) igetgds(1),igetgds(2) +c write(*,681) igetgds(3),igetgds(4) +c write(*,682) igetgds(5),igetgds(6) +c write(*,683) igetgds(7),igetgds(8) +c write(*,684) igetgds(9),igetgds(10) +c write(*,685) igetgds(11),igetgds(12) +c write(*,686) igetgds(13),igetgds(14) +c write(*,687) igetgds(15),igetgds(16) +c write(*,688) igetgds(17),igetgds(18) +c write(*,689) igetgds(19),igetgds(20) +c write(*,690) igetgds(21),igetgds(22) +c +c 780 format(' kpds(1) = ',i7,' kpds(2) = ',i7) +c 781 format(' kpds(3) = ',i7,' kpds(4) = ',i7) +c 782 format(' kpds(5) = ',i7,' kpds(6) = ',i7) +c 783 format(' kpds(7) = ',i7,' kpds(8) = ',i7) +c 784 format(' kpds(9) = ',i7,' kpds(10) = ',i7) +c 785 format(' kpds(11) = ',i7,' kpds(12) = ',i7) +c 786 format(' kpds(13) = ',i7,' kpds(14) = ',i7) +c 787 format(' kpds(15) = ',i7,' kpds(16) = ',i7) +c 788 format(' kpds(17) = ',i7,' kpds(18) = ',i7) +c 789 format(' kpds(19) = ',i7,' kpds(20) = ',i7) +c 790 format(' kpds(21) = ',i7,' kpds(22) = ',i7) +c 791 format(' kpds(23) = ',i7,' kpds(24) = ',i7) +c 792 format(' kpds(25) = ',i7) +c 680 format(' kgds(1) = ',i7,' kgds(2) = ',i7) +c 681 format(' kgds(3) = ',i7,' kgds(4) = ',i7) +c 682 format(' kgds(5) = ',i7,' kgds(6) = ',i7) +c 683 format(' kgds(7) = ',i7,' kgds(8) = ',i7) +c 684 format(' kgds(9) = ',i7,' kgds(10) = ',i7) +c 685 format(' kgds(11) = ',i7,' kgds(12) = ',i7) +c 686 format(' kgds(13) = ',i7,' kgds(14) = ',i7) +c 687 format(' kgds(15) = ',i7,' kgds(16) = ',i7) +c 688 format(' kgds(17) = ',i7,' kgds(18) = ',i7) +c 689 format(' kgds(19) = ',i7,' kgds(20) = ',i7) +c 690 format(' kgds(20) = ',i7,' kgds(22) = ',i7) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,' dx= ',dx,' dy= ',dy + endif + + +c ---------------------------------------------------------------- +c Get boundaries of the data grid. NOTE: gds(4) is referred to in +c GRIB documenatation as the "Latitude of origin", which might +c imply "minimum Latitude". However, for the grids that we'll be +c using in this program, the "Latitude of origin" will be listed +c under gds(4) as the northernmost point (eg., in MRF, +c gds(4) = 90), so for this program, use gds(4) as your max lat, +c and gds(7) as your min lat. However, in case NCEP, UKMET or +c ECMWF change their convention and begin flipping their grids, a +c check is made to make sure that the max lat is not less than the +c min lat. +c +c BUGFIX (August, 2001): It is possible to have an input grid +c which goes from south to north (such as NAVGEM). In this case, +c we flip the data in subroutine conv1d2d_real. However, the max +c and min latitudes listed in the GRIB GDS will be confused, so we +c need to check the value of the GRIB scanning mode flag here. + + need_to_flip_lons = .false. + + iscanflag = igetgds(11) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(igetgds(4))/1000. + glatmax = float(igetgds(7))/1000. + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(igetgds(7))/1000. + glatmax = float(igetgds(4))/1000. + need_to_flip_lats = .false. + endif + + glonmin = float(igetgds(5))/1000. + glonmax = float(igetgds(8))/1000. + + endif + +c After this point in this subroutine, nothing is GRIB1 / GRIB2 +c specific, so it does not need to be within the if/then +c statement above that differentiated between GRIB / GRIB2. + +c17Jul2014 if (glonmin < 0.0) glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax < 0.0) glonmax = 360. - abs(glonmax) + + if (glonmin >= 0.0 .and. glonmax >= 0.0) then + if (glonmin > glonmax) then + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Badly notated longitude boundaries in ' + print *,' GRIB PDS, because the min longitude ' + print *,' (glonmin) is greater than the max ' + print *,' longitude (glonmax) where both longitudes' + print *,' are greater than 0.' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + print *,' !!! STOPPING....' + stop 98 + endif + endif + elseif (glonmin < 0.0 .and. glonmax >= 0.0) then + ! An example of this is the MPAS data, which starts and ends + ! at the dateline and is specified as glonmin=-179.875, + ! glonmax=179.875. Convert to be positive and go from + ! 180.125 to 539.875. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0, glonmax > 0, so glonmin' + print *,' will be converted to be > 0 and 360 will' + print *,' be added to glonmax.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. + abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin < 0.0 .and. glonmax < 0.0) then + ! Examples of this are GFDL and HWRF. In this case, make + ! both glonmin and glonmax positive. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0 and glonmax < 0, so both' + print *,' will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin >= 0.0 .and. glonmax < 0.0) then + ! An example of this is the GFS data, which goes from + ! glonmin=0.0 to glonmax=-0.5. Convert it here to go + ! from glonmin=0.0 to glonmax=359.5 + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is >= 0 and glonmax < 0, so' + print *,' glonmax will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + endif + +c17Jul2014 if (glonmin < 0.0) then +c17Jul2014 glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax <= 0.0) then +c17Jul2014 glonmax = 360. - abs(glonmax) +c17Jul2014 else +c17Jul2014 glonmax = 360 + abs(glonmax) +c17Jul2014 endif +c17Jul2014 endif + + if (glatmax < glatmin) then + temp = glatmax + glatmax = glatmin + glatmin = temp + endif + + if (glonmin > 200.0 .and. glonmin <= 360.) then + if (glonmax < 50.) then + ! Likely GM-wrapping in current record + glonmax = glonmax + 360. + endif + endif +c + if ( verb .ge. 3 ) then + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + print *,' ' + print *,'NOTE: For regional grids, valid data points might' + print *,'NOT extend all the way to the gds-defined grid ' + print *,'boundary, due to the fact that data have been ' + print *,'interpolated from a NPS or Lamb-Conf grid onto a ' + print *,'lat/lon grid. This program checks the logical ' + print *,'bitmap for valid data points, but just keep this in' + print *,'mind if trying to debug errors that occur near the' + print *,'grid boundaries for regional models.' + endif + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glat)) deallocate(glat) + if (allocated(glon)) deallocate(glon) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + endif + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + + iggret = 96 + return + endif + + do j=1,jmax + glat(j) = glatmax - (j-1)*dy + enddo + do i=1,imax + glon(i) = glonmin + (i-1)*dx + enddo + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + +c -------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have +c forgotten to change the input grid bounds from a global grid +c run). Modify the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just query the +c netcdf file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE inparms + USE verbose_output; USE netcdf_parms + + implicit none +c + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (datecard) inp + + logical(1) :: need_to_flip_lats,need_to_flip_lons + real xhold,xlondiff,xlatdiff + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer iscanflag,iggret + integer, intent(in) :: ncfile_id + integer, intent(out) :: imax,jmax + integer :: iia,ija,midi,midj,i,j,iix,jix +c + + iggret = 0 + + call get_ncdim1(ncfile_id,netcdfinfo%lon_name,imax) + call get_ncdim1(ncfile_id,netcdfinfo%lat_name,jmax) + + if (allocated(tmplon)) deallocate (tmplon) + if (allocated(tmplat)) deallocate (tmplat) + allocate (tmplon(imax),stat=iia) + allocate (tmplat(jmax),stat=ija) + if (iia /= 0 .or. ija /= 0) then + print *,' ' + print *,'!!! ERROR in sub getgridinfo_netcdf allocating arrays.' + print *,'!!! iia = ',iia,' ija= ',ija + iggret = 94 + return + endif + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + endif + + call get_var1_double (ncfile_id,netcdfinfo%lon_name,imax,tmplon) + call get_var1_double (ncfile_id,netcdfinfo%lat_name,jmax,tmplat) + +c Compute the dx and dy by picking values out of the middle of +c the lat and lon arrays.... + + midi = imax/2 + midj = jmax/2 + + dx = abs(tmplon(midi) - tmplon(midi-1)) + dy = abs(tmplat(midj) - tmplat(midj-1)) + + if (verb .ge. 1) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,'dx= ',dx,' dy= ',dy + print *,' ' + write (6,112) midi,dx + write (6,113) midj,dy + + 112 format(1x,' DX: midi= ',i4,' dx= ',f8.4) + 113 format(1x,' DY: midj= ',i4,' dy= ',f8.4) + endif + + +c ------------------------------------------------------------------ +c Get boundaries of the data grid. Note that it is possible to have +c an input grid which goes from south to north (in fact, it appears +c that many NetCDF files are constructed this way). Keep in mind, +c however, that the tracker has been written such that point (1,1) +c should be the upper-leftmost point on the grid, while point +c (imax,jmax) should be the lower-rightmost point. If we check and +c find that we're dealing with data that instead starts from the +c south and increases northward, we flip the data in subroutine +c conv1d2d_real. Similarly here, we make sure to test so that when +c we are done in this routine, glatmax refers to the northernmost +c latitude and glatmin the southernmost latitude. + + if (tmplon(imax) > tmplon(1)) then + glonmin = tmplon(1) + glonmax = tmplon(imax) + else + glonmin = tmplon(imax) + glonmax = tmplon(1) + endif + + if (tmplat(1) > tmplon(jmax)) then + glatmax = tmplat(1) + glatmin = tmplat(jmax) + else + glatmax = tmplat(jmax) + glatmin = tmplat(1) + endif + + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glon)) deallocate (glon) + if (allocated(glat)) deallocate (glat) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + iggret = 96 + return + endif + + ! If the lat or lon grids are flipped (i.e., the lats increase + ! from south to north, or the lons increase westward), then we + ! will need to flip both the data arrays as well as the arrays + ! that are holding the values of the lats and lons.... + + need_to_flip_lats = .false. + need_to_flip_lons = .false. + + if (tmplat(1) > tmplon(jmax)) then + do j=1,jmax + glat(j) = tmplat(j) + enddo + else + do j=1,jmax + jix = jmax - j + 1 + glat(jix) = tmplat(j) + enddo + need_to_flip_lats = .true. + endif + + if (tmplon(imax) > tmplon(1)) then + do i=1,imax + glon(i) = tmplon(i) + enddo + else + do i=1,imax + iix = imax - i + 1 + glon(iix) = tmplon(i) + enddo + need_to_flip_lons = .true. + endif + +c do i = 1,imax +c print *,'i= ',i,' glon(i)= ',glon(i) +c enddo +c do j = 1,jmax +c print *,'j= ',j,' glat(j)= ',glat(j) +c enddo + +c --------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have forgotten +c to change the input grid bounds from a global grid run). Modify +c the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) +c +c ABSTRACT: The purpose of this subroutine is to read the "time" +c dimension and "time data" from the NetCDF file so that we know +c how many time levels there are and what those time levels are. +c One reason for doing this is that some models, like the GFDL +c FV3, do not output hour 0 data, so we need to check this first +c before running through the tracking processing for the various +c hours. We will take the list of hours read in here directly from +c the NetCDF file and compare that against the *requested* list of +c forecast hours that the user has entered. The user might not be +c aware that there is no hour 0 data for a given model. We compare +c these two lists of forecast hours and then write a message if +c there is a lead time that is not in the NetCDF file. +c +c INPUT: +c ncfile character name of NetCDF file +c ncfile_id integer id associated with NetCDF file after open +c ifhmax integer max number of lead times that the user has +c requested on the input lead times data file. This +c value was set in subroutine read_fhours. +c netcdfinfo variable of user-defined type netcdfstuff (from +c module netcdf_parms). +c +c OUTPUT: +c ncfile_tmax integer max number of lead times that are in the +c NetCDF file, as read in from this subroutine +c ncfile_has_hour0 character flag (y|n) that tells whether or not +c the input NetCDF data file actually has an hour0 +c record in it or not. +c + USE netcdf_parms; USE tracked_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo + + character :: ncfile*180,ncfile_has_hour0*1,match_check*1 + integer, intent(in) :: ncfile_id + integer, intent(out) :: ncfile_tmax + integer :: infta,k,m,n,ifhmax,irnhret,usertime +c + + irnhret = 0 + ncfile_has_hour0 = 'n' + + !----------------------------------------------------------- + ! First read the NetCDF file to get the number of time levels, + ! which will be returned in "ncfile_tmax".... + !----------------------------------------------------------- + + print *,' ' + print *,'in read_netcdf_hours...' + print *,'ncfile_id= ',ncfile_id + print *,'netcdfinfo%time_name= ',netcdfinfo%time_name + print *,'ncfile_tmax= ',ncfile_tmax + + call get_ncdim1(ncfile_id,netcdfinfo%time_name,ncfile_tmax) + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + print *,'Num netcdf time levs= ncfile_tmax= ',ncfile_tmax + endif + + if (allocated(netcdf_file_time_values)) then + deallocate (netcdf_file_time_values) + endif + + allocate (netcdf_file_time_values(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating' + print *,'!!! netcdf_file_time_values array. infta = ',infta + irnhret = 94 + return + endif + + + !----------------------------------------------------------- + ! Now read in the actual time values that are stored in the + ! NetCDF file.... + !----------------------------------------------------------- + + call get_var1_double (ncfile_id,netcdfinfo%time_name,ncfile_tmax + & ,netcdf_file_time_values) + + if (verb .ge. 1) then + do k = 1,ncfile_tmax + print *,'k= ',k,' netcdf_file_time_values(k)= ' + & ,netcdf_file_time_values(k) + enddo + endif + + !------------------------------------------------------------ + ! Now convert the NetCDF time values into minutes in order to + ! be able to compare with the user-requested list of lead + ! times. Remember that the NetCDF lead times will be listed + ! either as hours or as fractions of days. + !------------------------------------------------------------ + + if (allocated(nctotalmins)) then + deallocate (nctotalmins) + endif + + allocate (nctotalmins(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating ' + print *,'!!! nctotalmins array. infta = ',infta + irnhret = 94 + return + endif + + do k = 1,ncfile_tmax + + if (netcdfinfo%time_units == 'hours') then + nctotalmins(k) = int(netcdf_file_time_values(k)) * 60 + elseif (netcdfinfo%time_units == 'days') then + nctotalmins(k) = int(netcdf_file_time_values(k) * 60. * 24.) + else + print *,' ' + print *,'!!! ERROR: In read_netcdf_hours, the value of' + print *,' netcdfinfo%time_units is neither hours nor days.' + print *,' netcdfinfo%time_units= ',netcdfinfo%time_units + print *,' STOPPING....' + print *,' ' + stop 99 + endif + + if (verb .ge. 1) then + write (6,71) k,netcdf_file_time_values(k),nctotalmins(k) + endif + + enddo + + 71 format (1x,i5,' netcdf_file_time_values(k)= ',f8.4 + & ,' nctotalmins(k)= ',i10) + + !------------------------------------------------------------ + ! Now go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match + ! the two lists up. The big one to watch out for is whether + ! or not the NetCDF file actually has an hour 0 lead time. + !------------------------------------------------------------ + + userloop: do n = 1,ifhmax + + usertime = iftotalmins(n) + + match_check = 'n' + + netcdfloop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + if (verb .ge. 1) then + print *,'+++ Time match for usertime= ',usertime + endif + match_check = 'y' + endif + + enddo netcdfloop + + if (match_check == 'n') then + + if (usertime == 0) then + print *,' ' + print *,'Warning: For a NetCDF file, the user has requested' + print *,'to read in an hour 0 file, however a scan of the' + print *,'time data values in the NetCDF file indicates' + print *,'that there is no hour 0 data in this file. ' + print *,'We will substitute either missing values or ' + print *,'the values from the TC Vitals data in the ' + print *,'hour 0 record and then start searching at the ' + print *,'next lead time.' + ncfile_has_hour0 = 'n' + else + print *,' ' + print *,'!!! ERROR: For a NetCDF file, the user has' + print *,' requested to process a particular lead time that' + print *,' does not exist in the NetCDF list of time ' + print *,' values.' + print *,' n= ',n + print *,' usertime= iftotalmins(n)= ',iftotalmins(n) + print *,' STOPPING....' + stop 99 + endif + + elseif (match_check == 'y') then + + if (usertime == 0) then + if (verb .ge. 1) then + print *,' ' + print *,'+++ For the input NetCDF file, an hour0 data ' + print *,' record exists in the data file.' + endif + ncfile_has_hour0 = 'y' + endif + + endif + + enddo userloop +c + return + end +c +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_valid_point (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) +c +c ABSTRACT: This subroutine checks to see if the input lat/lon +c point is associated with four surrounding (i,j) locations that +c have valid data. The writing of this routine was prompted by the +c HFIP project in February, 2009. Some of their high resolution +c data for their inner nests contained grids that had been rotated +c from native map projections to regular lat/lon grids, but that +c rotation left "empty" spots on the lat/lon grid where there is +c no data. Then when searching in find_maxmin, we were running +c barnes iterations from these lat/lon locations where there was +c no data, which would give artificially low values at those +c lat/lon locations (because the barnes scheme would only include +c points that were relatively far away where there was valid data). +c So in this routine, we call subroutine fix_latlon_to_ij in order +c to get the nearest (i,j) coordinates, and then we check all of +c these points to make sure that valid data exist. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing in i-direction +c dy grid spacing in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c rlatt,rlont input lat/lon about which we will check the +c surrounding (i,j) locations for valid data. +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c icvpret return code from this routine. A value of 0 means that +c all is okay and the input point is surrounded by valid +c data. + + USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,ifix,jfix + integer ifilret,icvpret + character(*) cmaxmin + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real rlont,rlatt,xdum,gridpoint_maxmin + real dx,dy,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +c + call fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt + & ,xdum,ifix,jfix,gridpoint_maxmin,'checker' + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) + + if (ifilret /= 0) then + icvpret = 99 + return + endif + + if (valid_pt(ifix,jfix)) then + icvpret = 0 + else + icvpret = 99 + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.025) then + grfact = 20 + else if (grdspc > 0.025 .and. grdspc <= 0.05) then + grfact = 12 + else if (grdspc > 0.05 .and. grdspc <= 0.10) then + grfact = 6 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif + + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (1*grfact) + iend = ipfix + (2*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (2*grfact) + iend = ipfix + (1*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (1*grfact) + iend = ipfix + (1*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (2*grfact) + jend = jpfix + (1*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (1*grfact) + jend = jpfix + (2*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (1*grfact) + jend = jpfix + (1*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +c print *,' End of fix_latlon_to_ij, gridpoint_maxmin = ' +c & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine rvcal (imax,jmax,dlon,dlat,z,vp) +c +c ABSTRACT: This routine calculates the relative vorticity (zeta) +c from u,v on an evenly-spaced lat/lon grid. Centered finite +c differences are used on the interior points and one-sided +c differences are used on the boundaries. +c +c NOTE: There are 3 critical arrays in this subroutine, the first +c being zeta and the 2nd and 3rd being u and v. There is a +c critical difference in the array indexing for the levels. For +c zeta, the array is dimensioned with levels from 1 to 3, with +c 1 = 850, 2 = 700, 3 = sfc. However, there is an extra level +c for the winds, such that the level dimension goes 1 = 850, +c 2 = 700, 3 = 500, 4 = sfc. So we need to adjust for that in +c this routine. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE trig_vals; USE grid_bounds + USE verbose_output + + implicit none + + dimension cosfac(jmax),tanfac(jmax) + real tmpzeta(imax,jmax) + real xlondiff,xlatdiff,dlon,dlat,dfix + real dlat_edge,dlat_inter,dlon_edge,dlon_inter + real rlat(jmax),cosfac,tanfac + integer z,iscanflag,nlat,nlon,i,j,imax,jmax,w + integer ii,jj + logical(1) vp(imax,jmax) + +c -------------------------- + +c Figure out what level of data we have and what the array +c indices should be. + + if (z == 1) then + ! z = 1 for 850 mb zeta, w = 1 for 850 mb winds + w = 1 + else if (z == 2) then + ! z = 2 for 700 mb zeta, w = 2 for 700 mb winds + w = 2 + else if (z == 3) then + ! z = 3 for sfc zeta, w = 4 for sfc (10m) winds + w = 4 + endif + +c Calculate grid increments for interior and edge points. + +c IMPORTANT: If dtk is defined in module trig_vals in km, then +c we need to multiply by 1000 here to get meters. If it's defined +c as meters, just let it be. Since the wind values are given in +c meters, that's why we need the dlon values to be in meters. + + if (dtk < 750.) then ! chances are, dtk was defined as km + dfix = 1000.0 + else ! dtk was already defined as meters + dfix = 1.0 + endif + + dlon_edge = dtk * dfix * dlon ! Di dist over 1 grid pt + dlat_edge = dtk * dfix * dlat ! Dj dist over 1 grid pt + dlon_inter = dtk * dfix * 2.0 * dlon ! Di dist over 2 grid pts + dlat_inter = dtk * dfix * 2.0 * dlat ! Dj dist over 2 grid pts + + +c Calculate required trig functions. These are functions of +c latitude. Remember that the grid must go from north to south. +c This north-to-south requirement has +c already been checked in subroutine getgridinfo. If necessary, +c any flipping of the latitudes was done there, and flipping of +c the data, again if necessary, was done in subroutine getdata. + + do j=2,jmax-1 + rlat(j) = glatmax - ((j-1) * dlat) + cosfac(j) = cos(dtr*rlat(j)) + tanfac(j) = (tan(dtr*rlat(j)))/erad + enddo + +c Set trig factors at end points to closest interior point +c to avoid a singularity if the domain includes the poles, +c which it will for the global grids (MRF, GDAS, GFS, UKMET,NCE) + + cosfac(1) = cosfac(2) + tanfac(1) = tanfac(2) + cosfac(jmax) = cosfac(jmax-1) + tanfac(jmax) = tanfac(jmax-1) + +c NOTE: These next bits of vorticity calculation code assume that +c the input grid is oriented so that point (1,1) is the upper +c left-most (NW) and point (imax,jmax) is the lower right- +c most point. Any other grids will probably crash the +c program due to array out of bounds errors. +c NOTE: Before each calculation is done, the logical array is +c checked to make sure that all the data points in this +c calculation have valid data (ie., that the points are not +c outside a regional model's boundaries). +c +c !!! IMPORTANT NOTE: While testing this, I uncovered a bug, which was +c that I had the "j+1" and "j-1" reversed. Just from a physical +c understanding, the du/dy term at a point is calculated by taking +c the u value north of the point minus the u value south of the +c point. Intuitively, this is u(j+1) - u(j-1). However, we have +c designed this program to have the northernmost point as +c the beginning of the grid (i.e., for the global grids, j=1 at 90N, +c and j increases southward). Thus, if you would do u(j+1) - +c u(j-1), you would actually be taking the u value south of the +c point minus the u value north of the point, EXACTLY THE OPPOSITE +c OF WHAT YOU WANT. Therefore, the vorticity calculations have +c been changed so that we now have u(j-1) - u(j+1). +c +c UPDATE FEB 2009: With limited domain grids that have missing +c data on them (such as you would have for a grid that has been +c converted from a non-lat/lon grid to a lat/lon grid), we were +c running into problems below with the setting of zeta values to +c a missing value of -999. In place of this, the easiest thing to +c do is to simply assign a value of the background coriolis value +c to that point. No, this is not correct, but it is the easiest +c workaround for this right now. Setting it to zero would be too +c far off. Setting it to the coriolis component has a net effect +c of not having much impact on the barnes scheme result. +c +c --------------- +c Interior points +c --------------- + + if ( verb .ge. 3 ) then + print *,'Just before inter rvcalc, dlon_inter = ',dlon_inter + & ,' dlat_inter = ',dlat_inter + endif + + do j=2,jmax-1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1) .and. vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo + enddo +c +c ----------------------------- +c Bottom (Southernmost) points +c ----------------------------- +c + j=jmax + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------- +c Top (Northernmost) points +c -------------------------- +c + j=1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c ------------------------------- +c Left edge (Westernmost) points +c ------------------------------- +c + i=1 + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------------- +c Right edge (Easternmost) points +c -------------------------------- +c + i=imax + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c --------- +c SW corner +c --------- + i=1 + j=jmax + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i+1,j,w)-v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NW corner +c --------- + i=1 + j=1 + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NE corner +c --------- + i=imax + j=1 + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c SE corner +c --------- + i=imax + j=jmax + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i,j,w)-v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + do ii=1,imax + do jj=1,jmax + tmpzeta(ii,jj) = zeta(ii,jj,z) * 1.e5 + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine thickness_calc (imax,jmax,vp) +c +c ABSTRACT: This routine calculates the thicknesses for three +c different layers: 200-500, 500-850 and 200-850 mb. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE verbose_output + + implicit none + + integer i,j,layer,upper,lower,imax,jmax + logical(1) vp(imax,jmax) + +c -------------------------- + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 +c +c The array indices for the levels for the 4 different GP height +c arrays (as assigned in subroutine getdata) are as follows: +c 1: 850 mb +c 2: 700 mb +c 3: 500 mb +c 4: 200 mb + + + do layer = 1,3 + + select case (layer) + case (1); upper=3; lower=1; + case (2); upper=4; lower=3; + case (3); upper=4; lower=1; + end select + + do j = 1,jmax + do i = 1,imax + + if (vp(i,j)) then + thick(i,j,layer) = hgt(i,j,upper) - hgt(i,j,lower) + else + thick(i,j,layer) = -999.0 + endif + + enddo + enddo + + enddo +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine first_ges_center (imax,jmax,dx,dy,cparm,fxy + & ,cmaxmin,trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) +c +c ABSTRACT: This subroutine scans an array and picks out areas of +c max or min, then loads those center positions into the first- +c guess lat & lon arrays to be used by subroutine tracker for +c locating the very specific low center positions. +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c dx Grid spacing in i-direction for the input grid +c dy Grid spacing in j-direction for the input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c finf Logical. Field of influence. Dimension same as fxy +c cmaxmin Char string to indicate if search is for a max or a min +c trkrinfo Derived type that holds/describes various tracker parms, +c including the contour interval to be used +c ifh Index for the forecast hour +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c contour_info Type cint_stuff from module contours. Contains +c contour information +c +c OUTPUT: +c maxmini Integer array containing i-indeces of max/min locations +c maxminj Integer array containing j-indeces of max/min locations +c ifgcret return code from this subroutine +c +c OTHER: +c storm Contains the tcvitals for the storms (module def_vitals) + + USE trkrparms; USE grid_bounds; USE set_max_parms; USE def_vitals + USE contours; USE tracked_parms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,n,isstart,ifamret,ibeg,jbeg,iend,jend + integer ifh,maxstorm,imax,jmax,itemp,ifgcret + integer stormct,oldstormct,mm + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + character(*) cparm,cmaxmin + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real dmax,dmin,dx,dy,dbuffer,tmp + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of first_ges_center *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for new lows at hour ',i4,':',i2.2) + print *,'*-------------------------------------------------*' + endif + + +c First check the user-supplied grid boundaries to see if we will +c scan the entire array or just a portion of it. + + if (trkrinfo%northbd < -998.0 .or. trkrinfo%southbd < -998.0 .or. + & trkrinfo%westbd < -998.0 .or. trkrinfo%eastbd < -998.0) then + ! User did not specify a subgrid, so scan the whole domain + ibeg = 1 + iend = imax + jbeg = 1 + jend = jmax + else + +c if (trkrinfo%westbd > 360.0 .or. trkrinfo%eastbd < 0.0 .or. +c & trkrinfo%westbd < 0.0 .or. + + if (trkrinfo%westbd > 360.0 .or. + & trkrinfo%northbd > 90.0 .or. trkrinfo%northbd <-90.0 .or. + & trkrinfo%southbd > 90.0 .or. trkrinfo%southbd <-90.0 .or. + & trkrinfo%westbd >= trkrinfo%eastbd .or. + & trkrinfo%southbd >= trkrinfo%northbd) then + + if (trkrinfo%westbd > trkrinfo%eastbd) then + + if (trkrinfo%westbd < 360.0 .and. + & trkrinfo%eastbd >= 0.0)then + + ! In this special case, the user has specified that the + ! western boundary be to the west of the Greenwich + ! meridian and the eastern boundary be to the east of it. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE: The user supplied grid lon boundaries' + print *,'++ span across the Greenwich meridian.' + print *,'++ ' + print *,'++ Western boundary: ',trkrinfo%westbd + print *,'++ Eastern boundary: ',trkrinfo%eastbd + print *,'++ Northern boundary: ',trkrinfo%northbd + print *,'++ Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ! Calculate the beginning and ending i and j points for + ! this case of spanning the Greenwich meridian. The + ! beginning and ending j points are, obviously, the same + ! as for the regular case below in the else. The + ! i-beginning point will also be the same as for the + ! regular case. However, the i-ending point will be + ! modified for the meridian wrap; it will be > imax. + + jbeg = int(((glatmax + dy - trkrinfo%northbd) + & / dy) + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) + & / dy) + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) + & / dx) + 0.5) +c iend = int(((trkrinfo%eastbd - glonmin + dx) +c & / dx) + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) + & / dx) + 0.5) + imax + + goto 377 + + endif + endif + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. There is a' + print *,'!!! problem with the user-supplied grid ' + print *,'!!! boundaries. Please check them and ' + print *,'!!! resubmit the program.' + print *,'!!!' + print *,'!!! Western boundary: ',trkrinfo%westbd + print *,'!!! Eastern boundary: ',trkrinfo%eastbd + print *,'!!! Northern boundary: ',trkrinfo%northbd + print *,'!!! Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ifgcret = 91 + return + + 377 continue + + else + ! Calculate the beginning and ending i and j points.... + jbeg = int(((glatmax + dy - trkrinfo%northbd) / dy) + & + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) / dy) + & + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) / dx) + & + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) / dx) + & + 0.5) + endif + endif + +c Scan the requested portion of the grid and pick out the max and +c min data values, figure out what the max and min contour levels +c will be, and fill an array with the values of the various +c intermediate, incremental contour levels. + + if (trkrinfo%contint <= 0) then + + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. For a midlat' + print *,'!!! or tcgen run of the tracker, the contour' + print *,'!!! interval supplied by the user is not ' + print *,'!!! greater than 0.' + print *,'!!! ' + print *,'!!! User-supplied contint = ',trkrinfo%contint + print *,' ' + endif + + ifgcret = 91 + return + endif + + dmin = 9.99e20 + dmax = -9.99e20 + + do j = jbeg,jend + do i = ibeg,iend + if (i > imax) then + itemp = i - imax ! If wrapping past GM + else + itemp = i + endif + if (valid_pt(itemp,j)) then + if (fxy(itemp,j) < dmin) dmin = fxy(itemp,j) + if (fxy(itemp,j) > dmax) dmax = fxy(itemp,j) + endif + enddo + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*--------------------------------------------*' + print *,'In first_ges_center, dmin= ',dmin,' dmax= ',dmax + endif + + +c We want to allow for storms moving out of the sub-region, +c in which case we might hit slightly lower or higher +c contours than were found in the sub-region, so allow for +c an extra buffer and modify dmin and dmax.... + + dbuffer = (dmax - dmin) / 2.0 + dmax = dmax + dbuffer + dmin = dmin - dbuffer + + if ( verb .ge. 3 ) then + print *,'after adjustment, dmin= ',dmin,' dmax= ',dmax + endif + +c Next 2 lines changed for compiler compatibility on +c other platforms.... +c contour_info%xmaxcont = dmax - amod(dmax,trkrinfo%contint) +c contour_info%xmincont = dmin - amod(dmin,trkrinfo%contint) + + tmp = trkrinfo%contint + contour_info%xmaxcont = dmax - mod(dmax,tmp) + contour_info%xmincont = dmin - mod(dmin,tmp) + + if ( verb .ge. 3 ) then + print *,'A1 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A1 contour_info%xmincont= ',contour_info%xmincont + endif + + if (contour_info%xmincont > contour_info%xmaxcont) then + contour_info%xmincont = contour_info%xmaxcont + endif + +c if (dmin > contour_info%xmincont) then +c contour_info%xmincont=contour_info%xmincont + trkrinfo%contint +c endif +c if (dmax < contour_info%xmaxcont) then +c contour_info%xmaxcont=contour_info%xmaxcont - trkrinfo%contint +c endif + + if ( verb .ge. 3 ) then + print *,'A2 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A2 contour_info%xmincont= ',contour_info%xmincont + print *,'maxconts= ',maxconts + endif + +c NOTE: In the loop below, the contour_info%contvals array is now +c (5/2003) no longer used in subsequent subroutines. But we still +c need to figure out the value of the contvals as we iterate the +c loop so we can know when we've surpassed dmax and can stop +c incrementing contour_info%numcont, which we do need in subsequent +c subroutines. + + contour_info%numcont = 0 + do n = 1,maxconts + contour_info%numcont = contour_info%numcont + 1 + contour_info%contvals(n) = contour_info%xmincont + + & float(n-1)*trkrinfo%contint +c print *,'n= ',n,' contour_info%contvals(n)= ' +c & ,contour_info%contvals(n) + if (contour_info%contvals(n) >= dmax) exit + enddo + + oldstormct = stormct + call find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) + + if (stormct > 0) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' ' + print *,'!!! ************************************************' + print *,'!!! ' + print *,'!!! NOTE: In first_ges_center, the value of stormct' + print *,'!!! returned from find_all_maxmins is not greater' + print *,'!!! than 0. This means there are no new centers' + print *,'!!! to track, which is not likely. Perhaps you are' + print *,'!!! searching over too small of an area??' + print *,'!!! ' + print *,'!!! ************************************************' + print *,' ' + endif + + endif + + print *,'ifh= ',ifh,' oldstormct= ',oldstormct + print *, ' stormct= ',stormct + + do mm = 1,300 + print *,'mm= ',mm,' maxmini(mm)= ',maxmini(mm) + & ,' maxminj(mm)= ',maxminj(mm) + enddo + + if (stormct > oldstormct .and. stormct > 0) then + isstart = oldstormct + 1 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,*) 'New search: ' + write (6,*) 'Possible new max/min locations at ifh= ',ifh + write (6,*) '--------------------------------------------' + endif + + do n = isstart,stormct + if (trkrinfo%type == 'midlat') then + storm(n)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(n)%tcv_center = 'TCG ' + endif + slonfg(n,ifh) = glonmin + (maxmini(n)-1)*dx + slatfg(n,ifh) = glatmax - (maxminj(n)-1)*dy + storm(n)%tcv_stspd = -99 + storm(n)%tcv_stdir = -99 + write (storm(n)%tcv_storm_id,'(i4.4)') n + write (storm(n)%tcv_storm_name,'(i4.4)') n + stormswitch(n) = 1 + if (cparm == 'mslp') then + + if ( verb .ge. 3 ) then + write (6,71) maxmini(n),maxminj(n),slonfg(n,ifh) + & ,360.-slonfg(n,ifh),slatfg(n,ifh) + & ,slp(maxmini(n),maxminj(n))/100.0 + endif + + endif + enddo + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' New search: ' + print *,'!!! NOTE: No new storms found in find_all_maxmins' + print *,'!!! at ifh = ',ifh,' stormct= ',stormct + print *,'!!! oldstormct= ',oldstormct + print *,' ' + endif + + endif + + 71 format (1x,'i= ',i4,' j= ',i4,' lon: ',f7.2,'E (',f6.2,'W)' + & ,2x,' lat: ',f6.2,' mslp: ',f6.1,' mb') +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) +c +c ABSTRACT: This subroutine will search an area delineated by +c input i and j indeces in order to find all local maxes or mins +c in that area. The (i,j) locations of the maxes/mins are returned +c in the maxmini and maxminj arrays. The input 3-character string +c cmaxmin will tell the subroutine to look for a "max" or a "min". +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c ibeg i-index for upper left location of grid to search +c iend i-index for lower right location of grid to search +c jbeg j-index for upper left location of grid to search +c jend j-index for lower right location of grid to search +c fxy Real array of data values +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c contour_info Type cint_stuff from module contours containing the +c the following 4 variables: +c 1. xmincont Real value for min contour level in the fxy data array +c 2. xmaxcont Real value for max contour level in the fxy data array +c 3. contvals Real array holding values of cont levels at this time +c 4. numcont Number of contour intervals found at this time +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c trkrinfo derived type containing various user-input tracker parms +c cmaxmin String that declares if "min" or "max" is being searched +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c +c OUTPUT: +c maxmini integer array containing i-indeces of the max/min points +c maxminj integer array containing j-indeces of the max/min points +c ifamret return code from this subroutine + + USE trkrparms; USE set_max_parms; USE contours + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + integer stormct,i,j,ibeg,iend,jbeg,jend,ix,jx,ixp1,ixm1 + integer ip,jp,maxstorm,jxp1,jxm1,ifamret,isret,iaret,iclmret + integer isoiret,icccret,igicwret,imax,jmax + character ccflag*1,get_last_isobar_flag*1,point_is_over_water*1 + character(*) cmaxmin + logical(1) still_finding_valid_maxmins,rough_gradient_check_okay + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real xavg,stdv,search_cutoff,dmin,dmax,sphere_cutoff + real plastbar,rlastbar,fract_land,dx,dy + +c----- + still_finding_valid_maxmins = .true. + + +c print *,'ctm beg of find_all_maxmins, maxstorm= ',maxstorm + + +c First, we want to get the mean and standard deviation of the input +c field to be searched. We can use the standard deviation info as +c part of our guideline for when to stop searching for maxes & mins. +c We will set the search cut-off threshold at 1/2 standard deviation +c above the mean for min searches. So, for the example of mslp, if +c the mean pressure over the whole domain is 1010 mb and the +c standard deviation is 12 mb, then when we are searching, if the +c lowest available (i.e., hasn't been found in a previous iteration +c of this loop) pressure is 1016, then it's time to stop searching. + + call avgcalc (fxy,imax*jmax,valid_pt,xavg,iaret) + call stdevcalc (fxy,imax*jmax,valid_pt,xavg,stdv,isret) + if (iaret /= 0 .or. isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_all_maxmins, the calls to avgcalc' + print *,'!!! and/or stdevcalc returned an error.' + print *,'!!! iaret= ',iaret,' isret= ',iaret + print *,' ' + endif + + ifamret = 98 + return + endif + + if (cmaxmin == 'min') then + search_cutoff = xavg + stdv*0.5 + else + search_cutoff = xavg - stdv*0.5 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In find_all_maxmins, search_cutoff= ',search_cutoff + print *,' ' + endif + +c Now begin to search the domain. We do a simple gridpoint scan, +c and once we find the max/min value, we pass the (i,j) coordinates +c at that point to a routine to check for a closed contour. Then +c we mask out those points in the contour (or, if there is not a +c closed contour, just the 8 points immediately surrounding the low +c center) and we do another iteration of search_loop to look for +c more lows. We mask out points we've found so that on subsequent +c iterations of search_loop, we don't find the same old center +c again and again and again..... + + search_loop: do while (still_finding_valid_maxmins) + + dmin = 9.99e20 + dmax = -9.99e20 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + ip = i + jp = j + + if (ip > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: In find_all_maxmins, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. The search' + print *,'!!! will not extend to the user-requested' + print *,'!!! grid boundary.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',ip + print *,' ' + endif + + exit iloop + + endif + endif + + if (valid_pt(ip,jp) .and..not. masked_out(ip,jp)) then + if (cmaxmin == 'min') then + if (fxy(ip,jp) < dmin) then + dmin = fxy(ip,jp) + ix = ip + jx = jp + endif + else + if (fxy(ip,jp) > dmax) then + dmax = fxy(ip,jp) + ix = ip + jx = jp + endif + endif + endif + + enddo iloop + enddo jloop + + if (cmaxmin == 'min') then + if (dmin < search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + else + if (dmax > search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + endif + +c As a rough first check, see if the neighboring points on all +c 4 sides have a gradient sloping down into the found min point, +c or at least that there is a flat field not having a gradient +c sloping away from the center point. + + call get_ijplus1_check_wrap (imax,jmax,ix,jx,ixp1,jxp1,ixm1 + & ,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In find_all_maxmins, the center we found' + print *,'!!! is too close to the grid boundary and will' + print *,'!!! NOT be checked for a closed contour.' + print *,'!!! ix= ',ix,' jx= ',jx,' fxy= ',fxy(ix,jx) + print *,'!!! ' + print *,' ' + endif + + masked_out(ix,jx) = .true. + cycle search_loop + endif + + if (cmaxmin == 'min') then + if (fxy(ix,jx) <= fxy(ixp1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxm1) .and. + & fxy(ix,jx) <= fxy(ixm1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + else + if (fxy(ix,jx) >= fxy(ixp1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxm1) .and. + & fxy(ix,jx) >= fxy(ixm1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + endif + + if (rough_gradient_check_okay) then + + if ( verb .ge. 3 ) then + print *,'Found a possible max/min at ix= ',ix,' jx= ',jx + endif + + +c From this rough check, we appear to have a gradient sloping +c in towards the center point. Now call the subroutine to +c check whether or not there is in fact a closed contour +c surrounding this local maximum or minimum. + + get_last_isobar_flag = 'n' + ccflag = 'n' + call check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,ccflag,cmaxmin,trkrinfo + & ,1,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if (ccflag == 'y') then + if (stormct < maxstorm) then + stormct = stormct + 1 + + if ( verb .ge. 3 ) then + print *,'AAA stormct= ',stormct,' ix= ',ix,' jx= ',jx + endif + + ! For a tcgen case, we will add in one additional check, + ! and that is to ensure the point is (mostly) over water. + ! Only do this check if the user has requested it (some + ! of the global models do not have a land-sea mask + ! included in the grib data files). + + point_is_over_water = 'u' + + if (trkrinfo%use_land_mask == 'y') then + call check_land_mask (imax,jmax,ix,jx,fract_land + & ,valid_pt,dx,dy,point_is_over_water,iclmret) + if (iclmret /= 0) then + print *,' ' + print *,'!!! ERROR from check_land_mask for ix= ',ix + & ,' jx= ',jx + print *,'!!! STOPPING PROGRAM' + stop 95 + endif + endif + + if (point_is_over_water /= 'n') then + maxmini(stormct) = ix + maxminj(stormct) = jx + endif + + else + + if ( verb .ge. 3 ) then + print *,'---max stormct reached, stormct= ', stormct + endif + + endif + else + + if ( verb .ge. 3 ) then + print *,'!!! contour check negative, ccflag= ',ccflag + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*-----------------------------------------------*' + print *,' ' + endif + + endif + +c Regardless of whether or not the found point turns out to have +c a closed contour, we don't want to find this local minimum or +c its 8 surrounding points again in a search on a subsequent +c iteration of this loop. + + masked_out(ix,jx) = .true. + masked_out(ix,jxp1) = .true. + masked_out(ixp1,jxp1) = .true. + masked_out(ixp1,jx) = .true. + masked_out(ixp1,jxm1) = .true. + masked_out(ix,jxm1) = .true. + masked_out(ixm1,jxm1) = .true. + masked_out(ixm1,jx) = .true. + masked_out(ixm1,jxp1) = .true. + + enddo search_loop + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine mask_based_on_wind_circ (imax,jmax,dx,dy,level + & ,valid_pt,masked_outc,trkrinfo + & ,ctlon,ctlat,cmodel_type,imbowret) +c +c ABSTRACT: This subroutine masks out grid points for a storm that +c is currently being tracked. It is called after a fix has been +c made at the current forecast hour. It is only used as a backup, +c that is, if the mslp data were not there and/or a fix position +c for mslp could not be made, then that means that the mask would +c not be able to get updated using the routine in subroutine +c check_closed_contour. But we still do need to update that mask, +c so we will instead do it based on wind circulation. We will go +c out radially from the center, starting at 40 km, then every +c 40 km from there on out. When the mean cyclonic Vt drops below +c 3 m/s, stop searching, and then mask out all grid points within +c that last-searched radius. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_outc Logical. T = data point is already accounted for, +c under the influence of another nearby max or min +c center; F = data point is available to be scanned by +c this subroutine for max or min centers. +c ctlon Fix longitude for the input parameter to this routine +c ctlon Fix latitude for the input parameter to this routine +c cmodel_type character, 'global' or 'regional' + + USE set_max_parms; USE trkrparms; USE grid_bounds + USE verbose_output; USE level_parms + + implicit none + + type (trackstuff) trkrinfo + + character(*) cmodel_type + integer, parameter :: numazim=24 + integer imax,jmax,level,imbowret,nlev,iazim,i,j + integer ibiret1,ibiret2,azimuth_ct,igvtret + integer jnfix,jsfix,iefix,iwfix + real vr(numazim),vt(numazim) + real dx,dy,ctlon,ctlat,rdist,bear,targlat,targlon + real xintrp_u,xintrp_v,grid_buffer,xmax_rdist_reached + real vt_mean,vt_azim_sum,xbear,dist,degrees + logical(1) valid_pt(imax,jmax),masked_outc(imax,jmax) + logical(1) searching_valid_pts + + imbowret = 0 + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + searching_valid_pts = .true. + + rdist = 40.0 ! units in km + xmax_rdist_reached = rdist ! units in km + + radial_loop: do while (searching_valid_pts) + + azimuth_ct = 0 + vt_azim_sum = 0.0 + vt = -999.0 + vr = -999.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (ctlat,ctlon,rdist,bear,targlat,targlon) + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + xmax_rdist_reached = rdist + exit radial_loop + endif + + ! These calls to bilin_int_uneven pass a variable, level, + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (ctlon,ctlat,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim) + & ,vt(iazim),igvtret) + azimuth_ct = azimuth_ct + 1 + vt_azim_sum = vt_azim_sum + vt(iazim) + else + xmax_rdist_reached = rdist + exit radial_loop + endif + + enddo azimloop + + if (azimuth_ct > 0) then + ! Compute azimuthally-averaged Vt at this distance + vt_mean = vt_azim_sum / float(azimuth_ct) + else + vt_mean = -999.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: rdist= ',rdist,' azimuth_ct= ',azimuth_ct + & ,' vt_azim_sum= ',vt_azim_sum,' vt_mean= ',vt_mean + endif + + if (ctlat >= 0.0) then + if (vt_mean >= 3.0) then + ! For a NH storm, if the cyclonic mean Vt >= 3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + else + if (vt_mean <= -3.0 .and. vt_mean > -998.0) then + ! For a SH storm, if the cyclonic mean Vt <= -3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + enddo radial_loop + + if ( verb .ge. 3 ) then + print *,'mbow: After radial_loop, rdist= ',rdist + & ,' xmax_rdist_reached= ',xmax_rdist_reached + endif + +c ----------------------------------------------------------------- +c At this point, we are done searching radially outwards away from +c the storm center. The max radial distance we reached is called +c xmax_rdist_reached. By getting to this spot in the subroutine, +c that means that we bumped out of radial_loop above because the +c rdist being used in that loop got to a radius at which the mean +c cyclonic Vt no longer was strong enough to continue the search +c outward, so we need to reduce it by 40 km here (back to the value +c for the last successful search). At a minimum, we will mask to a +c radius of 80 km. + + if (xmax_rdist_reached > 80.0) then + xmax_rdist_reached = xmax_rdist_reached - 40.0 + else + xmax_rdist_reached = 80.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: After adjustment of xmax_rdist_reached, rdist= ' + & ,rdist,' xmax_rdist_reached= ',xmax_rdist_reached + endif + + bearloop: do i = 1,4 + + ! Now find the values of the longitude for the farthest west + ! and east points and find the values of the latitude for the + ! farthest north and south points. The i and j indices + ! associated with these lons and lats will be used to define + ! the bounds of the grid over which we scan to find points + ! that will update the mask. + + select case (i) + case (1); xbear = 0.0; + case (2); xbear = 90.0; + case (3); xbear = 180.0; + case (4); xbear = 270.0; + end select + + call distbear (ctlat,ctlon,xmax_rdist_reached,xbear + & ,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,'mbow: distbear for i= ',i,' targlon= ',targlon + & ,' targlat= ',targlat + endif + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon > glonmax for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmax= ',glonmax + imbowret = 95 + return + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon < glonmin for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmin= ',glonmin + imbowret = 95 + return + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'either targlat > glatmx or targlat < glatmin, so' + print *,'we cannot update the mask.' + print *,'targlat= ',targlat,' glatmin= ',glatmin + print *,' glatmin= ',glatmin + imbowret = 95 + return + cycle bearloop + endif + + ! Get the i & j starting and ending points for our loop where + ! we will update the mask.... + + if (i == 1) then + + ! Get j for northern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jnfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jnfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 2) then + + ! Get i for eastern longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iefix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + elseif (i == 3) then + + ! Get i for southern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jsfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jsfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 4) then + + ! Get i for western longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iwfix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + endif + + enddo bearloop + + if ( verb .ge. 3 ) then + print *,'mbow: iwfix= ',iwfix,' iefix= ',iefix + & ,' jnfix= ',jnfix,' jsfix= ',jsfix + endif + + do i = iwfix,iefix + do j = jnfix,jsfix + + call calcdist (glon(i),glat(j),ctlon,ctlat,dist,degrees) + + if (dist < xmax_rdist_reached) then + masked_outc(i,j) = .true. + endif + + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,closed_contour,cmaxmin,trkrinfo + & ,num_requested_contours,contour_info + & ,get_last_isobar_flag,plastbar,rlastbar,icccret) +c +c ABSTRACT: This subroutine checks a field of data around an input +c (ix,jx) data point to see if a closed contour exists around +c that data point. It can check for a closed contour on a max or a +c min field, depending on the value of the input variable 'cmaxmin'. +c The algorithm works by examining rings of the 8 data points +c surrounding a data point that is in the contour interval. For +c example, in the diagram below, the X represents the location of +c the local minimum value which was passed into this routine with +c the coordinates (ix,jx), let's say it's 985 mb. And let's assume +c that the data values at points A-I are all in the 4 mb contour +c interval of 985-989 mb, and that all the surrounding points have +c data values >= 989. To test for a closed contour, we first check +c the ring of 8 points immediately around point X to see what their +c data values are. If a data value is found that is below the +c lower limit of this contour interval (985 mb) or lower than the +c local minimum value at the X point that we initially targeted +c (985 mb), then we do NOT have a closed contour, and we exit this +c subroutine. But in our example, that's not the case, and we have +c 5 points (B,D,E,F,G) that are in the interval. So in our next +c iteration of the loop, we set up 5 rings, each one set up around +c the points found in the first iteration (B,D,E,F,G), and we check +c the 8 points around each of those points. A logical array is +c used so that as soon as a point is found, it is flagged as being +c found. In this way, when we look at the ring around point D, for +c example, we won't pick point X again and set up another ring +c around it in the next ring iteration and end up in an infinite +c loop, going back and forth between point X and point D. While +c checking the 8 points in a ring, if a found data value is above +c our contour interval (i.e., >= 989 mb), we just ignore the +c point; we only mark points that are in our contour interval, +c and again, if we find a point below our contour interval, we +c exit the subroutine with a flag indicating a closed contour was +c NOT found. So in this method, we keep spreading out from the +c initial local minimum and creating and checking new rings until +c we either: (a) Hit the edge of the regional grid, in which case +c we consider a closed contour NOT found, (b) Run into a data +c point that has been marked as being under the influence of +c another nearby low, in which case we consider a closed contour +c NOT found, (c) Run into a point which is below (above) our +c contour interval for a min (max) check, in which case we +c consider a closed contour NOT found, or (d) we run out of +c points to keep searching, we have no rings left to create and +c check because all of the surrounding points are above (below) +c our contour interval for a min (max) check, and by default we +c consider this a closed contour and return to the calling +c subroutine a flag indicating such. +c +c + + + + + + + + + + +c + + + + + + + + + + +c + + A B + + + + + + +c + + C D X E + + + + +c + + + + F G + + + + +c + + + + + H I + + + +c + + + + + + + + + + +c + + + + + + + + + + +c +c UPDATE: This subroutine was updated to keep searching for +c multiple closed contours until it can't find anymore. The +c input parameter num_requested_contours dictates how many +c contours to search for. In the case of just trying to roughly +c locate new centers and establish that there is a closed +c circulation, num_requested_contours will = 1, and we will exit +c after finding that 1 contour. But for a check after making a +c full center fix, we set num_requested_contours = 999 so that +c we can keep searching for all closed contours around the low. +c In this 999 case, you will eventually get to a point where +c there is no closed contour. In that case, in the standard +c output you will see a message telling you that you hit a point +c that is not in the contour and that there is no closed contour, +c but you will also notice that the ccflag = y, meaning there is +c a closed contour (because you have found at least 1 closed +c contour along the way). The reason to keep searching for more +c closed contours is that we can then return the value of the +c outermost closed isobar. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c cmaxmin character string ('max' or 'min') that tells this +c routine what we're looking for. +c trkrinfo derived type that holds/describes various tracker parms +c contour_info Type cint_stuff from module contours. Contains +c contour information +c num_requested_contours For the simple first_ges_center check, +c this will be 1 (we just want to know if there's at +c least 1 closed contour). For the verifying check after +c we've found a center, this will be 9999 (i.e., just keep +c searching for more contours) +c get_last_isobar_flag character ('y' or 'n') to indicate whether +c or not to report on the value of the last closed isobar +c and the radius of the last closed isobar. +c +c OUTPUT: +c closed_contour character; A returned value of 'y' indicates that +c this routine was able to find a closed contour. +c plastbar Contains the value of the last closed isobar (unrounded) +c rlastbar Contains the mean radius of the last closed isobar +c +c LOCAL: +c num_pts_in_all_contours Counter for the number of pts inside of +c the contour we're looking at +c next_ring_ct Counter for the number of points that have been +c tagged to be used as center points for the next +c iteration of multiple_ring_loop. +c next_contour_ct Counter for the number of points that have been +c tagged to be used as center points in the first iteration +c through single_contour_scan_loop as we begin to scan +c points in the *next* contour interval. This counter gets +c incremented when, for example, we are searching points +c around a current center point and we find one that is not +c in our current interval, but rather is in the next +c interval. We want to remember this point and store the +c location, so we increment this counter and store the +c location in next_contour_i and next_contour_j arrays. +c beyond_contour_ct Counter for the number of points that have been +c tagged to be used as center points for some subsequent +c iteration of successive_contours_loop. This is +c different from next_contour_ct, which is used to hold +c the locations of points that are definitely in the +c *next* contour interval. Here, we have points that we +c just store in a pool of potential points to be searched +c in future iterations. These points can come about in +c cases where there is a very intense, very compact low +c with a tight pressure gradient, such that multiple +c contour intervals could be spanned in between 2 adjacent +c gridpoints (this is especially the case if the contour +c interval you have chosen is small). You need to be +c careful with how you handle this array. Once you find +c that you have searchable points in next_contour_i or +c next_contour_j, do not just simply empty out this +c beyond_contour count and its i and j arrays. The +c reason being that some of these "beyond" points may end +c up being used and searched in subsequent iterations, but +c not if we just delete them now. + + + USE set_max_parms; USE trkrparms; USE contours; USE grid_bounds + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,ir,iria,irja,irx,jrx,ix,jx,imax,jmax + integer nb,ibx,jby,nct,iflip + integer mr,ringct,ixp1,ixm1,jxp1,jxm1,nring,iter + integer icenx,jcenx,icccret,next_ring_ct,igicwret + integer num_pts_in_all_contours,next_contour_ct + integer beyond_contour_ct + integer num_pts_in_one_contour + integer num_requested_contours,num_found_contours + integer nm,im,jm,inall,insingle,isc_count,rlast_distct + character found_a_point_in_our_contour*1,closed_contour*1 + character found_a_point_below_contour*1 + character found_a_point_above_contour*1,get_last_isobar_flag*1 + character(*) cmaxmin + logical(1) still_scanning + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + logical(1) point_is_already_in_our_contour(imax,jmax) + logical(1) point_is_already_in_next_contour(imax,jmax) + logical(1) point_is_already_in_beyond_pool(imax,jmax) + integer isni,isnj,inci,incj,ibci,ibcj,ihmi,ihmj,itmi,itmj + integer, allocatable :: search_next_i(:) + integer, allocatable :: search_next_j(:) + integer, allocatable :: next_contour_i(:) + integer, allocatable :: next_contour_j(:) + integer, allocatable :: beyond_contour_i(:) + integer, allocatable :: beyond_contour_j(:) + integer, allocatable :: hold_mask_i_loc(:) + integer, allocatable :: hold_mask_j_loc(:) + integer, allocatable :: temp_mask_i_loc(:) + integer, allocatable :: temp_mask_j_loc(:) + integer, allocatable :: ringposi(:),ringposj(:) + real,allocatable :: ringpos(:,:) + real fxy(imax,jmax),contvals(maxconts) + real contlo,conthi,xcentval,contlo_next,conthi_next + real dist,degrees,rlast_distsum,plastbar,rlastbar +c + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + allocate (search_next_i(imax*jmax),stat=isni) + allocate (search_next_j(imax*jmax),stat=isnj) + allocate (next_contour_i(imax*jmax),stat=inci) + allocate (next_contour_j(imax*jmax),stat=incj) + allocate (beyond_contour_i((imax*jmax)/2),stat=ibci) + allocate (beyond_contour_j((imax*jmax)/2),stat=ibcj) + allocate (hold_mask_i_loc(imax*jmax),stat=ihmi) + allocate (hold_mask_j_loc(imax*jmax),stat=ihmj) + allocate (temp_mask_i_loc(imax*jmax),stat=itmi) + allocate (temp_mask_j_loc(imax*jmax),stat=itmj) + if (isni /= 0 .or. isnj /= 0 .or. inci /= 0 .or. incj /= 0 .or. + & ibci /= 0 .or. ibcj /= 0 .or. ihmi /= 0 .or. ihmj /= 0 .or. + & itmi /= 0 .or. itmj /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various search, hold and temp arrays.' + print *,'!!! isni = ',isni,' isnj= ',isnj + print *,'!!! inci = ',inci,' incj= ',incj + print *,'!!! ibci = ',ibci,' ibcj= ',ibcj + print *,'!!! ihmi = ',ihmi,' ihmj= ',ihmj + print *,'!!! itmi = ',itmi,' itmj= ',itmj + print *,' ' + endif + + STOP 98 + endif + + closed_contour = 'n' + xcentval = fxy(ix,jx) + num_found_contours = 0 + next_contour_ct = 0 + beyond_contour_ct = 0 + num_pts_in_all_contours = 0 + hold_mask_i_loc = 0 + hold_mask_j_loc = 0 + beyond_contour_i = 0 + beyond_contour_j = 0 + point_is_already_in_our_contour = .false. + point_is_already_in_beyond_pool = .false. + icccret = 0 + isc_count = 0 + plastbar = -999.0 + rlastbar = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* Top of check_closed_contour, ix= ',ix,' jx= ',jx + print *,'*-----------------------------------------------*' + print *,' ' + print *,'fxy(ix,jx)= ',fxy(ix,jx),' xcentval= ',xcentval + endif + +c First, set up the contour intervals that will be used. In +c the original version of this code, we used preset +c standard intervals (984,988,992,996,1000,1004....). But upon +c further review, it was decided that this was too arbitrary. +c So instead, we consider the found min (max) value to be the +c bottom (top) of the list of contour intervals. In this way, +c we can clearly specify and screen storms based on the "depth" +c of the pressure field as compared to the surroundings. + + i = 1 + do while (i <= maxconts) + if (cmaxmin == 'min') then + contvals(i) = xcentval + float(i-1)*trkrinfo%contint + i = i + 1 + else + iflip = maxconts - i + 1 + contvals(iflip) = xcentval - float(i-1)*trkrinfo%contint + i = i + 1 + endif + enddo + +c This successive_contours loop is the master loop.... + + successive_contours_loop: do while (num_found_contours < + & num_requested_contours) + +c Find the contour interval in which the center value resides. +c Note that the lower bound is included for a min check, while +c the upper bound is included for a max check. Note also that +c this subroutine can be used to find the last closed contour, +c and part of that functionality shows up in the next while +c statement where we reference "num_found_contours" in the +c array indeces for the contour values. Basically, the way we +c do this is, for example, if our central value is 990.4 mb and +c our contour interval is 4 mb, then in the first run through +c successive_contours_loop we see if we have a closed contour in +c the interval 990.4-994.4. If yes, then the next time through +c this loop, we see if we have a closed contour in the interval +c 994.4-998.4. If yes, then the next loop check is for 998.4- +c 1002.4, and so on.... We stop searching if we find a value +c that is either below the xcentval input into this subroutine +c or below the lower value of the current contour interval (this +c would mean a change in the gradient and would indicate that, +c in the case of mslp, we are heading down towards another, +c different low). + + isc_count = isc_count + 1 + + point_is_already_in_next_contour = .false. + + i = 1 + do while (i < maxconts) + if (cmaxmin == 'min') then + if (contvals(i) <= xcentval .and. xcentval < contvals(i+1)) + & then + + if ( verb .ge. 3 ) then + print *,'At A, num_found_contours= ',num_found_contours + endif + + contlo = contvals(i+num_found_contours) + conthi = contvals(i+1+num_found_contours) + + if ( verb .ge. 3 ) then + print *,'At A, contlo= ',contlo,' conthi= ',conthi + endif + exit + + endif + else + if (contvals(i) < xcentval .and. xcentval <= contvals(i+1)) + & then + contlo = contvals(i-num_found_contours) + conthi = contvals(i-num_found_contours+1) + exit + endif + endif + i = i + 1 + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'num_found_contours= ',num_found_contours + print *,'contlo= ',contlo,' conthi= ',conthi + print *,'xcentval= ',xcentval + endif + + +c This single_contour_scan_loop is the main loop for searching +c for one individual contour. If it is determined that a contour +c exists, control is returned to the successive_contours_loop, +c and if more contours were requested to be found, then the +c search continues onward & outward.... + + temp_mask_i_loc = 0 + temp_mask_j_loc = 0 + + iter = 1 + num_pts_in_one_contour = 0 + still_scanning = .true. + + rlast_distsum = 0.0 + rlast_distct = 0 + + single_contour_scan_loop: do while (still_scanning) + +c print *,' ' +c print *,' top of single contour scan loop' +c print *,'+++ iter= ',iter +c print *,' N1: next_contour_ct= ',next_contour_ct + + if (iter == 1 .and. num_found_contours == 0) then + ! For the first iteration, we have only the first ring, + ! which is centered on the input minimum/maximum point. + ringct = 1 + search_next_i(1) = ix + search_next_j(1) = jx + +c point_is_already_in_our_contour(ix,jx) = .true. +c num_pts_in_one_contour = num_pts_in_one_contour + 1 +c temp_mask_i_loc(num_pts_in_one_contour) = ix +c temp_mask_j_loc(num_pts_in_one_contour) = jx + + else if (iter == 1 .and. num_found_contours > 0) then + ! This is the first iteration in a *new* contour. + ! That is, we have already found 1 or more previous + ! contours while in previous iterations of + ! successive_contours_loop and we are now beginning + ! to look for the next contour. + +c print *,' N2: next_contour_ct= ',next_contour_ct + + if (next_contour_ct == 0) then + ! This would be for the special case in which, for + ! example, you've got a very intense, compact storm + ! that "skips" a contour. That is, suppose the + ! min pressure of a storm is 982 mb, and we are + ! utilizing a 4-mb contour interval, but all + ! surrounding data points are, say, 987 mb or + ! higher. Then, next_contour_ct would be 0 since no + ! data points were found in the next contour interval + ! of 982-986 mb, but we can continue searching since the + ! gradient is still sloping the correct way. The code in + ! this if statement handles this special case. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ALERT: next_contour_ct = 0 ' + endif + + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + +c print *,'b4 ZZ, ringct= ',ringct +c print *,'at ZZ, bcc= ',beyond_contour_ct +c & ,'contlo_next= ',contlo_next +c & ,'conthi_next= ',conthi_next + + bey_con_min_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_min_loop + endif + +c print *,'-- ZZ, ibx= ',ibx,' jby= ',jby +c & ,' fxy(ibx,jby)= ',fxy(ibx,jby) + + if (fxy(ibx,jby) >= contlo_next .and. + & fxy(ibx,jby) < conthi_next) then + +c print *,'>> ZZ HIT!!, ibx= ',ibx,' jby= ',jby +c +c print *,' +++ BEYOND in NEXT: i= ',ibx,' j= ',jby +c & ,' fxy= ',fxy(ibx,jby) + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + +c print *,'.. ZZ, next_contour_ct= ',next_contour_ct + + enddo bey_con_min_loop + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + +c print *,'At A, beyond_contour_ct= ',beyond_contour_ct +c print *,' contlo_next = ',contlo_next +c print *,' conthi_next = ',conthi_next + + bey_con_max_loop: do nb = 1,beyond_contour_ct + +c print *,'in bey_con_max_loop, nb= ',nb + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_max_loop + endif + +c print *,'ibx= ',ibx,' jby= ',jby,' data= ' +c & ,fxy(ibx,jby) + + if (fxy(ibx,jby) > contlo_next .and. + & fxy(ibx,jby) <= conthi_next) then + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + +c print *,' ++ HIT! ibx= ',ibx,' jby= ',jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + enddo bey_con_max_loop + endif + + if (next_contour_ct > 0) then + ringct = next_contour_ct + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! XXX next_contour_ct not > 0 !!!' + print *,'next_contour_ct= ',next_contour_ct + print *,'beyond_contour_ct= ',beyond_contour_ct + print *,'ringct= ',ringct + print *,'next_ring_ct= ',next_ring_ct + print *,'cycling to top of successive_contours_loop..' + print *,' ' + endif + + ! The number of rings that we have available to search + ! in the next contour interval is 0, so cycle all the + ! way back to the top of the outer loop, which is + ! successive_contours_loop, so that we can increase the + ! contour bounds and search inside those new bounds. + ! Again, this is for the case in which we have an + ! intense, compact storm and we are using a small + ! contour interval, such that we are essentially + ! "skipping" over one of these intervals in one of the + ! loop iterations. We need to bump up the + ! num_found_contours by one in order to increase the + ! array index in the contvals array at the top of the + ! successive_contours_loop. It is kosher to do this + ! since the reason we are cycling back to the top of + ! that loop is that we are skipping over a contour + ! interval. + + num_found_contours = num_found_contours + 1 + cycle successive_contours_loop + + endif + + else + + ringct = next_contour_ct + + endif + + do nring = 1,ringct + search_next_i(nring) = next_contour_i(nring) + search_next_j(nring) = next_contour_j(nring) +c print *,'at A, nring= ',nring,' next_contour_i(nring)= ' +c & ,next_contour_i(nring),' next_contour_j(nring)= ' +c & ,next_contour_j(nring) + enddo + + next_contour_ct = 0 + + else + ringct = next_ring_ct + endif + + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + allocate (ringposi(ringct),stat=iria) + allocate (ringposj(ringct),stat=irja) + if (iria /= 0 .or. irja /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various ring arrays. iria = ',iria + print *,'!!! irja = ',irja + print *,' ' + endif + + STOP 98 + endif + +ctm +c print *,' ' +c print *,'ringct= ',ringct + + do nring = 1,ringct + ringposi(nring) = search_next_i(nring) + ringposj(nring) = search_next_j(nring) +ctm +c print *,'nring= ',nring,' ringposi= ',ringposi(nring) +c & ,' ringposj= ',ringposj(nring) + enddo + + next_ring_ct = 0 + + ! This next loop reviews the points that have been + ! labelled for the "beyond_contour" pool. As we get further + ! into successive iterations of successive_contours_loop, + ! some of these previously "beyond" points are now within + ! the contour interval range that we are checking, so we + ! need to go through the list of "beyond" points and remove + ! any that are no longer in that "beyond" category.... + + check_beyond_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! This point may have been removed already in a + ! previous iteration of successive_contours_loop. + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle check_beyond_loop + endif + + ! Check to see if any of the points being searched in the + ! upcoming multiple_ring_loop are points that had previously + ! been saved as "beyond_contour" points. If so, remove + ! their status as "beyond_contour" points by setting the + ! logical flag to false. + + do nring = 1,ringct + + if (ibx == ringposi(nring) .and. jby == ringposj(nring)) + & then +c print *,' ' +c print *,'!!! beyond remove: ibx= ',ibx,' jby= ',jby + point_is_already_in_beyond_pool(ibx,jby) = .false. + endif + + enddo + + enddo check_beyond_loop + + +c In each iteration of single_contour_scan_loop, we can have a +c different number of rings to analyze. In the first +c iteration, we only have 1 ring, the initial ring around the +c local max/min that was input to this subroutine. Subsequent +c iterations will have a variable number of rings, depending on +c how many new data points within our contour interval were +c found in the previous iteration. + + multiple_ring_loop: do mr = 1,ringct + + icenx = ringposi(mr) + jcenx = ringposj(mr) + +ctm +c print *,' --- iter= ',iter,' mr= ',mr,' icenx= ',icenx +c & ,' jcenx= ',jcenx,' imax= ',imax,' jmax= ',jmax + + call get_ijplus1_check_wrap (imax,jmax,icenx,jcenx,ixp1,jxp1 + & ,ixm1,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NO CLOSED CONTOUR: The call to ' + print *,'!!! get_ijplus1_check_wrap indicates the' + print *,'!!! max/min contour extends past the edge of' + print *,'!!! our regional grid. ' + print *,' ' + print *,' ' + endif + + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c For each individual ring, we check all 8 points surrounding +c the center point. The points are numbered for each ring as +c shown in the diagram to the right of the "select case" +c statement just below. REMEMBER: The j in our grids +c increases from north to south, so that for a global grid, +c j=1 is at 90N and j=jmax is at 90S. + + individual_ring_loop: do ir = 1,9 + + select case (ir) + case (1); irx=ixm1; jrx=jcenx;! 2 3 4 + case (2); irx=ixm1; jrx=jxm1; ! + case (3); irx=icenx;jrx=jxm1; ! + case (4); irx=ixp1; jrx=jxm1; ! 1 (icenx,jcenx) 5 + case (5); irx=ixp1; jrx=jcenx;! + case (6); irx=ixp1; jrx=jxp1; ! + case (7); irx=icenx;jrx=jxp1; ! 8 7 6 + case (8); irx=ixm1; jrx=jxp1; ! + case (9); irx=icenx; jrx=jcenx; ! = center pt of ring + end select + +c Make sure the point we are looking at has valid data. +c This is an issue only on regional grids, where we have a +c buffer of bitmapped (null) data points surrounding the +c real grid. + +c print *,'ind ring loop: ir= ',ir,' irx= ',irx,' jrx= ',jrx + + if (.not. valid_pt(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a non-' + print *,'!!! valid point, meaning we are near the' + print *,'!!! bounds of the grid, or at least the ' + print *,'!!! bounds of the valid data for this ' + print *,'!!! grid. We will skip the' + print *,'!!! search for this center.' + print *,'!!! ' + print *,'!!! (i,j) of non-valid pt = (' + & ,irx,',',jrx,')' + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c Check to make sure that the point we are looking at is +c not considered under the influence of another nearby low. + + if (masked_out(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a point' + print *,'!!! that has been masked out, meaning it' + print *,'!!! belongs under the influence of ' + print *,'!!! another nearby low, so we will skip' + print *,'!!! the search for this center....' + print *,'!!! ' + print *,'!!! Min central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Masked-out value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of masked value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we have already hit this point on a previous ring +c check, then just ignore this point and cycle past it. + + if (point_is_already_in_our_contour(irx,jrx)) then +ctm +c print *,' ' +c print *,'Pt. AAA, already-in-contour.....' +c print *,'irx= ',irx,' jrx= ',jrx + cycle individual_ring_loop + endif + +c For a MIN check, check to see if the data point is below +c the contour interval or is below the local minimum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c For a MAX check, check to see if the data point is above +c the contour interval or is above the local maximum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c +c For example, for mslp, this would be as we're moving +c outward away from lower pressures to higher pressures, +c and then all of a sudden we come upon a lower pressure. +c This probably means we're heading toward another low +c pressure area, so mark the point and return to the +c calling routine. + + found_a_point_below_contour = 'n' + found_a_point_above_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) < xcentval .or. fxy(irx,jrx) < contlo) + & then + found_a_point_below_contour = 'y' + endif + else + if (fxy(irx,jrx) > xcentval .or. fxy(irx,jrx) > conthi) + & then + found_a_point_above_contour = 'y' + endif + endif + + if (found_a_point_below_contour == 'y' .or. + & found_a_point_above_contour == 'y') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a data' + print *,'!!! value that is less (greater) than the' + print *,'!!! current contour interval bound for a' + print *,'!!! min (max) and/or is less (greater) ' + print *,'!!! than the minimum (maximum) central ' + print *,'!!! value that we are centering the ' + print *,'!!! search on.' + print *,'!!! ' + print *,'!!! Central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Flagged value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of flagged value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we've made it this far, then we at least know that the +c gradient is still heading in the right direction. Do the +c check now to see if the value at this point is within our +c specific contour interval (there is the possibility that +c the value is beyond our interval, which will be checked +c for just below, and if that's the case, then that point +c will be processed in a subsequent iteration of this loop +c that encompasses that correct contour interval). + + found_a_point_in_our_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) >= contlo .and. fxy(irx,jrx) < conthi) + & then + found_a_point_in_our_contour = 'y' + endif + else + if (fxy(irx,jrx) > contlo .and. fxy(irx,jrx) <= conthi) + & then + found_a_point_in_our_contour = 'y' + endif + endif + + if (found_a_point_in_our_contour == 'y') then + ! We've found a data point in our interval, something + ! that is inside the closed contour, and it hasn't been + ! marked as being found in a previous iteration of this + ! loop, so mark it now and store the (i,j) location so + ! that we can scan a ring around this point in a + ! successive iteration of this loop for more potential + ! points within this interval... + + point_is_already_in_our_contour(irx,jrx) = .true. + + next_ring_ct = next_ring_ct + 1 + search_next_i(next_ring_ct) = irx + search_next_j(next_ring_ct) = jrx + +c print *,'at B, next_ring_ct= ',next_ring_ct +c & ,' search_next_i()= ',search_next_i(next_ring_ct) +c & ,' search_next_j()= ',search_next_j(next_ring_ct) + + num_pts_in_one_contour = num_pts_in_one_contour + 1 + temp_mask_i_loc(num_pts_in_one_contour) = irx + temp_mask_j_loc(num_pts_in_one_contour) = jrx + + if (get_last_isobar_flag == 'y') then + call calcdist (glon(ix),glat(jx) + & ,glon(irx),glat(jrx),dist,degrees) + rlast_distsum = rlast_distsum + dist + rlast_distct = rlast_distct + 1 + endif + +ctm +c print *,' ' +c print *,' PT IN! irx= ',irx,' jrx= ',jrx,' xval= ' +c & ,fxy(irx,jrx) +c print *,'next_ring_ct= ',next_ring_ct +c print *,'num_pts_in_one_contour= ' +c & ,num_pts_in_one_contour + endif + +c If we've made it this far AND the +c found_a_point_in_our_contour flag indicates that this +c point is not in our contour interval, then by default that +c means that this point is for a contour interval beyond +c what we're currently looking at. E.g., if we're looking +c at the contours around a 972 mb low and we're moving +c outward and currently checking the 984-988 mb contour +c interval, it means that we found, say, a gridpoint with +c 991 mb. So we want to mark that point for a future +c iteration of this loop that would be checking the +c 988-992 mb contour interval. + + if (found_a_point_in_our_contour /= 'y' .and. + & .not. point_is_already_in_next_contour(irx,jrx)) then + ! We've found a data point that is beyond our interval, + ! so this is not a concern for finding the bounds of + ! our current contour interval, but we want to mark + ! these points and remember them for the next iteration + ! of successive_scan_loop. (For example, suppose we + ! are currently searching for points in the 984-988 mb + ! range, and we find a point that is 990 -- mark it + ! here to be remembered when we scan for 988-992 mb). + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + if (fxy(irx,jrx) >= contlo_next .and. + & fxy(irx,jrx) < conthi_next) then + ! "NEXT_CONTOUR" Comment: + ! We've found a point that is in the very next + ! contour interval.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. + else if (fxy(irx,jrx) >= conthi_next) then + ! "BEYOND_CONTOUR" Comment: + ! This point is at least 1 contour interval beyond + ! the next contour interval. Dump the info into + ! these i and j arrays. This info will be used if + ! in the next iteration of single_contour_scan_loop, + ! next_contour_ct = 0. That would mean that we + ! have, e.g., an intensely deep low with a sharp + ! mslp gradient that essentially "skips" over a + ! contour interval. E.g., if using a 4 mb interval, + ! we go from 947 to 953 AND there are NO + ! intervening gridpoints in the 948-952 interval. + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) + endif + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + if (fxy(irx,jrx) > contlo_next .and. + & fxy(irx,jrx) <= conthi_next) then + ! See "NEXT_CONTOUR" comment just above.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. +c print *,'NEXT ncc= ',next_contour_ct +c & ,'next_contour_i()= ' +c & ,next_contour_i(next_contour_ct) +c & ,'next_contour_j()= ' +c & ,next_contour_j(next_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + else if (fxy(irx,jrx) <= contlo_next) then + ! See "BEYOND_CONTOUR" comment just above.... + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'BEYOND bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + endif + endif + endif + + enddo individual_ring_loop + + enddo multiple_ring_loop + + if (next_ring_ct > 0) then + iter = iter + 1 + else + icccret = 0 + still_scanning = .false. + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + num_found_contours = num_found_contours + 1 + closed_contour = 'y' + if (num_found_contours == 1) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Closed contour found ' + endif + + endif + endif + + enddo single_contour_scan_loop + + do insingle = 1,num_pts_in_one_contour + num_pts_in_all_contours = num_pts_in_all_contours + 1 + inall = num_pts_in_all_contours + hold_mask_i_loc(inall) = temp_mask_i_loc(insingle) + hold_mask_j_loc(inall) = temp_mask_j_loc(insingle) + enddo + + if (get_last_isobar_flag == 'y') then + if (cmaxmin == 'min') then + plastbar = conthi + else + plastbar = contlo + endif + if (rlast_distct > 0) then + rlastbar = rlast_distsum / float(rlast_distct) + rlastbar = rlastbar * 0.539638 ! convert km to nm + else + rlastbar = -999.0 + endif + endif + + enddo successive_contours_loop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'END SUM: num of iterations = ',isc_count + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_land_mask (imax,jmax,ix,jx,fract_land,valid_pt + & ,dx,dy,point_is_over_water,iclmret) +c +c ABSTRACT: This subroutine looks at the values for the land-sea +c mask surrounding an input (i,j) position to determine if less +c than 50% of the area surrounding the input (i,j) position within +c 75 km radius is land. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c +c OUTPUT: +c fract_land Fraction of points/area that is covered by land +c point_is_over_water y/n: A value of 'y' is returned if <50% +c of the points/area is covered by land +c iclmret Return code from this routine +c + USE grid_bounds; USE tracked_parms + USE trkrparms; USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + logical(1) valid_pt(imax,jmax) + character point_is_over_water*1 + integer, parameter :: numazim=8 + integer iazim,ibiret1,imax,jmax,ix,jx,iclmret,imct + real bear,targlat,targlon,xplon,yplat,rdist,xintrp_mask + real fract_land,dx,dy,xmask_sum +c + iclmret = 0 + +c First, calculate the longitude and latitude of the input ix and +c jx points. If the xplon value ends up being >360.0 (this can +c happen for basin-scale HWRF), don't worry about it. Just leave +c it be, as the trigonometry will work out the same for lons >360. + + xplon = glonmin + (ix-1)*dx + yplat = glatmax - (jx-1)*dy + + rdist = 75.0 ! (We will always look only 75 km radius out for + ! this particular land-sea mask application) + + imct = 0 + +c Now go around the storm via azimloop and get interpolated +c values of the land-sea mask at each azimuth at a radial +c distance of 75 km from the center point.... + + xmask_sum = 0.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 45.0 + + call distbear (yplat,xplon,rdist,bear,targlat,targlon) + + ! These calls to bilin_int_uneven pass a variable, level, + ! that is used for applications of interpolating wind + ! data. Here, we are instead interpolating the land-sea + ! mask data, so we don't care about the level, so just + ! pass a dummy value of 850, which never gets used. + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,850,'m',xintrp_mask,ibiret1) + + if (ibiret1 == 0) then + xmask_sum = xmask_sum + xintrp_mask + imct = imct + 1 + else + iclmret = 95 + return + endif + + enddo azimloop + +c Now get the mask value directly at the point that was input to +c this routine.... + + xmask_sum = xmask_sum + lsmask(ix,jx) + imct = imct + 1 + +c Now get the mean land fraction.... + + if (imct > 0) then + + fract_land = xmask_sum / float(imct) + if (fract_land < 0.50) then + point_is_over_water = 'y' + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Land check yes: Point is over water. ' + print *,' Land check value: fract_land= ',fract_land + endif + else + point_is_over_water = 'n' + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Land check NO: Point is over land. ' + print *,' Land check value: fract_land= ',fract_land + endif + endif + + else + + iclmret = 95 + return + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine get_ijplus1_check_wrap (imax,jmax,i,j,iplus1,jplus1 + & ,iminus1,jminus1,trkrinfo,igicwret) +c +c ABSTRACT: This subroutine takes an (i,j) position input and +c returns the four neighboring (i,j) points to the east, south, +c west and north. The routine checks for wrap around the GM, so +c that if, for example, you are on a global 360x181 grid and you +c are at point i=360, then i+1 = 361, so you need something to +c adjust that back to i = 1. Likewise, if you are at i=1 and +c looking for point i-1, it will adjust it to be point 360 +c instead of the meaningless point 0 (i=0). + + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer i,j,imax,jmax,iplus1,jplus1,iminus1,jminus1,igicwret + + igicwret = 0 + + jplus1 = j + 1 + jminus1 = j - 1 + iplus1 = i + 1 + iminus1 = i - 1 + + if (iplus1 > imax) then + if (trkrinfo%gridtype == 'global') then + iplus1 = iplus1 - imax ! If wrapping east of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is too close to the eastern bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested eastern ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',iplus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (iminus1 < 1) then + if (trkrinfo%gridtype == 'global') then + iminus1 = imax + iminus1 ! If wrapping west of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested western search boundary' + print *,'!!! is too close to the western bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested western ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested western i = ',iminus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (jplus1 > jmax .or. jminus1 < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The ' + print *,'!!! user-requested northern or southern search' + print *,'!!! boundary is too close to the bounds of the' + print *,'!!! grid. Cut back your requested northern or' + print *,'!!! southern boundary by a degree or 2 in the' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested northern j = ',jminus1 + print *,'!!! User-requested southern j = ',jplus1 + print *,'!!! jmax of grid = ',jmax + print *,' ' + endif + + igicwret = 91 + return + endif + + return + end + +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + SUBROUTINE qsort(x,ind,n) +c +c Code converted using TO_F90 by Alan Miller +c Date: 2002-12-18 Time: 11:55:47 + + IMPLICIT NONE + INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(12, 60) + + REAL (dp), INTENT(IN) :: x(n) + INTEGER, INTENT(OUT) :: ind(n) + INTEGER, INTENT(IN) :: n + +c *************************************************************************** +c +c ROBERT RENKA +c OAK RIDGE NATL. LAB. +c +c THIS SUBROUTINE USES AN ORDER N*LOG(N) QUICK SORT TO SORT A REAL (dp) +c ARRAY X INTO INCREASING ORDER. THE ALGORITHM IS AS FOLLOWS. IND IS +c INITIALIZED TO THE ORDERED SEQUENCE OF INDICES 1,...,N, AND ALL INTERCHANGES +c ARE APPLIED TO IND. X IS DIVIDED INTO TWO PORTIONS BY PICKING A CENTRAL +c ELEMENT T. THE FIRST AND LAST ELEMENTS ARE COMPARED WITH T, AND +c INTERCHANGES ARE APPLIED AS NECESSARY SO THAT THE THREE VALUES ARE IN +c ASCENDING ORDER. INTERCHANGES ARE THEN APPLIED SO THAT ALL ELEMENTS +c GREATER THAN T ARE IN THE UPPER PORTION OF THE ARRAY AND ALL ELEMENTS +c LESS THAN T ARE IN THE LOWER PORTION. THE UPPER AND LOWER INDICES OF ONE +c OF THE PORTIONS ARE SAVED IN LOCAL ARRAYS, AND THE PROCESS IS REPEATED +c ITERATIVELY ON THE OTHER PORTION. WHEN A PORTION IS COMPLETELY SORTED, +c THE PROCESS BEGINS AGAIN BY RETRIEVING THE INDICES BOUNDING ANOTHER +c UNSORTED PORTION. +c +c INPUT PARAMETERS - N - LENGTH OF THE ARRAY X. +c +c X - VECTOR OF LENGTH N TO BE SORTED. +c +c IND - VECTOR OF LENGTH >= N. +c +c N AND X ARE NOT ALTERED BY THIS ROUTINE. +c +c OUTPUT PARAMETER - IND - SEQUENCE OF INDICES 1,...,N PERMUTED IN THE SAME +c FASHION AS X WOULD BE. THUS, THE ORDERING ON +c X IS DEFINED BY Y(I) = X(IND(I)). +c +c ********************************************************************* + + ! NOTE -- IU AND IL MUST BE DIMENSIONED >= LOG(N) WHERE LOG HAS BASE 2. + + !********************************************************************* + + INTEGER :: iu(21), il(21) + INTEGER :: m, i, j, k, l, ij, it, itt, indx + REAL :: r + REAL (dp) :: t + + ! LOCAL PARAMETERS - + + ! IU,IL = TEMPORARY STORAGE FOR THE UPPER AND LOWER + ! INDICES OF PORTIONS OF THE ARRAY X + ! M = INDEX FOR IU AND IL + ! I,J = LOWER AND UPPER INDICES OF A PORTION OF X + ! K,L = INDICES IN THE RANGE I,...,J + ! IJ = RANDOMLY CHOSEN INDEX BETWEEN I AND J + ! IT,ITT = TEMPORARY STORAGE FOR INTERCHANGES IN IND + ! INDX = TEMPORARY INDEX FOR X + ! R = PSEUDO RANDOM NUMBER FOR GENERATING IJ + ! T = CENTRAL ELEMENT OF X + + IF (n <= 0) RETURN + + ! INITIALIZE IND, M, I, J, AND R + + DO i = 1, n + ind(i) = i + END DO + m = 1 + i = 1 + j = n + r = .375 + + ! TOP OF LOOP + + 20 IF (i >= j) GO TO 70 + IF (r <= .5898437) THEN + r = r + .0390625 + ELSE + r = r - .21875 + END IF + + ! INITIALIZE K + + 30 k = i + + ! SELECT A CENTRAL ELEMENT OF X AND SAVE IT IN T + + ij = i + r*(j-i) + it = ind(ij) + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) > t) THEN + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + END IF + + ! INITIALIZE L + + l = j + + ! IF THE LAST ELEMENT OF THE ARRAY IS LESS THAN T, + ! INTERCHANGE IT WITH T + indx = ind(j) + IF (x(indx) >= t) GO TO 50 + ind(ij) = indx + ind(j) = it + it = indx + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) <= t) GO TO 50 + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + GO TO 50 + + ! INTERCHANGE ELEMENTS K AND L + + 40 itt = ind(l) + ind(l) = ind(k) + ind(k) = itt + + ! FIND AN ELEMENT IN THE UPPER PART OF THE ARRAY WHICH IS + ! NOT LARGER THAN T + + 50 l = l - 1 + indx = ind(l) + IF (x(indx) > t) GO TO 50 + + ! FIND AN ELEMENT IN THE LOWER PART OF THE ARRAY WHCIH IS NOT SMALLER THAN T + + 60 k = k + 1 + indx = ind(k) + IF (x(indx) < t) GO TO 60 + + ! IF K <= L, INTERCHANGE ELEMENTS K AND L + + IF (k <= l) GO TO 40 + + ! SAVE THE UPPER AND LOWER SUBSCRIPTS OF THE PORTION OF THE + ! ARRAY YET TO BE SORTED + + IF (l-i > j-k) THEN + il(m) = i + iu(m) = l + i = k + m = m + 1 + GO TO 80 + END IF + + il(m) = k + iu(m) = j + j = l + m = m + 1 + GO TO 80 + + + ! BEGIN AGAIN ON ANOTHER UNSORTED PORTION OF THE ARRAY + + 70 m = m - 1 + IF (m == 0) RETURN + i = il(m) + j = iu(m) + + 80 IF (j-i >= 11) GO TO 30 + IF (i == 1) GO TO 20 + i = i - 1 + + ! SORT ELEMENTS I+1,...,J. NOTE THAT 1 <= I < J AND J-I < 11. + + 90 i = i + 1 + IF (i == j) GO TO 70 + indx = ind(i+1) + t = x(indx) + it = indx + indx = ind(i) + IF (x(indx) <= t) GO TO 90 + k = i + + 100 ind(k+1) = ind(k) + k = k - 1 + indx = ind(k) + IF (t < x(indx)) GO TO 100 + + ind(k+1) = it + GO TO 90 + END SUBROUTINE qsort + +c +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT11/FORT31 + subroutine open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + +C ABSTRACT: This subroutine must be called before any attempt is +C made to read from the input GRIB files. The GRIB and index files +C are opened with a call to baopenr. This call to baopenr was not +C needed in the cray version of this program (the files could be +C opened with a simple Cray assign statement), but the GRIB-reading +C utilities on the SP do require calls to this subroutine (it has +C something to do with the GRIB I/O being done in C on the SP, and +C the C I/O package needs an explicit open statement). +C +C INPUT: +c inp Contains user-input info on the date & data +C lugb The Fortran unit number for the GRIB data file +C lugi The Fortran unit number for the GRIB index file +c ifh integer index for lead time level +c gfilename If using individual files for each tau, gfilename will +c contain the grib data filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +c ifilename If using individual files for each tau, gfilename will +c contain the grib index filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +C lout The Fortran unit number for the output grib file +C +C OUTPUT: +C iret The return code from this subroutine + + USE inparms + USE verbose_output + + implicit none +c + type (datecard) inp + + logical(1) output_file_open + logical(1) file_open + character(*) gfilename,ifilename +c character(120) gopen_g_file,gopen_i_file +cPeng 05/28/2018 Bug Fixed for FV3-GFS Job Crashed. + character(255) gopen_g_file,gopen_i_file + character(2) lugb_c,lugi_c + character(6) enameb,enamei + integer igoret,iioret,iooret,lugb,lugi,lout,iret,nlen1,nlen2 + + iret=0 + + if (inp%file_seq == 'onebig') then + write(lugb_c,'(i2)')lugb + write(lugi_c,'(i2)')lugi + enameb='FORT'//adjustl(lugb_c) + enamei='FORT'//adjustl(lugi_c) + call get_environment_variable(enameb,gopen_g_file,status=igoret) + call get_environment_variable(enamei,gopen_i_file,status=iioret) + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + gopen_g_file(1:5) = "fort." + gopen_i_file(1:5) = "fort." + write(gopen_g_file(6:7),'(I2)') lugb + write(gopen_i_file(6:7),'(I2)') lugi + endif + else + nlen1 = len_trim(gfilename) + gopen_g_file = trim(gfilename(1:nlen1)) + nlen2 = len_trim(ifilename) + gopen_i_file = trim(ifilename(1:nlen2)) + endif + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + +c print *,'gopen_g_file= ',gopen_g_file,'....' +c print *,'gopen_i_file= ',gopen_i_file,'....' + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end + +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT12 + subroutine read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + ii=1 + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + maxstorm = numtcv + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that you have the Fortran' + print *,'!!! unit assigned right in your script.' + endif + + iret = 99 + return + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT14 + subroutine read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + ii = numtcv + 1 + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1,1x + & ,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_main_gfs.f_10152019 b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_main_gfs.f_10152019 new file mode 100644 index 0000000000..0d78ed4e0b --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_main_gfs.f_10152019 @@ -0,0 +1,25617 @@ + program trakmain +c +c$$$ MAIN PROGRAM DOCUMENTATION BLOCK +c +c Main Program: GETTRK Track model vortices +C PRGMMR: MARCHOK ORG: NP22 DATE: 2002-05-20 +c +c ABSTRACT: This program tracks the average of the max or min +c of several parameters in the vicinity of an input +c first guess (lat,lon) position of a vortex in order to give +c forecast position estimates for that vortex for a given numerical +c model. For the levels 700 & 850 mb, the tracked parameters are: +c Relative vorticity (max), wind magnitude (min), and geopotential +c height (min). Also tracked is the min in the MSLP. So many +c parameters are tracked in order to provide more accurate position +c estimates for weaker storms, which often have poorly defined +c structures/centers. Currently, the system is set up to be able +c to process GRIB input data files from the GFS, MRF, UKMET, GDAS, +c ECMWF, NGM, NAM and FNMOC/NAVGEM models. Two 1-line files +c are output from this program, both containing the forecast fix +c positions that the tracker has obtained. One of these output +c files contains the positions at every 12 hours from forecast +c hour 0 to the end of the forecast. The other file is in ATCF +c format, which is the particular format needed by the Tropical +c Prediction Center, and provides the positions at forecast hours +c 12, 24, 36, 48 and 72, plus the maximum wind near the storm center +c at each of those forecast hours. +c +c Program history log: +c 98-03-16 Marchok - Original operational version. +c 98-07-15 Marchok - Added code to calculate radii of gale-, storm-, +c and hurricane-force winds in each quadrant. +c 99-04-01 Marchok - Added code to be able to read in 4-digit years +c off of the TC Vitals records. +c Added code, including subroutine is_it_a_storm, +c to make a better determination of whether or +c not the center that was found at each time is +c the center of a storm, and not just a passing +c vort max, etc. +c 99-06-15 Marchok - Fixed a bug in calcdist that was triggered by a +c rounding error sending a number just above 1 +c into ACOS to get the distance between 2 +c identical points (which, obviously, is 0). +c 00-06-20 Marchok - Added GDAS option for vortex relocation work. +c Changed nhalf from 3 to 5. Relaxed the +c requirements for pthresh and vthresh. +c 00-11-30 Marchok - Added ability to handle GFDL and NCEP Ensemble +c model data. Extended time range to be able to +c handle 5-day capability. Forecast hours are +c now input via a namelist (easiest way to account +c for NAM, GFS and GFDL having different forecast +c lengths at 00/12z and 06/18z). Model ID's are +c now input via a namelist (makes it easier, for +c example, to run for many different ensemble +c members). Added new output, the atcfunix +c format, needed for 5-day forecasts. +c 01-08-24 Marchok Fixed a bug in rvcal and getgridinfo. When a +c grid that was south-->north is flipped in +c conv1d2d_real to be north-->south, the scanning +c mode flag remains 64 and what we would consider +c the max and min latitudes are reversed, so I +c added code to correct this in both routines. +c 02-05-20 Marchok Weakened the mslp gradient threshold and v850 +c threshold in is_it_a_storm to cut down on the +c number of dropped storms. +c 03-03-18 Marchok Fixed a bug in get_ij_bounds that was allowing +c a cos(90) and cos(-90), which then led to a +c divide by zero. +c 05-08-01 Marchok Updated to allow tracking of ECMWF hi-res, ECMWF +c ensemble, CMC hi-res, CMC ensemble, NCEP +c ensemble. +c 06-11-07 Marchok Updated to locate, and report to the atcfunix +c file, the value of the gridpoint minimum value +c of mslp. Previously, the barnes-averaged +c value had been used. +c 08-01-10 Marchok Changed the storm ID for genesis tracking so +c that the ID includes info +c on storm detection location & time. Added +c algorithms for Hart's cyclone phase space. +c Added new output fields to the atcfunix +c records, actually creating a modified atcfunix +c record, to include things such as the mean & +c max values of zeta850 & zeta700 centered on +c the storm, the speed & direction of storm +c translation, and the Hart CPS parameters. +c 10-01-07 Marchok - input grib lead time can be hrs or minutes +c - added code for warm core check +c - added code to detect genesis +c - added code to report on sfc wind structure +c - added buffer ("grid_buffer") to avoid fixing +c center to boundaries on regional grids +c - modified rvcal to report missing zeta values +c as background coriolis instead of -999, since +c the -999 was messing up center-fixing +c - added 10-m wind and sfc zeta as center-fixing +c parms. +c +c 10-05-25 Slocum Add verbose feature to code +c 0 = Not terminal output, 1 = error messages only +c 2 = all output +c +c 10-05-26 Marchok - added flags and code to check the temporal +c consistency of the mslp closed contour and +c Vt850 checks for tcgen and midlat cases. +c +c 13-04-01 Marchok Added code to upgrade the wind radii diagnosid. +c Hurricane Sandy exposed an issue with the +c tracker for large storms. The code was modified +c to use an iterative technique that can +c diagnose radii for large storms but still +c accurately diagnost radii for small storms. See +c subroutine getradii for more details. +c +c 15-11-01 Marchok Replaced the routine which tracks the wind +c minimum at the center of a storm, as that +c routine proved troublesome with very hi-res +c grids (0.02-deg) from HWRF for very small +c storms. This has been replaced with a routine +c that looks for "wind circulation difference", +c whereby the center for this parm is located at +c the spot where the tangential wind circulation +c minus the wind magnitude at the candidate +c center position is maximized. ALSO: Added in +c tracking of thickness as an additional +c tracked parm. ALSO: Added a separate verbose +c flag for only the GRIB2 read diagnostics, which +c can be voluminous. +c +c 16-09-01 Marchok Added in the ability to read in NetCDF files. +c As with GRIB data, the NetCDF data must be on +c a lat/lon grid. +c +c 17-08-31 Marchok Added a logical bitmap capability for NetCDF +c files to prevent the accessing of missing data. +c Also modified the code to permit more accurate +c reporting of the grid point value of the +c minimum SLP for reporting to the atcfunix file. +c Finally, fixed a bug (reported by JTWC) whereby +c radii were being reported for thresholds that +c were in exceedance of the tracker-diagnosed +c Vmax (e.g., 34-kt radii for a storm with +c Vmax = 25 kts). +c +c Input files: +c unit 11 Unblocked GRIB1 file containing model data +c unit 12 Text file containing TC Vitals card for current time +c unit 31 Unblocked GRIB index file +c +c Output files: +c unit 61 Output file with forecast positions every 12h from +c vt=00h to the end of the forecast +c unit 62 Output file in ATCF format, with forecast positions +c at vt = 12, 24, 36, 48 and 72h, plus wind speeds. +c unit 63 Output file with forecast wind radii for 34, 50 and +c 64 knot thresholds in each quadrant of each storm. +c +c Subprograms called: +c read_nlists Read input namelists for input date & storm number info +c read_tcv_card Read TC vitals file to get initial storm position +c getgridinfo Read GRIB file to get basic grid information +c tracker Begin main part of tracking algorithm +c +c Attributes: +c Language: Standard Fortran_90 +c +c$$$ +c +c------- +c +c LOCAL: +c +c ifhours: Integer array holding numerical forecast times for +c the input model (99 = no more times available). +c These values are read in via a namelist. +c Model numbers used: (1) GFS, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) NAM, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble (13) SREF +c Ensemble, (14) NCEP Ensemble (from ensstat mean +c fields), (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) Ensemble RELOCATION (21) UKMET hi-res (NHC) +c (23) FNMOC Ensemble +c stormswitch: This switch tells how to handle each storm in +c the TCV file: +c 1 = process this storm for this forecast hour. +c 2 = Storm was requested to be tracked, but either +c the storm went off the grid (regional models), +c the storm dissipated, or the program was +c unable to track it. +c 3 = Storm was NOT requested to be tracked at all. +c storm: An array of type tcvcard. Each member of storm +c contains a separate TC Vitals card. +c maxstorm: Maximum number of storms the system is set up to +c handle at any 1 time. +c slonfg,slatfg: Holds first guess positions for storms. The +c very first, first guess position is read from the +c TC vitals card. (maxstorm,maxtime) +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms) +c + USE def_vitals; USE inparms; USE set_max_parms; USE level_parms + USE trig_vals; USE atcf; USE trkrparms; USE verbose_output + USE netcdf_parms +c + implicit none +c + logical(1) file_open + integer date_time(8) + character (len=10) big_ben(3) + character :: ncfile*180,ncfile_has_hour0*1 + integer itret,iggret,iicret,igcret,iret,ifhmax,maxstorm,numtcv + integer iocret,enable_timing,ncfile_id,ncfile_tmax,irnhret + integer, parameter :: lugb=11,lugi=31,lucard=12,lgvcard=14,lout=51 +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + +c -------------------------------------------------------- + + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: beginning ... ',i2.2,':',i2.2,':',i2.2) + + call w3tagb('GETTRK ',1999,0104,0058,'NP22 ') + + pi = 4. * atan(1.) ! Both pi and dtr were declared in module + dtr = pi/180.0 ! trig_vals, but were not yet defined. + ncfile_has_hour0 = 'n' ! Default value; set in read_netcdf_hours +c + call read_nlists (inp,trkrinfo,netcdfinfo) + enable_timing=trkrinfo%enable_timing + + call read_fhours (ifhmax) + + call read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_tcv_card, num vitals = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_tcv_card, rc= ',iret + endif + goto 890 + endif + + call read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) + + if (iret == 0) then + if ( verb .ge. 3 ) then + print *,'After read_gen_vitals, total number of vitals (both' + & ,' TC and non-TC) now = ',numtcv + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in read_gen_vitals, rc= ' + & ,iret + endif + goto 890 + endif + + if (inp%file_seq == 'onebig') then + if (trkrinfo%inp_data_type == 'netcdf') then + ncfile = netcdfinfo%netcdf_filename + print *,' ' + print *,'before open_ncfile call, ncfile= ',ncfile + call open_ncfile (ncfile,ncfile_id) + print *,'after open_ncfile call, ncfile_id= ',ncfile_id + call read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) + if (irnhret /= 0) then + print *,'(/,a32,a5,i4,/)','!!! ERROR: in read_netcdf_hours,' + & ,' rc= ',irnhret + goto 890 + endif + else + call open_grib_files (inp,lugb,lugi,'dummy','dummy',lout,iret) + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: in open_grib_files, rc= ' + & ,iret + goto 890 + endif + endif + endif + + call tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +890 continue + + igcret=0 + iicret=0 + iocret=0 + + inquire (unit=lugb, opened=file_open) + if (file_open) call baclose(lugb,igcret) + inquire (unit=lugi, opened=file_open) + if (file_open) call baclose(lugi,iicret) + inquire (unit=lout, opened=file_open) + if (file_open) call baclose(lout,iocret) + if ( verb .ge. 3 ) then + print *,'baclose: igcret= ',igcret,' iicret= ',iicret + print *,'baclose: iocret= ',iocret + endif + call w3tage('GETTRK ') +c + stop + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine tracker (inp,maxstorm,numtcv,ifhmax,trkrinfo,ncfile + & ,ncfile_id,netcdfinfo,ncfile_has_hour0 + & ,ncfile_tmax,itret) +c +c ABSTRACT: This subroutine is the core of the program. It contains +c the main loop for looping through all the forecast hours and all +c the storms. Basically, the way it works is that it has an outer +c loop that loops on the forecast hour. At the beginning of this +c loop, the data are read in for all parameters and levels needed +c for tracking. The full regional or global grid is read in. +c If vorticity was not read in (some of the centers do not send us +c vorticity), then vorticity calculations are done on the whole +c grid at both 850 and 700 mb. Then the program goes into the inner +c loop, which loops on storm number (program originally set up to +c handle a max of 15 storms). For each storm, subroutine +c find_maxmin is called for the following parameters: Rel Vort and +c geopotential hgt at 700 & 850 mb, and MSLP. Within find_maxmin, +c a barnes analysis is performed over the guess position of the +c storm to find the max or min value, and then iteratively, the +c grid size is cut in half several times and the barnes analysis +c rerun to refine the positioning of the max or min location. After +c the center positions for these parameters have been obtained, +c subroutine get_uv_center is called to get a center fix for the +c minimum in the wind field, specifically, a minimum in the +c magnitude of the wind speed (vmag). The calculation of the vmag +c minimum is done differently than the calculation for the other +c parameters; for vmag, the grid near the storm center guess +c position is interpolated down to a very fine grid, and then +c find_maxmin is called and a barnes analysis is done on that +c smaller grid. For vmag, there are no further calls made to barnes +c with a smaller grid, since the grid has already been interpolated +c down to a smaller grid. Once all of the parameter center fixes +c have been made, subroutine fixcenter is called to average these +c positions together to get a best guess fix position. Then a check +c is done with a call to subroutine is_it_a_storm to make sure that +c the center that we have found does indeed resemble a tropical +c cyclone. Finally, subroutine get_next_ges is called to make a +c guess position for the next forecast time for this storm. +c +c INPUT: +c inp contains input date and model number information +c maxstorm maximum # of storms to be handled +c numtcv number of storms read off of the tcvitals file +c ifhmax max number of analysis & forecast times to be handled +c trkrinfo derived type that holds/describes various tracker parms +c ncfile if the input data type is netcdf, then this ncfile +c variable contains the name of the netcdf file +c ncfile_id if the input data type is netcdf, then this ncfile_id +c variable contains an integer id assigned to the netcdf +c file from the open_ncfile subroutine +c ncfile_has_hour0 character flag (y|n) that, if the tracker is +c running on NetCDF data, tells if the NetCDF file +c actually contains hour0 data or not (some, like the +c 2016 version of FV3, do not). +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file itself in +c subroutine read_netcdf_fhours. +c +c OUTPUT: +c itret return code from this subroutine +c +c LOCAL PARAMETERS: +c storm contains the tcvitals for the storms +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c maxtime Max number of forecast times program can track +c maxtp Max number of tracked parameters program will track. +c Currently (7/2015), this maxtp is 11, and these 11 are +c listed just a few lines below. +c readflag L Indicates status of read for each of 16 parms: +c 1: 850 mb absolute vorticity +c 2: 700 mb absolute vorticity +c 3: 850 mb u-comp +c 4: 850 mb v-comp +c 5: 700 mb u-comp +c 6: 700 mb v-comp +c 7: 850 mb gp hgt +c 8: 700 mb gp hgt +c 9: MSLP +c 10: near-surface u-comp +c 11: near-surface v-comp +c 12: 500 mb u-comp +c 13: 500 mb v-comp +c 14: Mean temperature, centered at 400 mb +c 15: 500 mb gp hgt +c 16: 200 mb gp hgt +c 17: Land-Sea Mask (for use in tcgen applications, and +c even there, it's optional) +c +c calcparm L indicates which parms to track and which not to. +c Array positions are defined exactly as for clon +c and clat, listed next, except that, in general, when +c flag 3 is set to a value, flag 4 is set to the same +c value as 3, and when flag 5 is set to a value, flag +c 6 is set to the same value as 5. This is because +c 3 & 4 are for the 850 mb winds, and if either u or +c v is missing, we obviously can't calculate the +c magnitude of the wind. The same applies for 5 & 6, +c which are for the 700 mb winds. And also for reference, +c here is a list of all the variables & levels for the +c tracked parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c clon,clat: Holds the coordinates for the center positions for +c all storms at all times for all parameters. +c (max_#_storms, max_fcst_times, max_#_parms). +c For the third position (max_#_parms), here they are: +c 1: Relative vorticity at 850 mb +c 2: Relative vorticity at 700 mb +c 3: Wind circulation difference at 850 mb +c 4: NOT CURRENTLY USED +c 5: Wind circulation difference at 700 mb +c 6: NOT CURRENTLY USED +c 7: Geopotential height at 850 mb +c 8: Geopotential height at 700 mb +c 9: Mean Sea Level Pressure +c 10: Wind circulation difference at 10 m +c 11: Relative vorticity at 10 m +c 12: Lower-level thickness (500-850) +c 13: Upper-level thickness (200-500) +c 14: Deep-Layer thickness (200-850) +c +c xmaxwind Contains maximum near-surface wind near the storm +c center for each storm at each forecast hour. +c stderr Standard deviation of the position "errors" of the +c different parameters for each storm at each time. +c fixlat,fixlon: Contain the final coordinates for each storm at +c each forecast hour. These coordinates are a +c weighted average of all the individual parameter +c positions (hgt, zeta, mslp, vmag). +c cvort_maxmin: Contains the characters 'max' or 'min', and is +c used when calling the find_maxmin routine for the +c relative vorticity (Look for max in NH, min in SH). +c vradius Contains the distance from the storm fix position to +c each of the various near-surface wind threshhold +c distances in each quadrant. +c (3,4) ==> (# of threshholds, # of quadrants) +c See subroutine getradii for further details. +c wfract_cov Fractional coverage (areal coverage) of winds +c exceeding a certain threshold (34, 50, 64 kts) in +c each quadrant. +c (5,5,3) ==> (# of quadrants + 1, # of distance bins, +c # of thresholds). +c The "extra" array size for quadrants (5, instead of 4) +c is there to hold the total (i.e., "whole disc") +c statistics. +c See subroutine get_fract_wind_cov for further details +c +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c isastorm Character array used in the call to is_it_a_storm, +c tells whether the minimum requirement for an MSLP +c gradient was met (isastorm(1)), whether for the midlat +c and tcgen cases if a closed mslp contour was found +c (isastorm(2)), and if a circulation exists at 850 mb +c (isastorm(3)). Can have a value of 'Y' (requirement +c met), 'N' (requirement not met) or 'U' (requirement +c undetermined, due to the fact that no center location +c was found for this parameter). +c maxmini These 2 arrays contain the i and j indeces for the +c maxminj max/min centers that are found using the rough check +c in first_ges_ctr and subsequent routines. Only needed +c for a midlatitude or a genesis run, NOT needed for a +c TC tracker run. +c stormct Integer: keeps and increments a running tab of the +c number of storms that have been tracked at any time +c across all forecast hours. Used only for midlat or +c tcgen runs. +c gridprs This contains the actual value of the minimum pressure +c at a gridpoint. The barnes analysis will return an +c area-averaged value of pressure; this variable will +c contain the actual minimum value at a gridpoint near +c the lat/lon found by the barnes analysis. +c closed_mslp_ctr_flag This flag keeps track of the value of the +c closed contour flag returned from subroutine +c check_closed_contour. +c vt850_flag This flag keeps track of the value of the flag for +c the 850 mb Vt check. +c----- +c + USE def_vitals; USE inparms; USE tracked_parms; USE error_parms + USE set_max_parms; USE level_parms; USE grid_bounds; USE trkrparms + USE contours; USE atcf; USE radii; USE trig_vals; USE phase + USE gen_vitals; USE structure; USE verbose_output + USE waitfor_parms; USE module_waitfor; USE netcdf_parms + USE tracking_parm_prefs +c + implicit none +c + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (cint_stuff) contour_info +c + character, allocatable :: closed_mslp_ctr_flag(:,:)*1 + character, allocatable :: vt850_flag(:,:)*1 + character :: r34_check_okay*1,had_to_try_backup_850_vt_check*1 + character :: need_to_expand_r34(4)*1,ncfile_has_hour0*1 + character*(*), intent(in) :: ncfile + integer :: ncfile_id + integer, parameter :: nreadparms=17 + real, allocatable :: prstemp(:),iwork(:) + integer, parameter :: numdist=14,numquad=4,lout=51 + integer, allocatable :: prsindex(:) + integer imax,jmax,ifh,ist,irf,jj,istmp,ifhtemp,itret,ivpa + integer isiret1,isiret2,isiret3,idum,m,iix,jjx,imode,numtcv + integer iha,isa,iua,iva,iza,maxstorm,ivort,ifix,jfix,issret + integer imoa,imoca,iksa,isda,ileadtime,leadtime_check + integer ioaret,ioaxret,ifgcret,ifmret,igugret,isoiret,icccret + integer igrret,igmwret,iorret,ignret,iovret,icbret,igucret,ita + integer ifilret,ifret,iaret,isret,iotmret,iwa,iisa,sl_counter + integer iicret,igcret,pfcret,igwcret,imbowret,iatret + logical(1), allocatable :: valid_pt(:,:) + logical(1), allocatable :: masked_outc(:,:),masked_out(:,:) + logical(1) readflag(nreadparms),calcparm(maxtp,maxstorm) + logical(1) tracking_previously_known_storms + logical(1) need_to_flip_lats,need_to_flip_lons + logical(1) file_open,first_time_thru_getradii + character cvort_maxmin*3,isastorm(3)*1,ccflag*1,gotten_avg_value*1 + character cmaxmin*3,get_last_isobar_flag*1,wcore_flag*1 + character gfilename*120,ifilename*120,gridmove_status*7 + integer vradius(3,4),igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) + integer maxmini(maxstorm),maxminj(maxstorm),pdf_ct_bin(16) + integer ifcsthour,stormct,prevstormct,kf,istmspd,istmdir,iggret + integer igiret,iuret,jdum,icount,ilonfix,jlatfix,igpret,ifhmax + integer ibeg,jbeg,iend,jend,ix1,ix2,n,ilev,npts,icpsa,igzvret + integer igfwret,ioiret,igisret,iofwret,iowsret,igwsret,igscret + integer pdf_ct_tot,lugb,lugi,iret,icmcf,iccfh,ivt8f + integer waitfor_gfile_status,waitfor_ifile_status,ncfile_tmax + integer wait_max_ifile_wait,ivr,r34_good_ct,itha,ilma,inctcv + integer date_time(8) + character (len=10) big_ben(3) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridprs(maxstorm,maxtime) + real wfract_cov(5,5,3) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real ike(max_ike_cats) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmaxwind(maxstorm,maxtime),xmeanzeta + real stderr(maxstorm,maxtime),xval(maxtp),cps_vals(3) + real gridpoint_maxmin,dist,distnm,xknots,xmaxspeed + real uvgeslon,uvgeslat,xavg,stdv,search_cutoff,re,ri,dx,dy + real xinp_fixlat,xinp_fixlon,degrees,plastbar,rlastbar + real xinterval_fhr,cc_time_sum_tot,cc_time_sum_yes + real rmax,sdp,wdp,paramb,vtl_slope,vtu_slope + real xsfclon,xsfclat,cc_time_pct,radmax,r34_dist_thresh + real prev_latmax,prev_latmin,prev_lonmax,prev_lonmin + real vradius_km,hold_old_contint,tcv_max_wind_ms + real tcv_mslp_pa,r34_from_tcv,roci_from_tcv + real proci_from_tcv,prs_contint_thresh + integer enable_timing,igrct + character(pfc_cmd_len) :: pfc_final +c + prev_latmax = -999.0 + prev_latmin = -999.0 + prev_lonmax = -999.0 + prev_lonmin = -999.0 + enable_timing=trkrinfo%enable_timing + icmcf = 0 + ivt8f = 0 + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + allocate (closed_mslp_ctr_flag(maxstorm,ifhmax),stat=icmcf) + allocate (vt850_flag(maxstorm,ifhmax),stat=ivt8f) + ! Initialize flags to 'u', not 'n'. That way, + ! when we are evaluating its value back over recent past hours, + ! we can distinguish a "no" value from an initialized value of + ! 'u' for which a storm hadn't yet been detected. + closed_mslp_ctr_flag = 'u' + vt850_flag = 'u' + endif + + allocate (prsindex(maxstorm),stat=iisa) + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iisa /= 0 .or. iva /= 0 .or. iwa /= 0 .or. icmcf /= 0 .or. + & ivt8f /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating prsindex,' + print *,'!!! prstemp or iwork array for storms: iisa = ',iisa + print *,'!!! iva= ',iva,' iwa= ',iwa,' icmcf= ',icmcf + print *,'!!! ivt8f= ',ivt8f + endif + itret = 94 + return + endif + + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + clon = 0.0 + clat = 0.0 + stderr = stermn ! initialize stderr to 0.1 (error_parms) + itret = 0 + xmaxwind = 0.0 + stormct = 0 + + ! It is critical to initialize the gridprs array to something + ! greater than normal atmospheric pressures (I've chosen 9999.99 + ! mb). This is so that in the sort on pressure before stormloop, + ! the top of the sorting index array will be filled with pressure + ! values from active storms, while those inactive 9999 storms + ! will fill the bottom of the sorting index array (prsindex). + + gridprs = 999999.0 + fixlon = -999.0 + fixlat = -999.0 + + if (inp%file_seq == 'multi') then + ! Each tau will have a separate file, starting with unit + ! number 200 (GRIB data) and 5200 (GRIB index file) and + ! incrementing upwards from there for each tau. + if (trkrinfo%gribver == 1) then + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + else + lugb = 200 +c lugi = 5200 + lugi = 600 ! 3/2017: w3lib on Jet cannot handle unit #'s >999 + endif + else + ! All lead times are included in one big file. These values + ! for lugb and lugi will remain static for all taus. + lugb = 11 + lugi = 31 + endif + + ifh = 1 + + if ( verb .ge. 3 ) then + print *,'top of tracker, ifh= ',ifh,' ifhmax= ',ifhmax + endif + + ifhloop: do while (ifh <= ifhmax) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------*' + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* New forecast hour: ',i4,':',i2.2) + print *,'*-------------------------------------------*' + endif + + if (inp%file_seq == 'multi') then + + lugb = lugb + 1 + lugi = lugi + 1 + + call get_grib_file_name (ifh,gfilename,ifilename) + + if (use_waitfor == 'y') then + + ! First check for existence of grib file.... + + call waitfor(trim(gfilename),waitfor_gfile_status + & ,wait_min_age,wait_min_size,wait_max_wait + & ,wait_sleeptime) + if (waitfor_gfile_status /= 0) then + print *,' ' + write(6,405) + write(6,406) wait_max_wait,trim(gfilename) + 405 format('ERROR: TIMEOUT from waitfor for GRIB file.') + 406 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + ! Now check for existence of index file. Use a separate + ! max_wait time -- a much shorter one -- since once the + ! grib file is there, the index file should appear within + ! a matter of seconds. Also, the index file is much + ! smaller, so set the wait_min_size accordingly. + + wait_max_ifile_wait = 180 + wait_min_size = 500 + call waitfor(trim(ifilename),waitfor_ifile_status + & ,wait_min_age,wait_min_size,wait_max_ifile_wait + & ,wait_sleeptime) + if (waitfor_ifile_status /= 0) then + print *,' ' + write(6,415) + write(6,416) wait_max_ifile_wait,trim(ifilename) + 415 format('ERROR: TIMEOUT from waitfor for INDEX file.') + 416 format('Waited longer than ',I0,' seconds for "',A,'"') + stop 91 + endif + + endif + + call open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + + if (iret /= 0) then + print '(/,a50,i4,/)','!!! ERROR: from open_grib_files, rc= ' + & ,iret + print *,'!!! Files after hour0 are missing, ' + & ,'exiting normally' + stop 0 + endif + endif + + if (trkrinfo%inp_data_type == 'grib') then + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugb= ',lugb,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is OPEN' + else + print *,'TEST b4 getgridinfo, unit lugi= ',lugi,' is CLOSED' + endif + endif + + !-------------------------------------------------------------- + ! Within this next IF statement, we deal with writing out atcf + ! records for storms for the case in which we have netcdf data, + ! but that netcdf data does not have hour0 data (as of Nov 2016, + ! this is the case for FV3 data). In this case, we write out + ! missing values for the hour0 time, and then we update the + ! guess for next lead time by extrapolating data from TC Vitals. + ! Note in the IF statement itself, "iftotalmins" is the array + ! of *user-requested* lead times, meaning that the user has + ! requested to look at hour0, but the ncfile_has_hour0 flag + ! indicates the hour0 time is not in the NetCDF data. + !-------------------------------------------------------------- + + if (ifh == 1 .and. iftotalmins(ifh) == 0 .and. + & trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') then + + null_netcdf_hour0_storm_loop: do inctcv = 1,numtcv + + call output_atcfunix (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,inctcv + & ,0,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,inctcv + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',inctcv + write (6,431) storm(inctcv)%tcv_storm_id + & ,storm(inctcv)%tcv_storm_name + 431 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + + call advect_tcvitals_from_hour0 (slonfg,slatfg,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) + + if (iatret /= 0) then + fixlon (inctcv,ifh) = -999.0 + fixlat (inctcv,ifh) = -999.0 + stormswitch(inctcv) = 2 + cycle null_netcdf_hour0_storm_loop + endif + + stormswitch(inctcv) = 1 + + enddo null_netcdf_hour0_storm_loop + + ifh = ifh + 1 + cycle ifhloop + + endif + + !-------------------------------------------------------------- + ! Make call to getgridinfo in order to get info on the imax, + ! jmax, as well as the x- and y-increments, and also to see if + ! the grid is correctly oriented for the tracker so that the + ! data go north to south and west to east or if we need to flip + ! either the lats or the lons. + !-------------------------------------------------------------- + + if (trkrinfo%inp_data_type == 'grib') then + call getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) + else + print *,' ' + print *,'!!! ERROR: trkrinfo%inp_data_type NOT VALID ' + print *,'!!! trkrinfo%inp_data_type= ',trkrinfo%inp_data_type + print *,'!!! Should have value of grib or netcdf.' + print *,'!!! EXITING....' + print *,' ' + stop 93 + endif + + if (iggret == 0) then + if ( verb .ge. 1 ) then + print *,'TEST after getgridinfo in sub tracker, ' + & ,'iggret= ',iggret + endif + else + if ( verb .ge. 1 ) then + print '(/,a50,i4,/)','!!! ERROR: in getgridinfo, rc= ' + & ,iggret + endif + stop 95 + endif + + if (inp%modtyp == 'regional' .and. inp%nesttyp == 'moveable') + & then + if (glatmax == prev_latmax .and. glatmin == prev_latmin .and. + & glonmax == prev_lonmax .and. glonmin == prev_lonmin) then + ! The moveable, nested regional grid has not moved since + ! the last lead time. This could be an indication that the + ! model lost the storm and so the grid has not moved to + ! stay with the cyclone center. Set a flag to indicate this. + gridmove_status = 'stopped' + else + gridmove_status = 'moving' + endif + else + gridmove_status = 'notappl' + endif + + prev_latmax = glatmax + prev_latmin = glatmin + prev_lonmax = glonmax + prev_lonmin = glonmin + + gotten_avg_value = 'n' + +c First, allocate the working data arrays.... + + if (allocated(valid_pt)) deallocate (valid_pt) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + + ! Allocate all of the allocatable arrays.... + + allocate (valid_pt(imax,jmax),stat=ivpa) + allocate (zeta(imax,jmax,nlevzeta),stat=iza) + allocate (u(imax,jmax,nlevs),stat=iua) + allocate (v(imax,jmax,nlevs),stat=iva) + allocate (hgt(imax,jmax,nlevhgt),stat=iha) + allocate (slp(imax,jmax),stat=isa) + allocate (tmean(imax,jmax),stat=ita) + allocate (thick(imax,jmax,nlevthick),stat=itha) + allocate (lsmask(imax,jmax),stat=ilma) + allocate (masked_out(imax,jmax),stat=imoa) + allocate (masked_outc(imax,jmax),stat=imoca) + + ita=0 + icpsa=0 + if (phaseflag == 'y') then + if (phasescheme == 'cps' .or. phasescheme == 'both') then + if (allocated(cpshgt)) deallocate (cpshgt) + allocate (cpshgt(imax,jmax,nlevs_cps),stat=icpsa) + endif + endif + + if (iza /= 0 .or. iua /= 0 .or. iha /= 0 .or. ivpa /= 0 .or. + & iva /= 0 .or. isa /= 0 .or. icpsa /= 0 .or. ita /= 0 .or. + & itha /= 0 .or. imoa /= 0 .or. imoca /= 0 .or. ilma /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub tracker allocating arrays.' + print *,'!!! iza = ',iza,' iua= ',iua,' iha= ',iha + print *,'!!! iva = ',iva,' isa= ',isa,' icpsa= ',icpsa + print *,'!!! iksa = ',iksa,' isda= ',isda,' ivpa= ',ivpa + print *,'!!! ita = ',ita,' imoa= ',imoa,' imoca= ',imoca + print *,'!!! itha = ',itha,' ilma= ',ilma + endif + itret = 94 + return + endif + + masked_out = .false. ! Initialize all pts to false at each hr + masked_outc = .false. ! Initialize all pts to false at each hr + + if ( verb .ge. 3 ) then + print *,'in beginning of tracker, imax= ',imax,' jmax= ',jmax + endif + +c Initialize all readflags to NOT FOUND for this forecast time, +c then call subroutine to read data for this forecast time. + + zeta = -9999.0 + u = -9999.0 + hgt = -9999.0 + v = -9999.0 + slp = -9999.0 + tmean = -9999.0 + + readflag = .FALSE. + + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: b4 getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + + if (trkrinfo%inp_data_type == 'grib') then + call getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) + elseif (trkrinfo%inp_data_type == 'netcdf') then + call getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3),date_time) + write (6,32) date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: after getdata ... ',i2.2,':',i2.2,':',i2.2) + endif + +c Count how many parms were successfully read for this fcst time. +c Also, for right now, put the value of readflag into all of the +c calcparms for parameters 3 through 9. Note that in getdata we +c read in 17 parms, but in this next loop we only check the +c readflags up to maxtp (= 14 as of 7/2015). That's because +c parms 12 & 13 are for 500 mb u & v, which are not used for +c tracking, only for calculating the deep layer mean wind for +c the next guess, and parm 14 is the 300-500 mb mean temperature, +c which is used for determining storm phase. Parms 10 & 11 are +c for the near-surface winds, which are used in estimating surface +c winds near the storm, and will now also be used as a +c parameter for position estimates. Finally, parm 17 is the +c land-sea mask, which is not used as a tracking parm. + + idum = 0 + do irf = 1,nreadparms + if (readflag(irf)) idum = idum + 1 + if (irf > 2 .and. irf < 10) then + ! calcparm for parms > 9 is done further below. + do jj=1,maxstorm + calcparm(irf,jj) = readflag(irf) + enddo + endif + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Of ',nreadparms,' readable parms, you read in ',idum + print *,'parms for this fcst hour from the input grib file.' + endif + +c If not enough tracked parms were read in, exit the program.... + + if (idum == 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in subroutine tracker' + print *,'!!! Not enough tracked parms read in from getdata.' + print *,'!!! Check for a problem with the input GRIB file.' + print *,'!!! Model identifier = ',inp%model + print *,'!!! STOPPING EXECUTION FOR THIS MODEL' + endif + itret = 99 + ifhtemp = ifh + do while (ifhtemp <= ifhmax) + do istmp=1,maxstorm + fixlon (istmp,ifhtemp) = -999.0 + fixlat (istmp,ifhtemp) = -999.0 + enddo + ifhtemp = ifhtemp + 1 + enddo +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) + if (ifh == 1) then + ! Per Jim Gross (1/01), if the tracker ran but was unable + ! to get an initial fix (or, in this case, unable to get + ! the data needed to run), write out zeroes for the 00h + ! fixes to indicate that the tracker ran unsuccessfully, + ! but don't write out any subsequent forecast times + ! with zeroes.... + vradius = 0 + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initial value of 'undetermined' + do istmp = 1,maxstorm + if (stormswitch(istmp) /= 3) then + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0,-999.0,inp,istmp + & ,ifcsthour,0.0,0.0,vradius,maxstorm + & ,trkrinfo,-99.0,-99.0,-99.0,cps_vals + & ,wcore_flag,ioaxret) + call output_hfip (-999.0,-999.0,inp,istmp + & ,ifh,0.0,0.0,vradius,-99.0,ioaxret) + endif + enddo + endif + return + endif + +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for z850, z700 and mslp.... + + if (user_wants_to_track_gph850 == 'n' .or. + & user_wants_to_track_gph850 == 'N') then + do jj=1,maxstorm + calcparm(7,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_gph700 == 'n' .or. + & user_wants_to_track_gph700 == 'N') then + do jj=1,maxstorm + calcparm(8,jj) = .FALSE. + enddo + endif + + if (user_wants_to_track_mslp == 'n' .or. + & user_wants_to_track_mslp == 'N') then + do jj=1,maxstorm + calcparm(9,jj) = .FALSE. + enddo + endif + + +c Parameters 1 & 2 are abs vorticity at 850 & 700. If the data +c files had this parm at 850 & 700 (ECMWF & UKMET do NOT), then +c we don't need to re-calculate relative vorticity, we just need +c to subtract out the Coriolis component. If the files did not +c have vorticity, then we need to calculate relative vorticity. +c If we're able to read vorticity or calculate it, then set the +c vorticity calcparms to TRUE for all storms for now. + + vortloop: do ivort=1,2 + + if (ivort == 1) then + if (user_wants_to_track_zeta850 == 'n' .or. + & user_wants_to_track_zeta850 == 'N') then + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (ivort == 2) then + if (user_wants_to_track_zeta700 == 'n' .or. + & user_wants_to_track_zeta700 == 'N') then + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + cycle vortloop + endif + endif + + if (readflag(ivort)) then + + call subtract_cor (imax,jmax,dy,ivort) + + do jj=1,maxstorm + calcparm(ivort,jj) = .TRUE. + enddo + else + if (ivort == 1) then + if (readflag(3) .and. readflag(4)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(1,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(1,jj) = .FALSE. + enddo + endif + else + if (readflag(5) .and. readflag(6)) then + call rvcal (imax,jmax,dx,dy,ivort,valid_pt) + do jj=1,maxstorm + calcparm(2,jj) = .TRUE. + enddo + else + do jj=1,maxstorm + calcparm(2,jj) = .FALSE. + enddo + endif + endif + endif + + enddo vortloop + + +c Check the flags that were read in from the namelist for +c determining which parameters the user wants to track. +c Here, check for user preferences for the wind circulation +c difference at 850 & 700... + + if (readflag(3) .and. readflag(4)) then + if (user_wants_to_track_wcirc850 == 'n' .or. + & user_wants_to_track_wcirc850 == 'N') then + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(3,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(3,jj) = .FALSE. + enddo + endif + + if (readflag(5) .and. readflag(6)) then + if (user_wants_to_track_wcirc700 == 'n' .or. + & user_wants_to_track_wcirc700 == 'N') then + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(5,jj) = .TRUE. + enddo + endif + else + do jj=1,maxstorm + calcparm(5,jj) = .FALSE. + enddo + endif + + +c Compute the sfc vorticity if sfc_u and sfc_v have been read in. + + if (readflag(10) .and. readflag(11)) then + + if (user_wants_to_track_wcircsfc == 'n' .or. + & user_wants_to_track_wcircsfc == 'N') then + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + enddo + else + do jj=1,maxstorm + calcparm(10,jj) = .TRUE. + enddo + endif + + if (user_wants_to_track_zetasfc == 'n' .or. + & user_wants_to_track_zetasfc == 'N') then + do jj=1,maxstorm + calcparm(11,jj) = .FALSE. + enddo + else + ! The 3 in the next call to rvcal is to indicate the 3rd + ! level for the zeta array, which is for the surface (or + ! 10m) data. + call rvcal (imax,jmax,dx,dy,3,valid_pt) + do jj=1,maxstorm + calcparm(11,jj) = .TRUE. + enddo + endif + + else + do jj=1,maxstorm + calcparm(10,jj) = .FALSE. + calcparm(11,jj) = .FALSE. + enddo + endif + + +c Compute the thicknesses for 200-850, 200-500 and 500-850 mb +c if the gp hgt fields have been read in for 200, 500 and 850. + + if (readflag(7) .and. readflag(15) .and. readflag(16)) then + + call thickness_calc (imax,jmax,valid_pt) + + do jj=1,maxstorm + + if (user_wants_to_track_thick500850 == 'n' .or. + & user_wants_to_track_thick500850 == 'N') then + calcparm(12,jj) = .FALSE. + else + calcparm(12,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200500 == 'n' .or. + & user_wants_to_track_thick200500 == 'N') then + calcparm(13,jj) = .FALSE. + else + calcparm(13,jj) = .TRUE. + endif + + if (user_wants_to_track_thick200850 == 'n' .or. + & user_wants_to_track_thick200850 == 'N') then + calcparm(14,jj) = .FALSE. + else + calcparm(14,jj) = .TRUE. + endif + + enddo + else + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Thickness will not be tracked since at least' + print *,'one of the gp height fields was not read in.' + print *,' readflag(7) -- 850 mb ---> ',readflag(7) + print *,' readflag(15) -- 500 mb ---> ',readflag(15) + print *,' readflag(16) -- 200 mb ---> ',readflag(16) + print *,' ' + endif + do jj=1,maxstorm + calcparm(12,jj) = .FALSE. + calcparm(13,jj) = .FALSE. + calcparm(14,jj) = .FALSE. + enddo + endif + +c --------------------------------------------------------------- +c Now call find_maxmin for the variables zeta, hgt and slp. Only +c process those storms for which stormswitch is set to 1. If a +c storm is selected to be processed, we still have to check the +c calcparm for each parameter, to make sure that the particular +c parm exists at that level and is able to be processed. +c +c The following commented-out data statements are just included +c as a reference so you can see the array positioning of the +c different parameters and levels that are read in: +c +c data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7/ +c data iglevtyp /100,100,100,100,100,100,100,100,102,sfc,sfc +c ,100,100,100,100,100/ +c data iglev /850,700,850,850,700,700,850,700,0,sfc,sfc +c ,500,500,400,500,200/ +c +c And also for reference, here are the variables / levels for +c the *tracked* parameters (i.e., the "calcparm" elements): +c +c 1: 850 mb relative vorticity +c 2: 700 mb relative vorticity +c 3: 850 mb wind circulation difference +c 4: NOT USED +c 5: 700 mb wind circulation differenc +c 6: NOT USED +c 7: 850 mb geopotential height +c 8: 700 mb geopotential height +c 9: MSLP +c 10: 10-m wind circulation difference +c 11: 10-m relative vorticity +c 12: 500-850 mb thickness (lower level) +c 13: 200-500 mb thickness (upper level) +c 14: 200-850 mb thickness (deep-layer) +c +c NOTE: For mid-latitude cases, we will track ONLY mslp, which +c is why we set all the other calcparms to 'false' just below. + + if (trkrinfo%type == 'midlat') then + do m = 1,maxstorm + calcparm(1,m) = .false. + calcparm(2,m) = .false. + calcparm(3,m) = .false. + calcparm(4,m) = .false. + calcparm(5,m) = .false. + calcparm(6,m) = .false. + calcparm(7,m) = .false. + calcparm(8,m) = .false. + calcparm(10,m) = .false. + calcparm(11,m) = .false. + calcparm(12,m) = .false. + calcparm(13,m) = .false. + calcparm(14,m) = .false. + enddo + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + call sort_storms_by_pressure (gridprs,ifh,maxstorm,prsindex + & ,issret) + if ( (ifh == 1) .or. + & (ifh == 2 .and. trkrinfo%inp_data_type == 'netcdf' .and. + & ncfile_has_hour0 == 'n') ) then + stormct = numtcv + endif + endif + + prevstormct = stormct + tracking_previously_known_storms = .true. + + stormloop: do sl_counter = 1,maxstorm + + cps_vals(1) = -9999.0 + cps_vals(2) = -9999.0 + cps_vals(3) = -9999.0 + wcore_flag = 'u' ! 'u' = initialized value of 'undetermined' + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + ist = prsindex(sl_counter) + else + ist = sl_counter + endif + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') + & then + + if (ist == (prevstormct + 1)) then + + ! For the mid-latitude and tropical cyclogenesis cases, we + ! need to scan the mslp field to find new storms. If we + ! are at this point inside the if statement in stormloop, + ! then that means we have looped through and attempted to + ! track all storms that have already been found up to this + ! point in the forecast, and we need to scan the field for + ! any new storms at this forecast hour. If this is for + ! forecast hour = 0, then right off the bat we may be + ! scanning the field (if there were no tcvitals records + ! read in for this forecast), since ist = 1 and + ! (prevstormct + 1) = 0 + 1 = 1. All that the call just + ! below to first_ges_center does is return a rough idea + ! of the location of new lows; more specific locations are + ! obtained through the barnes analysis tracking algorithm + ! further below. + + if (readflag(9)) then + if (ifh > 1) then + ! We need the use of 2 different masks. One + ! (masked_out) is to be used when looking for new lows, + ! so that after we find a new low, we mask out the + ! surrounding area so we don't find it on a subsequent + ! search for this forecast hour. The other + ! (masked_outc) is used in the routine to check for a + ! closed contour. If checking for a closed contour + ! at, say 70W/25N, this and surrounding points may have + ! already been masked out in first_ges_center, so "N" + ! would misleadingly/incorrectly be returned from + ! check_closed_contour, so that is why we need 2 masks. + ! But now after the first forecast hour (t=0), the way + ! we have this set up is that we track previously known + ! storms first, and once we're done with them, we + ! search for new storms at that same forecast hour. + ! But when looking for new storms, we need to know the + ! positions of the previously tracked storms at this + ! current forecast hour, so we copy the masked_outc + ! array to masked_out in this case.... + + masked_out = masked_outc + + endif + call first_ges_center (imax,jmax,dx,dy,'mslp',slp + & ,'min',trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) + tracking_previously_known_storms = .false. + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In subroutine tracker, readflag' + print *,'!!! for mslp indicates that the mslp data' + print *,'!!! is not available for this forecast ' + print *,'!!! hour, and it is needed for a "midlat"' + print *,'!!! or "tcgen" run of the tracker. ' + print *,'!!! We will exit....' + print *,'!!! readflag(9) = ',readflag(9) + print *,'!!! ifh= ',ifh + print *,' ' + endif + itret = 98 + return + endif + endif + endif + + xval = 0.0 ! initialize entire xval array to 0 + isastorm = 'U' ! re-initialize flag for each time, each storm + + select case (stormswitch(ist)) + + case (1) + + vradius = 0 + + if ( verb .ge. 2 ) then + print *,' ---------------------------------------------' + print *,' | *** TOP OF STORM LOOP *** ' + print *,' | Beginning of storm loop in tracker for' + print *,' | Storm number ',ist + write (6,418) ifhours(ifh),ifclockmins(ifh) + 418 format (1x,' | Forecast hour: ',i4,':',i2.2) + print *,' | Storm name = ',storm(ist)%tcv_storm_name + print *,' | Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,' ---------------------------------------------' + print *,' ' + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3 + & ,'_',i3.3,a1,'_',i4.4,a1,'_',a3) + + endif +c First, make sure storm is within the grid boundaries... + + call check_bounds (slonfg(ist,ifh),slatfg(ist,ifh),ist,ifh + & ,trkrinfo,icbret) + if (icbret == 95) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + + if (slatfg(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + if (calcparm(1,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,1),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(1,ist),clon(ist,ifh,1),clat(ist,ifh,1) + & ,xval(1),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(2,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for zeta at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,2),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(2,ist),clon(ist,ifh,2),clat(ist,ifh,2) + & ,xval(2),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(7,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 850 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,1),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(7,ist) + & ,clon(ist,ifh,7),clat(ist,ifh,7),xval(7) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(8,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for hgt at 700 mb' + endif + + call find_maxmin (imax,jmax,dx,dy,'hgt' + & ,hgt(1,1,2),'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(8,ist) + & ,clon(ist,ifh,8),clat(ist,ifh,8),xval(8) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(9,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for mslp' + endif + + call find_maxmin (imax,jmax,dx,dy,'slp' + & ,slp,'min',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(9,ist) + & ,clon(ist,ifh,9),clat(ist,ifh,9),xval(9) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(11,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for sfc zeta' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,3),cvort_maxmin,ist,slonfg(ist,ifh) + & ,slatfg(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,calcparm(11,ist),clon(ist,ifh,11),clat(ist,ifh,11) + & ,xval(11),glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 + + if (calcparm(12,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 500-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,1),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(12,ist) + & ,clon(ist,ifh,12),clat(ist,ifh,12),xval(12) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(13,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-500 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,2),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(13,ist) + & ,clon(ist,ifh,13),clat(ist,ifh,13),xval(13) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + if (calcparm(14,ist)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling find_maxmin for thickness in' + print *,'the 200-850 mb layer.' + endif + + call find_maxmin (imax,jmax,dx,dy,'thick' + & ,thick(1,1,3),'max',ist,slonfg(ist,ifh),slatfg(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,calcparm(14,ist) + & ,clon(ist,ifh,14),clat(ist,ifh,14),xval(14) + & ,glatmax,glatmin,glonmax,glonmin + & ,inp%modtyp,ifmret) + if (ifmret /= 0) then ! Out of regional grid bounds + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + +c Now get centers for wind circulation at 700 & 850 mb and +c at 10m. First, get a modified guess lat/lon position for +c wind circulation. Do this because we will be searching +c for this wind circulation center over a smaller area and +c so it's more crucial to have a better first guess position. +c This modified guess position will be an average of the first +c guess position for this time and the fix positions for this +c time from some of the other parameters. + + if (slatfg(ist,ifh) >= 0.0) then + cmaxmin = 'max' + else + cmaxmin = 'min' + endif + + if (calcparm(3,ist) .and. calcparm(4,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 850 mb ' + endif + + print *,' ' + print *,'Before first call to get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' inp%modtyp= ',inp%modtyp + print *,' cmaxmin= ',cmaxmin + print *,' nlev850= ',nlev850 + print *,' u(1,1,nlev850)= ',u(1,1,nlev850) + print *,' u(imax,jmax,nlev850)= ',u(imax,jmax,nlev850) + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' calcparm(3,ist)= ',calcparm(3,ist) + print *,' clon(ist,ifh,3)= ',clon(ist,ifh,3) + print *,' clat(ist,ifh,3)= ',clat(ist,ifh,3) + print *,' xval(3)= ',xval(3) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,141) date_time(5),date_time(6),date_time(7) + 141 format (1x,'TIMING: Before GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,850,valid_pt,calcparm(3,ist) + & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,142) date_time(5),date_time(6),date_time(7) + 142 format (1x,'TIMING: After GWC 850 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,850,valid_pt,calcparm(3,ist) +c & ,clon(ist,ifh,3),clat(ist,ifh,3),xval(3),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + endif + else + calcparm(3,ist) = .FALSE. + calcparm(4,ist) = .FALSE. + clon(ist,ifh,3) = 0.0 + clat(ist,ifh,3) = 0.0 + endif + endif + + if (calcparm(5,ist).and. calcparm(6,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for 700 mb ' + endif + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,143) date_time(5),date_time(6),date_time(7) + 143 format (1x,'TIMING: Before GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,700,valid_pt,calcparm(5,ist) + & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo + & ,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,144) date_time(5),date_time(6),date_time(7) + 144 format (1x,'TIMING: After GWC 700 ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,700,valid_pt,calcparm(5,ist) +c & ,clon(ist,ifh,5),clat(ist,ifh,5),xval(5),trkrinfo +c & ,igucret) + + if (igwcret /= 0) then + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + endif + else + calcparm(5,ist) = .FALSE. + calcparm(6,ist) = .FALSE. + clon(ist,ifh,5) = 0.0 + clat(ist,ifh,5) = 0.0 + endif + endif + + if (calcparm(10,ist)) then + call get_uv_guess (slonfg(ist,ifh),slatfg(ist,ifh) + & ,clon,clat,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) + if (igugret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,' --- --- ---' + print *,'Now calling get_wind_circulation for the' + print *,'surface (10m) level' + endif + + ! NOTE: The 1020 in the call here is just a number/code + ! to indicate to the subroutine to process sfc winds. + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,145) date_time(5),date_time(6),date_time(7) + 145 format (1x,'TIMING: Before GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + + call get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy,ist,1020,valid_pt,calcparm(10,ist) + & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) + & ,trkrinfo,inp%modtyp,cmaxmin,igwcret) + + if (enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,146) date_time(5),date_time(6),date_time(7) + 146 format (1x,'TIMING: After GWC Sfc ... ',i2.2,':',i2.2 + & ,':',i2.2) + endif + +c call get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy +c & ,ist,1020,valid_pt,calcparm(10,ist) +c & ,clon(ist,ifh,10),clat(ist,ifh,10),xval(10) +c & ,trkrinfo,igucret) + + if (igwcret /= 0) then + calcparm(10,ist) = .FALSE. + endif + else + calcparm(10,ist) = .FALSE. + clon(ist,ifh,10) = 0.0 + clat(ist,ifh,10) = 0.0 + endif + endif + +c ------------------------------------------------------ +c All of the parameter center fixes have been done. Now +c average those positions together to get the best guess +c fix position. If a center fix is able to be made, then +c call subroutine get_max_wind to get the maximum near- +c surface wind near the center, and then call get_next_ges +c to get a guess position for the next forecast hour. + + if (stormswitch(ist) == 1) then + + call fixcenter (clon,clat,ist,ifh,calcparm + & ,slonfg(ist,ifh),slatfg(ist,ifh),inp + & ,stderr,fixlon,fixlat,xval,maxstorm,ifret) + + if (ifret == 0) then + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'regional')then + if (fixlon(ist,ifh) > (trkrinfo%eastbd + 7.0) .or. + & fixlon(ist,ifh) < (trkrinfo%westbd - 7.0) .or. + & fixlat(ist,ifh) > (trkrinfo%northbd + 7.0) .or. + & fixlat(ist,ifh) < (trkrinfo%southbd - 7.0)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! will NOT be made for this time due' + print *,'!!! the storm being more than 7 degrees' + print *,'!!! outside the user-specified lat/lon' + print *,'!!! bounds for this run. We will stop' + print *,'!!! tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + 432 format (1x,'!!! Fcst hr = ',i4,':',i2.2) + print *,'!!! fixlat= ',fixlat(ist,ifh) + print *,'!!! fixlon= ',fixlon(ist,ifh) + print *,'!!! User East Bound = ',trkrinfo%eastbd + print *,'!!! User West Bound = ',trkrinfo%westbd + print *,'!!! User North Bound = ',trkrinfo%northbd + print *,'!!! User South Bound = ',trkrinfo%southbd + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + endif + cycle stormloop + endif + endif + else + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + +c Just because we've found a center doesn't mean there is +c actually a storm there. I noticed in the first year that +c for some decaying or just weak storms, the tracker would +c identify a center to follow, but it may have only been +c a weak trough passing by, or something else that's not +c our storm. This next subroutine checks to see that the +c surface pressure gradient and/or tangential winds at +c 850 mb resemble a storm. It is called twice; the first +c time for MSLP, the 2nd time for 850 mb winds. We will +c apply these storm-checking criteria if either the mslp +c or v850 check come back negative. Remember, there +c is the possibility that centers could not be found for +c 1 or both of these parameters, in which case the isastorm +c flag will have a value of 'U', for "undetermined". + + isiret1 = 0; isiret2 = 0; isiret3 = 0 + + print *,' ttest, ifret= ',ifret + + if (ifret == 0) then + + print *,' ttest, calcparm(9,ist)= ',calcparm(9,ist) + + if (calcparm(9,ist)) then + + ! Do a check of the mslp gradient.... + + print *,' ttest, in IF part: ' + print *,' clon(ist,ifh,9)= ',clon(ist,ifh,9) + print *,' clat(ist,ifh,9)= ',clat(ist,ifh,9) + print *,' xval(9)= ',xval(9) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,clon(ist,ifh,9),clat(ist,ifh,9) + & ,xval(9),trkrinfo,isastorm(1),isiret1) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for mslp (e.g., + ! maybe the mslp fix was too far away from the + ! guess?), then this check isn't performed. We are + ! changing this so that the mslp gradient check will + ! still be performed, but using the mean fixlat and + ! fixlon positions as the center. Still, we first + ! need to check to see if mslp was even read in. If + ! it wasn't, then we are just out of luck. + + print *,' ttest, in ELSE part: ' + + if (trkrinfo%use_backup_mslp_grad_check == 'y' .or. + & trkrinfo%use_backup_mslp_grad_check == 'Y') then + + print *,' ttest ELSE, readflag(9)= ',readflag(9) + + if (readflag(9)) then + + print *,'ttest ELSE A, ist= ',ist,' ifh= ',ifh + print *,'ttest ELSE A, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE A, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,9999.0,ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + + print *,'ttest ELSE B, ifilret= ',ifilret + + if (ifilret == 0) then + + print *,'ttest ELSE B, ifilret= ',ifilret + print *,'ttest ELSE B, fixlon(ist,ifh)= ' + & ,fixlon(ist,ifh) + print *,'ttest ELSE B, fixlat(ist,ifh)= ' + & ,fixlat(ist,ifh) + + call is_it_a_storm (imax,jmax,dx,dy,'slp',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,gridpoint_maxmin,trkrinfo,isastorm(1) + & ,isiret1) + + if (isiret1 == 0) then + ! Even though calcparm(9) is FALSE and mslp + ! will not be used for center-fixing + ! purposes, we need to fill the clat and clon + ! arrays just a few lines below so that + ! calls to fix_latlon_to_ij below do not + ! get screwed up. So, into the clat and clon + ! arrays we put the mean fixlat and fixlon + ! positions for this lead time. + clat(ist,ifh,9) = fixlat(ist,ifh) + clon(ist,ifh,9) = fixlon(ist,ifh) + xval(9) = gridpoint_maxmin + endif + + endif + + endif + + endif + + endif + + ! If we have found a valid mslp gradient, then make + ! a call to fix_latlon_to_ij to (1) get the actual + ! gridpoint value of the mslp (the value previously + ! stored in xval(9) is an area-averaged value coming + ! from the barnes analysis), and (2) to get the + ! (i,j) indices for this gridpoint to be used in the + ! call to check_closed_contour below. + ! + ! NOTE: If a mslp fix was not made, or if the mslp + ! "isastorm" flag comes back as no, we make the same + ! call to fix_latlon_to_ij, but we use the mean fix + ! position as our input to search around, and then + ! basically we just find the lowest mslp near that + ! mean fix position. There is a check on the value + ! of xinp_fixlat and xinp_fixlon to make sure that + ! they contain valid values and not just the + ! initialized -999 values. + + if (isiret1 == 0 .and. isastorm(1) == 'Y') then + xinp_fixlat = clat(ist,ifh,9) + xinp_fixlon = clon(ist,ifh,9) + if (verb >= 3) then + print *,' ttest at location C IF....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + else + xinp_fixlat = fixlat(ist,ifh) + xinp_fixlon = fixlon(ist,ifh) + if (verb >= 3) then + print *,' ttest at location C ELSE....' + print *,' xinp_fixlat= ',xinp_fixlat + print *,' xinp_fixlon= ',xinp_fixlon + endif + endif + + if (xinp_fixlat > -99.0 .and. xinp_fixlon > -990.0) + & then + if (verb >= 3) then + print *,' ttest at location D' + endif + call fix_latlon_to_ij (imax,jmax,dx,dy,slp,'min' + & ,valid_pt,xinp_fixlon,xinp_fixlat + & ,xval(9),ifix,jfix,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (verb >= 3) then + print *,' ttest at location E, ifilret= ',ifilret + endif + if (ifilret == 0) then + gridprs(ist,ifh) = gridpoint_maxmin + else + ! Search went out of regional grid bounds.... + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + endif + + print *,' ttest at location F' + + ! For a "tracker" case, check to see if the user has + ! requested to compute and write out the ROCI. If + ! so, then we make a call to check_closed_contour, + ! being sure to specify 999 as the number of levels + ! to check.... + + if (isiret1 == 0 .and. isastorm(1) == 'Y' .and. + & trkrinfo%type == 'tracker') then + + if (trkrinfo%want_oci) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + if (xval(9) < 1100.0) then + ! Pressure units are in mb... + prs_contint_thresh = 4.0 + elseif (xval(9) >80000.0) then + ! Pressure units are in Pa... + prs_contint_thresh = 400.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' tracker. The mslp value' + print *,' (xval(9)) is not in range.' + print *,' before call to' + print *,' check_closed_contour.' + print *,' xval(9) = ',xval(9) + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + if (trkrinfo%contint < prs_contint_thresh) then + hold_old_contint = trkrinfo%contint + trkrinfo%contint = prs_contint_thresh + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before going into routine to diagnose' + print *,'the ROCI for a tracker run, the ' + print *,'requested contour interval is being ' + print *,'adjusted up (coarser) to avoid having' + print *,'the contour check routine break and ' + print *,'return an invalid value.' + print *,'User-requested contint value (Pa) = ' + & ,hold_old_contint + print *,'Modified contint value (Pa) = ' + & ,trkrinfo%contint + endif + endif + + masked_outc = .false. + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ' + & ,rlastbar,' nm' + print *,' ' + endif + + endif + + endif + + ! For the midlat & tcgen cases, do a check to see if + ! there is a closed mslp contour. The ifix and jfix + ! values passed into check_closed_contour are the + ! values for the (i,j) at the gridpoint minimum, + ! which was obtained just above from the call to + ! fix_latlon_to_ij. + ! UPDATE 7/12/2016 tpm: A change was made to fix a + ! hole in the logic. Previously, for a genesis run + ! (type = midlat or tcgen), if a fix was not made + ! for mslp, then the isastorm(1) flag would not be + ! 'Y', and so the call to check_closed_contour in + ! the following IF statement would not be made, and + ! that would prevent the mask from getting updated + ! for this particular storm, allowing the same storm + ! to be detected when the scan for new storms takes + ! place at this lead time (i.e., after all previously- + ! known storms from the last lead time have been + ! tracked). As a fix, if that isastorm(1) flag is not + ! 'Y', then we call a new subroutine which updates the + ! mask based on the circulation at 850 mb. + + if (isastorm(1) == 'Y' .and. isiret1 == 0 .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + endif + + if (contour_info%numcont == 0) then + contour_info%numcont = maxconts + endif + + get_last_isobar_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,slp + & ,valid_pt,masked_outc,ccflag,'min',trkrinfo + & ,999,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to check_closed_contour, ' + print *,'ifix= ',ifix,' jfix= ',jfix + print *,'longitude= ',xinp_fixlon,'E (' + & ,360-xinp_fixlon,'W)' + print *,'latitude= ',xinp_fixlat + print *,'mean mslp value (xval(9))= ',xval(9) + print *,'gridpoint mslp value= ',slp(ifix,jfix) + print *,'ccflag= ',ccflag + print *,'prs of last closed isobar = ',plastbar + print *,'radius of last closed isobar = ',rlastbar + & ,' nm' + print *,' ' + endif + + ! This next bit of code adds a second layer of closed + ! contour checking. This is to decrease the + ! occurrence of interrupted midlat and tcgen tracks, + ! which usually happens when the closed contour + ! criterion is not met for one time period. So in + ! this next code, we check to see if the ccflag was + ! 'y' for at least half the time over the last 24h. + ! For time periods shorter than 24h (e.g., the storm + ! was just detected at 144h and we are now at 156h), + ! the threshold is still that for at least half of + ! the time the system has been detected as a storm, + ! it must have a ccflag value of 'y'. + + if (ccflag == 'y') then + closed_mslp_ctr_flag(ist,ifh) = 'y' + else + closed_mslp_ctr_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & closed_mslp_ctr_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (closed_mslp_ctr_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.50) then + ccflag = 'y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE ON CLOSED CONTOUR CHECK: The' + print *,' ccflag returned for this hour was' + print *,' NO, but a check of recent ccflags' + print *,' indicates that more than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + ccflag = 'n' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!! NOTE ON CLOSED CONTOUR CHECK: The' + print *,'!! ccflag returned for this hour was' + print *,' NO, and a check of recent ccflags' + print *,' indicates that less than 50% of ' + print *,' the ccflags over the last 24h are' + print *,' YES, so we will stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + if (ccflag == 'y') then + isastorm(2) = 'Y' + else if (ccflag == 'n') then + isastorm(2) = 'N' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*---------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*---------------------------------------*' + print *,' ' + endif + + + else if (isastorm(1) /= 'Y' .and. + & calcparm(3,ist) .and. + & (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen')) then + + ! The isastorm(1) flag indicates that a mslp gradient + ! could not be found at this lead time, so the mask + ! cannot be updated using mslp. Instead, + ! do a check of the 850 mb wind circulation + ! surrounding the 850 wind circulation fix, and then + ! set the mask to be TRUE for all points within the + ! area where mean cyclonic Vt exceed +1 m/s.... + +c call check_closed_contour (imax,jmax,ifix,jfix,slp +c & ,valid_pt,masked_outc,ccflag,'min',trkrinfo +c & ,999,contour_info,get_last_isobar_flag,plastbar +c & ,rlastbar,icccret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Calling mask_based_on_wind_circ at ' + & ,ifcsthour + endif + + call mask_based_on_wind_circ (imax,jmax,dx,dy,850 + & ,valid_pt,masked_outc,trkrinfo + & ,clon(ist,ifh,3),clat(ist,ifh,3),inp%modtyp + & ,imbowret) + + endif + + ! For tropical cyclones, check the avg 850 mb tangential + ! windspeed close to the storm center.... + + if (trkrinfo%type == 'tcgen' .or. + & trkrinfo%type == 'tracker') then + + had_to_try_backup_850_vt_check = 'n' + + if (calcparm(3,ist)) then + + if (verb .ge. 3) then + print *,' ' + print *,'Checking 850 mb Vt speed using 850 mb ' + print *,'wind circulation fix: ' + print *,' 850 mb wcirc fix lon= ',clon(ist,ifh,3) + print *,' 850 mb wcirc fix lat= ',clat(ist,ifh,3) + print *,' Multi-parm fix lon= ',fixlon(ist,ifh) + print *,' Multi-parm fix lat= ',fixlat(ist,ifh) + print *,' ' + endif + + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,clon(ist,ifh,3),clat(ist,ifh,3) + & ,xval(3),trkrinfo,isastorm(3),isiret3) + + else + + ! Sept 2016: There has been a hole in this logic for + ! a while. If a fix can't be made for 850 mb wind + ! circulation (maybe the 850 mb wind circulation fix + ! was too far away from the guess?), then this check + ! isn't performed. We are changing this so that the + ! 850 mb Vt wind speed check will still be + ! performed, but using the mean fixlat and fixlon + ! positions as the center. Still, we first need to + ! check to see if 850 mb u-comp and v-comp were even + ! read in. If they weren't, then we are just out + ! of luck. + + had_to_try_backup_850_vt_check = 'y' + isiret3 = -99 + + if (trkrinfo%use_backup_850_vt_check == 'y' .or. + & trkrinfo%use_backup_850_vt_check == 'Y') then + + if (readflag(3) .and. readflag(4)) then + + if (verb .ge. 3) then + print *,' ' + print *,'!!! NOTE: 850 mb wcirc fix not ' + print *,'available. We are instead ' + print *,'checking 850 mb Vt speed using ' + print *,'multi-parm fix position: ' + print *,' Multi-parm fix lon= ' + & ,fixlon(ist,ifh) + print *,' Multi-parm fix lat= ' + & ,fixlat(ist,ifh) + print *,' ' + endif + + call is_it_a_storm (imax,jmax,dx,dy,'v850',ist + & ,valid_pt,fixlon(ist,ifh),fixlat(ist,ifh) + & ,0.00,trkrinfo,isastorm(3),isiret3) + + endif + + endif + + endif + + if (calcparm(3,ist) .or. + & (had_to_try_backup_850_vt_check == 'y' .and. + & isiret3 == 0) ) then + + if (trkrinfo%type == 'tcgen') then + ! This next bit of code adds a second layer of 850 + ! mb Vt magnitude checking. This is to decrease + ! the occurrence of interrupted tcgen tracks, + ! which occasionally happens for weak storms when + ! this criterion is not met for one time period. + ! So in this next code, we check to see if the + ! vt850_flag was 'y' for at least 75% of the time + ! over the last 24h. For time periods shorter + ! than 24h (e.g., the storm was just detected at + ! 144h and we are now at 156h), the threshold is + ! still that for at least 75% of the time the + ! system has been detected as a storm, it must + ! have a vt850_flag value of 'y'. + + if (isastorm(3) == 'Y') then + vt850_flag(ist,ifh) = 'y' + else + vt850_flag(ist,ifh) = 'n' + if (ifh > 1) then + iccfh = ifh + cc_time_sum_tot = 0.0 + cc_time_sum_yes = 0.0 + do while (iccfh > 1 .and. + & vt850_flag(ist,iccfh) /= 'u' .and. + & cc_time_sum_tot < 24.0) + xinterval_fhr = fhreal(iccfh) - + & fhreal(iccfh-1) + cc_time_sum_tot = cc_time_sum_tot + & + xinterval_fhr + if (vt850_flag(ist,iccfh) == 'y') then + cc_time_sum_yes = cc_time_sum_yes + & + xinterval_fhr + endif + iccfh = iccfh - 1 + enddo + if (cc_time_sum_tot > 0.0) then + cc_time_pct = cc_time_sum_yes / + & cc_time_sum_tot + else + cc_time_pct = 0.0 + endif + if (cc_time_pct >= 0.75) then + isastorm(3) = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ NOTE ON Vt_850 CHECK: The ' + print *,' isastorm flag returned for ' + print *,' this hour was NO, but a' + print *,' check of recent vt850_flags' + print *,' indicates that more than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' continue.' + print *,' cc_time_pct= ',cc_time_pct + print *,' ' + endif + + else + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE ON Vt_850 CHECK: The ' + print *,'!!! isastorm flag returned for ' + print *,' this hour was NO, and a' + print *,' check of recent vt850_flags ' + print *,' indicates that less than 75%' + print *,' of the vt850_flags over the' + print *,' last 24h are YES, so we will' + print *,' stop tracking.' + print *,' cc_time_pct= ',cc_time_pct + endif + + endif + endif + endif + + endif + + endif + endif + + else + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + isastorm(1) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a midlat or tcgen case, a fix ' + print *,'!!! could not be made for mslp, ' + print *,'!!! therefore we will stop tracking ' + print *,'!!! for this storm.' + endif + + else + isastorm(1) = 'N' + isastorm(3) = 'N' + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! For a TC tracker case, a fix could' + print *,'!!! not be made using any tracked parms,' + print *,'!!! therefore we will stop tracking for' + print *,'!!! this storm.' + endif + + endif + + if ( verb .ge. 3 ) then + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + + endif + + if (isiret1 /= 0 .or. isiret2 /= 0 .or. isiret3 /= 0) + & then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: One of the calls to ' + print *,'!!! is_it_a_storm produced an error.' + print *,'!!! Chances are this is from a call to ' + print *,'!!! get_ij_bounds, meaning we are too close' + print *,'!!! to a regional grid boundary to do this ' + print *,'!!! analysis. Processing will continue....' + print *,'!!! isiret1= ',isiret1,' isiret2= ',isiret2 + print *,'!!! isiret3= ',isiret3 + endif + + endif + + if (isastorm(1) == 'N' .or. isastorm(2) == 'N' .or. + & isastorm(3) == 'N') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! At least one of the isastorm flags from' + print *,'!!! subroutine is_it_a_storm is "N", so ' + print *,'!!! either we were unable to find a good ' + print *,'!!! mslp gradient and/or a valid 850 mb ' + print *,'!!! circulation for the storm at this time,' + print *,'!!! or, for the cases of midlat or tcgen ' + print *,'!!! tracking, a closed mslp contour could ' + print *,'!!! not be found, thus we will stop tracking' + print *,'!!! this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ',storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! mslp gradient flag = ',isastorm(1) + print *,'!!! closed contour flag = ',isastorm(2) + print *,'!!! 850 mb winds flag = ',isastorm(3) + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + endif + + ! Now do another check for the tracker and tcgen cases. + ! If the isastorm flags for mslp gradient and v850 BOTH + ! came back positive AND you have been able to locate an + ! 850 mb vort center, just do a check to make sure that + ! the distance between the 850 vort center and the mslp + ! center is not too great. + + if (trkrinfo%type == 'tracker' .or. + & trkrinfo%type == 'tcgen') then + if (isastorm(1) == 'Y' .and. isastorm(3) == 'Y' .and. + & calcparm(1,ist) .and. stormswitch(ist) == 1) then + +c if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) >= 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else if (atcfname == 'GFSO' .and. +c & abs(slatfg(ist,ifh)) < 25.0) then +c trkrinfo%max_mslp_850 = 405.0 +c else +c trkrinfo%max_mslp_850 = 323.0 +c endif + + call calcdist (clon(ist,ifh,9),clat(ist,ifh,9) + & ,clon(ist,ifh,1),clat(ist,ifh,1),dist + & ,degrees) + + if (dist > trkrinfo%max_mslp_850) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, the dist betw' + print *,'!!! the mslp center & the 850 zeta ' + print *,'!!! center is too great, thus we will' + print *,'!!! stop tracking this storm.' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + print *,'!!! Actual distance (km) = ',dist + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Actual distance between the parm centers' + print *,'for 850 zeta and mslp is ',dist,' (km)' + print *,'Max dist allowed (km) = ' + & ,trkrinfo%max_mslp_850 + endif + + endif + endif + endif + + ! Do one final check. Check the new fix position and + ! the old fix position and calculate the speed that the + ! storm would have had to travel to get to this point. + ! If that speed exceeds a certain threshold (~60 kt), + ! assume you're tracking the wrong thing and quit. + ! Obviously, only do this for times > 00h. The check + ! in the if statement to see if the previous hour's + ! lats and lons were > -999 is for the midlat and + ! tcgen cases -- remember, they can have genesis at + ! any hour of the forecast, in which case the previous + ! forecast hour's lat & lon would be -999. + + if (ifh > 1 .and. stormswitch(ist) == 1) then + if (fixlon(ist,ifh-1) > -999.0 .and. + & fixlat(ist,ifh-1) > -999.0 ) then + + if (trkrinfo%type == 'midlat') then + xmaxspeed = maxspeed_ml + else + xmaxspeed = maxspeed_tc + endif + + call calcdist (fixlon(ist,ifh-1),fixlat(ist,ifh-1) + & ,fixlon(ist,ifh),fixlat(ist,ifh),dist + & ,degrees) + + ! convert distance from km to nm and get speed. + + distnm = dist * 0.539638 + xinterval_fhr = fhreal(ifh) - fhreal(ifh-1) + xknots = distnm / xinterval_fhr + + if (xknots > xmaxspeed) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In routine tracker, calculated spd' + print *,'!!! of the storm from the last position' + print *,'!!! to the current position is too high,' + print *,'!!! so we will stop tracking this storm' + print *,'!!! (For fear that we are not actually ' + print *,'!!! tracking our storm, but have instead' + print *,'!!! locked onto some other feature....)' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm = ' + & ,storm(ist)%tcv_storm_name + write (6,432) ifhours(ifh),ifclockmins(ifh) + print *,'!!! Max speed allowed (kt) = ',xmaxspeed + print *,'!!! Actual speed (kt) = ',xknots + print *,' ' + endif + + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'The average speed that the storm moved' + print *,'at since the previous forecast time is' + & ,xknots,' knots.' + endif + + endif + + endif + + endif + + endif + +c Now get the maximum near-surface wind speed near the storm +c center (get_max_wind). Also, call getradii to get the +c radii in each storm quadrant of gale-force, storm-force +c and hurricane force winds. + + if (readflag(10) .and. readflag(11) .and. ifret == 0 + & .and. stormswitch(ist) == 1) then + call get_max_wind (fixlon(ist,ifh),fixlat(ist,ifh) + & ,imax,jmax,dx,dy,valid_pt,levsfc + & ,xmaxwind(ist,ifh),trkrinfo,rmax,igmwret) +c if (igmwret /= 0 .and. gridmove_status == 'stopped') then + if (igmwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Return code from get_max_wind is /= 0. ' + print *,'!!! rcc= igmwret= ',igmwret + print *,'!!! Also, this is a moveable, regional grid' + print *,'!!! and the grid did not change from last' + print *,'!!! lead time to current one, so what has' + print *,'!!! likely happened is that the storm has ' + print *,'!!! moved close to the edge of the nested ' + print *,'!!! grid domain, but the nested grid itself' + print *,'!!! had stopped moving, probably because it' + print *,'!!! dropped or lost the storm.' + print *,'!!! ' + print *,'!!! TRACKING WILL STOP FOR THIS STORM' + print *,'!!! ' + endif + + stormswitch(ist) = 2 + cycle stormloop + endif + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + + ! For the radii, we encountered a problem with radmax + ! being too small. It was set at 650 km. Hurricane + ! Sandy exceeded this in the models, so the values + ! returned from getradii were close to the default + ! radmax value of 650 km (350 nm), instead of higher. + ! To fix it, we now use an iterative technique, where + ! we start with radmax as a small value (500 km). If + ! getradii returns a value for R34 in a quadrant that + ! does not exceed 0.97*radmax, then that value is ok. + ! If it does exceed 0.97*radmax, then we bump up radmax + ! by 50 km and call getradii again, looking to diagnose + ! radii only in those quadrants where the + ! need_to_expand_r34 flag = 'n'. BTW... note the + ! initial IF statement... we will only go into this + ! routine if the max wind just diagnosed for this lead + ! time is at least 34 kts (17.5 m/s). + + if (xmaxwind(ist,ifh) >= 17.5) then + + vradius = 0 + first_time_thru_getradii = .true. + r34_check_okay = 'n' + do ivr = 1,4 + need_to_expand_r34(ivr) = 'y' + enddo + radmax = 500.0 ! Initial radmax, in km + + igrct = 1 + + if ( verb .ge. 3 ) then + write (6,242) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 242 format (1x,'TIMING: b4 getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + getrad_iter_loop: do while + & (r34_check_okay == 'n' .and. radmax <= 1050.) + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,244) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 244 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + call getradii (fixlon(ist,ifh),fixlat(ist,ifh),imax + & ,jmax,dx,dy,valid_pt,storm(ist)%tcv_storm_id + & ,ifcsthour,xmaxwind(ist,ifh),vradius + & ,trkrinfo,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) + + if (igrret /= 0) then + if (verb >= 3) then + print *,' ' + print *,'!!! ERROR: Return code from getradii = ' + & ,igrret + print *,'!!! Searching for radii will not be ' + print *,'!!! completed for this lead time and' + print *,'!!! all radii values will be set to ' + print *,'!!! missing.' + print *,' ' + exit getrad_iter_loop + endif + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,245) ifcsthour,igrct,date_time(5) + & ,date_time(6),date_time(7) + 245 format (1x,'TIMING: after call getradii, fhr= ',i5 + & ,' igrct= ',i2,' ',i2.2,':',i2.2,':',i2.2) + endif + + first_time_thru_getradii = .false. + igrct = igrct + 1 + r34_dist_thresh = 0.97 * radmax + r34_good_ct = 0 + do ivr = 1,4 + vradius_km = float(vradius(1,ivr)) / 0.5396 + if (vradius_km < r34_dist_thresh) then + r34_good_ct = r34_good_ct + 1 + need_to_expand_r34(ivr) = 'n' + endif + enddo + if (r34_good_ct == 4) then + r34_check_okay = 'y' + endif + radmax = radmax + 50.0 + enddo getrad_iter_loop + + if ( verb .ge. 3 ) then + write (6,246) ifcsthour,igrct,xmaxwind(ist,ifh) + & ,date_time(5) + & ,date_time(6),date_time(7) + 246 format (1x,'TIMING: after getrad_iter_loop, fhr= ',i5 + & ,' igrct= ',i2,' Vmax (m/s)= ',f8.3 + & ,' ',i2.2,':',i2.2,':',i2.2) + endif + + endif + + endif + +c If the user has requested so, then call a routine to +c determine the type of cyclone, using Bob Hart's +c cyclone phase space (CPS) algorithms. It is only used +c for times after t=0, since for the first check (of the +c "parameter B" thickness asymmetry), we need to know +c in which direction the storm is moving. Pulling that +c storm movement data off of the tcvitals is not reliable +c since the model storm may not be moving in the same +c direction as the observed storm. However, we could do +c an upgrade later where this storm movement data is +c pulled from the "genesis vitals", which are derived +c from the model forecast data itself, not the obs. + + if (phaseflag == 'y' .and. stormswitch(ist) == 1) then + wcore_flag = 'u' ! 'u' = undetermined + call get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag,igpret) + endif + + if (structflag == 'y' .or. ikeflag == 'y') then + call get_sfc_center (fixlon(ist,ifh),fixlat(ist,ifh) + & ,clon,clat,ist,ifh,calcparm,xsfclon + & ,xsfclat,maxstorm,igscret) + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,er_wind,sr_wind,er_vr,sr_vr + & ,er_vt,sr_vt,maxstorm,trkrinfo,igwsret) + if (igwsret == 0) then + call output_wind_structure (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),er_wind,sr_wind + & ,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iowsret) + endif + endif + + if (structflag == 'y' .and. stormswitch(ist) == 1) then + call get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,wfract_cov,pdf_ct_bin + & ,pdf_ct_tot,maxstorm,trkrinfo,igfwret) + if (igfwret == 0) then + call output_fract_wind (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),wfract_cov,'earth' + & ,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) + endif + endif + + if (ikeflag == 'y' .and. stormswitch(ist) == 1) then + call get_ike_stats (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat + & ,valid_pt,calcparm,ike,sdp,wdp,maxstorm + & ,trkrinfo,igisret) + if (igisret == 0) then + call output_ike (fixlon(ist,ifh) + & ,fixlat(ist,ifh),xsfclon,xsfclat,inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),ike,sdp,wdp,maxstorm + & ,ioiret) + endif + endif + +c Now print out the current fix position and intensity +c (in knots) to standard output. Conversion for m/s to +c knots (1.9427) is explained in output_atcf. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After call to fixcenter, fix positions at ' + write (6,442) ifhours(ifh),ifclockmins(ifh) + 442 format (1x,'forecast hour= ',i4,':',i2.2,' follow:') + print *,' ' + endif + + if (ifret == 0 .and. stormswitch(ist) == 1) then + + if ( verb .ge. 3 ) then + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + & ,int((xmaxwind(ist,ifh)*1.9427) + 0.5) + print *,' ' + endif + + ! Only call output routines every atcffreq/100 hours.... + + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + + if (leadtime_check == 0) then + + ifcsthour = ileadtime / 100 + + call output_atcfunix (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + + ! Get the storm motion vector and the speed of + ! motion so that we can output this in the + ! "atcf_sink" forecast text file. + + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'vitals',trkrinfo + & ,ignret) + else + istmdir = -999 + istmspd = -999 + ignret = 0 + endif + + if ( verb .ge. 3 ) then + write (6,617) istmspd,istmdir,ignret + 617 format (1x,'+++ RPT_STORM_MOTION: istmspd= ',i5 + & ,' istmdir= ',i5,' rcc= ',i3) + endif + + ! Call a routine to find the mean & max relative + ! vorticity near the storm at 850 & 700. These will + ! be written out to the "atcf_sink" fcst text file. + + imeanzeta = -99 + igridzeta = -99 + call get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm,trkrinfo + & ,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + endif + + call output_atcf_sink (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist + & ,ifcsthour,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta + & ,igridzeta,cps_vals,plastbar,rlastbar + & ,ioaxret) + + if (inp%model == 12 .and. ifcsthour == 0) then + ! Write vitals for GFS ens control analysis + call output_tcvitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,iovret) + + endif + endif + + ! The exception here is for the call to the output_hfip + ! routine, which will be called for every lead time + ! that is processed.... + + call output_hfip (fixlon(ist,ifh),fixlat(ist,ifh),inp,ist + & ,ifh,xmaxwind(ist,ifh) + & ,gridprs(ist,ifh),vradius,rmax,ioaxret) + else + + if ( verb .ge. 3 ) then + write (6,452) 'fixpos ',storm(ist)%tcv_storm_id + & ,' fhr= ',ifhours(ifh),ifclockmins(ifh) + & ,' Fix not made for this forecast hour' + 452 format (1x,a7,1x,a4,a6,i4,':',i2.2,a36) + + print *,' ' + print *,'!!! RETURN CODE from fixcenter not equal to 0,' + print *,'!!! or output from is_it_a_storm indicated the' + print *,'!!! system found was not our storm, or the ' + print *,'!!! speed calculated indicated we may have ' + print *,'!!! locked onto a different center, thus a fix' + print *,'!!! was not made for this storm at this ' + print *,'!!! forecast hour.' + print *,'!!! mslp gradient check = ',isastorm(1) + print *,'!!! mslp closed contour check = ',isastorm(2) + print *,'!!! 850 mb winds check = ',isastorm(3) + print *,'!!! fixcenter return code = ifret = ',ifret + print *,' ' + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + +c if (inp%model == 1 .or. inp%model == 8 .or. +c & inp%model == 22) then +cPENG + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + + ! For the vt=00h lead time, if the tracker failed to + ! locate a position, we are going to write out an + ! atcfunix record that contains the position, + ! intensity, mslp and 34-kt wind radii from TC Vitals + ! for this storm and initial time. Only do this for + ! the GFS or GDAS runs of the tracker. + + tcv_max_wind_ms = float(storm(ist)%tcv_vmax) + tcv_mslp_pa = float(storm(ist)%tcv_pcen) * 100.0 + + ! Convert tcvitals NE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15ne) + if (r34_from_tcv > 0.0) then + vradius(1,1) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,1) = 0 + endif + + ! Convert tcvitals SE 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15se) + if (r34_from_tcv > 0.0) then + vradius(1,2) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,2) = 0 + endif + + ! Convert tcvitals SW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15sw) + if (r34_from_tcv > 0.0) then + vradius(1,3) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,3) = 0 + endif + + ! Convert tcvitals NW 34-kt wind radius from km to nm + r34_from_tcv = float(storm(ist)%tcv_r15nw) + if (r34_from_tcv > 0.0) then + vradius(1,4) = int( ((r34_from_tcv*0.5396) + & / 5.0) + 0.5) * 5 + else + vradius(1,4) = 0 + endif + + ! Convert tcvitals roci from km to nm + + if (storm(ist)%tcv_penvrad > 0) then + roci_from_tcv = float(storm(ist)%tcv_penvrad) + rlastbar = roci_from_tcv * 0.5396 + else + rlastbar = -99.0 + endif + + ! Convert tcvitals pressure at roci from km to nm + + if (storm(ist)%tcv_penv > 0) then + proci_from_tcv = float(storm(ist)%tcv_penv) + plastbar = proci_from_tcv * 100.0 + else + plastbar = -99.0 + endif + + write (6,291) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + & ,atcfymdh + 291 format (1x,'NOTE: TCVITALS_USED_FOR_ATCF_F00 ' + & ,' Storm ID: ',a4,' Storm name: ',a9 + & ,' YMDH: ',i10) + + call output_atcfunix (slonfg(ist,ifh) + & ,slatfg(ist,ifh),inp,ist + & ,ifcsthour,tcv_max_wind_ms + & ,tcv_mslp_pa,vradius,maxstorm,trkrinfo + & ,plastbar,rlastbar,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + else + + ! For all other models, we print out missing + ! data values at tau=00h if the tracker was + ! unable to find the storm.... + + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + + endif + + imeanzeta = -99 + igridzeta = -99 + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + + if (trkrinfo%type == 'tracker') then + ! Update 11/11: For a 'tracker' run, i.e., one in + ! which we know that there is an observed storm in + ! the area, we will assume that there was some type + ! of problem in the initialization that prevented + ! the storm from being found. In this case, even + ! though we have written out zeroes for the 00h + ! time, we want to at least try tracking again at + ! the next lead time. Requested by HWRF folks.... + if (verb .ge. 3) then + print *,' ' + print *,'++ NOTE: Even though a fix could not be' + print *,' made for this storm at 00h, we will ' + print *,' use the storm heading info from tc' + print *,' vitals to create a guess for the next' + print *,' lead time and attempt to track again' + print *,' at that time.' + print *,' ifh= ',ifh,' ist= ',ist + write (6,301) storm(ist)%tcv_storm_id + & ,storm(ist)%tcv_storm_name + 301 format (1x,' storm_id = ',a4,' storm_name = ',a9) + endif + call get_next_ges (slonfg,slatfg,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + stormswitch(ist) = 1 + endif + + endif + cycle stormloop + endif + + +c Now get first guess for next forecast time's position. +c But first, if this is the first time level (ifh=1) and +c the user has requested that storm vitals be output (this +c is usually only done for model analyses in order to get +c an analysis position from one time to the next), we will +c write out a storm vitals record for this time level. +c Note that we have already gotten the next guess position +c info just above for the case of the repeated analysis +c data, so we'll just output the genesis vitals record. + + if (ifh <= ifhmax) then + if (ifh == 1 .and. trkrinfo%out_vit == 'y') then + call output_gen_vitals (fixlon(ist,ifh) + & ,fixlat(ist,ifh),inp,ist,istmspd,istmdir,iovret) + endif + if (ifh < ifhmax) then + call get_next_ges (fixlon,fixlat,ist,ifh + & ,imax,jmax,dx,dy,inp%model,valid_pt,readflag + & ,maxstorm,istmspd,istmdir,'tracker',trkrinfo + & ,ignret) + if (ignret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: Problem getting first guess ' + print *,'!!! position for next lead time. Return' + print *,'!!! code from call to get_next_ges = ' + print *,'!!! ignret = ',ignret + print *,'!!! Storm name = ' + & ,storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! TRACKING WILL STOP FOR THIS STORM.' + endif + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + stormswitch(ist) = 2 + cycle stormloop + endif + else + istmdir = -999 + istmspd = -999 + endif + endif + + case (2) + fixlon (ist,ifh) = -999.0 + fixlat (ist,ifh) = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Case 2 in tracker for stormswitch' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + if (ifh == 1) then + vradius = 0 + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 + call output_atcfunix (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99.0,-99.0,-99.0 + & ,cps_vals,wcore_flag,ioaxret) + imeanzeta = -99 + igridzeta = -99 + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + call output_atcf_gen (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,-999.0,-999.0,-99.0 + & ,cps_vals,'u',imeanzeta,igridzeta,ioaxret) + endif + call output_atcf_sink (-999.0 + & ,-999.0,inp,ist + & ,ifcsthour,0.0 + & ,0.0,vradius,maxstorm,trkrinfo + & ,-99,-99,imeanzeta,igridzeta + & ,cps_vals,-999.0,-999.0,ioaxret) + call output_hfip (-999.0 + & ,-999.0,inp,ist + & ,ifh,0.0 + & ,0.0,vradius,-99.0,ioaxret) + endif + + case (3) + continue + +c print *,' ' +c print *,'!!! Case 3 in tracker for stormswitch' +c print *,'!!! Storm name = ',storm(ist)%tcv_storm_name +c print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + if (trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') then + ileadtime = nint(fhreal(ifh) * 100.0) + leadtime_check = mod(ileadtime,atcffreq) + if (leadtime_check == 0) then + ifcsthour = ileadtime / 100 + endif + if (trkrinfo%inp_data_type == 'grib') then + call output_tracker_mask (masked_outc,valid_pt,ifh + & ,ifcsthour,imax,jmax,iotmret) + endif + endif + + if(use_per_fcst_command=='y') then +c User wants us to run a command per forecast time + +! Replace %[FHOUR] with forecast hour, %[FMIN] with forecast minute. + +! The %[] format is chosen to avoid shell syntax errors if someone +! includes unknown %[] constructs. A stray , for example, +! would generate syntax errors or unexpected results in some +! shells. + +! If an unrecognized %[xxx] sequence is used, it will be retained in +! the final command. This allows the underlying command to detect +! the unreplaced %[] and use suitable default values or abort, as +! appropriate. + + pfc_final=per_fcst_command + call argreplace(pfc_final,pfc_cmd_len,'%[FHOUR]', & + & ifhours(ifh)) + call argreplace(pfc_final,pfc_cmd_len,'%[FMIN]', & + & iftotalmins(ifh)) + + if(verb.ge.2) then + print *,' ' + print *,'!!! Running per-fcst command' + print *,'!!! Unparsed = ',trim(per_fcst_command) + print *,'!!! Parsed = ',trim(pfc_final) + endif + call run_command(trim(pfc_final),pfcret) + if(pfcret/=0 .and. verb.ge.1) then + print *,' ' + print *,'!!! Non-zero exit status from per-fcst command' + print *,'!!! Command = ',trim(pfc_final) + print *,'!!! Exit status = ',pfcret + print *,'!!! Continuing anyway...' + elseif(pfcret==0 .and. verb.ge.2) then + print *,' ' + print *,'!!! Per-fcst command returned success status (0)' + endif + endif + + ifh = ifh + 1 + if (ifh > ifhmax) exit ifhloop + + if (inp%file_seq == 'multi') then + call baclose(lugb,igcret) + call baclose(lugi,iicret) + if ( verb .ge. 3 ) then + print *,'baclose return code for unit ',lugb,' = igcret = ' + & ,igcret + print *,'baclose return code for unit ',lugi,' = iicret = ' + & ,iicret + endif + endif + + enddo ifhloop +c +cPENG call output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +cPENG call output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm,ifhmax +cPENG & ,ioaret) +c + 73 format ('fixpos ',a4,' fhr= ',i4,':',i2.2,' Fix position= ' + & ,f7.2,'E (',f6.2,'W)',2x,f7.2,' Max Wind= ',i3,' kts') + + if (allocated(prstemp)) deallocate (prstemp) + if (allocated(prsindex)) deallocate (prsindex) + if (allocated(iwork)) deallocate(iwork) + if (allocated(zeta)) deallocate (zeta) + if (allocated(u)) deallocate (u) + if (allocated(v)) deallocate (v) + if (allocated(hgt)) deallocate (hgt) + if (allocated(slp)) deallocate (slp) + if (allocated(tmean)) deallocate (tmean) + if (allocated(thick)) deallocate (thick) + if (allocated(lsmask)) deallocate (lsmask) + if (allocated(masked_out)) deallocate (masked_out) + if (allocated(masked_outc)) deallocate (masked_outc) + if (allocated(cpshgt)) deallocate (cpshgt) + if (allocated(vt850_flag)) deallocate (vt850_flag) + if (allocated(closed_mslp_ctr_flag)) + & deallocate (closed_mslp_ctr_flag) + if (allocated(netcdf_file_time_values)) + & deallocate (netcdf_file_time_values) + if (allocated(nctotalmins)) + & deallocate (nctotalmins) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine argreplace(arg,n,name,val) + ! This subroutine is used to generate the pre-forecast-command + ! It will edit the command (argument "arg") and replace string + ! name with value val. That is how the per-forecast-command + ! has these modifications: + + ! %[FHOUR] -> replace with -> last forecast hour + ! %[FMIN] -> replace with -> last forecast minute + + implicit none + + integer, intent(in) :: n + character(n), intent(inout) :: arg + character(*), intent(in) :: name + integer, intent(in) :: val + + integer found,namelen,i1,i2 + character(n) :: out + + found=index(arg,name) + namelen=len(name) + i1=found-1 ! last char that is before name + i2=found+namelen ! index of last char in name + + if(found==0) return + + out=' ' + + if(found>1 .and. i21) then +! special case: name is at end of string +! hope the value fits... + write(out,'(A,I0)') arg(1:i1),val + elseif(i2 + & ,'... gopen_i_file= ...',a,'...') + + print *,'gopen_g_file= ',gopen_g_file,'....' + print *,'gopen_i_file= ',gopen_i_file,'....' + + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + inquire (unit=lout, opened=output_file_open) + if (output_file_open) then + iooret = 0 + else + fnameo(1:5) = "fort." + write(fnameo(6:7),'(I2)') lout + call baopenw (lout,fnameo,iooret) + endif + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + inquire (file=gopen_g_file, opened=file_open4) + if (file_open4) then + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is OPEN' + else + print *,'TEST gname open_grib_files, gfile= ' + & ,gopen_g_file,' is CLOSED' + endif + + inquire (file=gopen_i_file, opened=file_open5) + if (file_open5) then + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is OPEN' + else + print *,'TEST iname open_grib_files, ifile= ' + & ,gopen_i_file,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'gettrk baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine open_ncfile (filename,ncid) + +c ABSTRACT: This subroutine opens a netcdf file specified by the +c input file "ncfile" and returns the netcdf file id that will be +c associated with that file. +c +c INPUT: +c ncfile character full-path file netcdf name +c +c OUTPUT: +c ncfile_id integer, netcdf id assigned to the netcdf file + + implicit none + + include "netcdf.inc" + + character*(*), intent(in) :: filename + integer, intent(out) :: ncid + integer :: status + + status = nf_open (filename, NF_NOWRITE, ncid) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine open_ncfile +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine is_it_a_storm (imax,jmax,dx,dy,cparm,ist + & ,defined_pt,parmlon,parmlat + & ,parmval,trkrinfo,stormcheck,isiret) + +c ABSTRACT: This subroutine is called after the center of the storm +c has been fixed. Its purpose is to determine whether or not +c the center that was found is actually a storm, and not just some +c passing trough (this has happened in the case of decaying or weak +c storms). It's called twice -- once to check for a minimum MSLP +c gradient, and once to check for a circulation at 850 mb. The +c subroutine input parameter "cparm" determines which parameter to +c check for. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is to be checked: +c slp = mslp, for a check of mslp gradient +c v850 = tangential winds at 850 mb +c ist integer storm number (internal to the tracker) +c defined_pt Logical; bitmap indicating if valid data at that pt. +c parmlon Longitude of the max/min value for the input parameter +c parmlat Latitude of the max/min value for the input parameter +c parmval Data value at parm's max/min point (used for mslp call) +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c stormcheck Character; set to 'Y' if mslp gradient or 850 mb +c tangential winds check okay. +c isiret Return code for this subroutine. +c + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE tracked_parms; USE atcf; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + real vt,vtavg,vr,parmlat,parmlon,parmval,dist + real pthresh,vthresh,degrees,dx,dy,dell,ri,radinf + real pgradient,xmaxpgrad + character(*) cparm + logical(1) defined_pt(imax,jmax) + character*1 stormcheck + integer isiret,imax,jmax,ist,npts,ilonfix,jlatfix,igvtret + integer ibeg,iend,jbeg,jend,ivt,i,j,iix,jix,bskip,igiret + + isiret = 0 + stormcheck = 'N' + + dell = (dx+dy)/2. + +c First define the radius of influence, which depends on the +c grid spacing of the model data being used. The ceiling statement +c for npts in the first if statement is needed in case the +c resolution of the grib files eventually goes very low, down to +c say a half degree or less, in order to cover enough points in +c the search. + + if (dell < 1.24) then ! GFS, MRF, NAM, NGM, NAVGEM, GDAS, + ! GFDL, NCEP Ensemble & Ensemble + ! Relocation, SREF Ensemble + ri = ritrk_most + if (cparm == 'slp') then + radinf = 300.0 + else + radinf = 225.0 + endif + npts = ceiling(radinf/(dtk*(dx+dy)/2.)) + else if (dell >= 1.24 .and. dell < 2.49) then ! UKMET + ri = ritrk_most + radinf = 275.0 + npts = 2 + else ! ECMWF + ri = ritrk_coarse + radinf = 350.0 + npts = 1 + endif + + pthresh = trkrinfo%mslpthresh ! These are read in in + vthresh = trkrinfo%v850thresh ! subroutine read_nlists.... + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,parmlon,parmlat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij B, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij B, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print*,' ' + print*,'!!! ERROR in is_it_a_storm from call to' + print*,'!!! get_ij_bounds, stopping processing for ' + print*,'!!! storm number ',ist + endif + + isiret = 92 + return + endif + +c If the input cparm is slp, then check to see that the MSLP +c gradient in any direction from the MSLP center is at least +c 1mb / 200km, or 0.005mb/km. This is based on discussions with +c Morris & Bob, who have had good results using a 2mb/200km +c requirement. Since their model has a much finer resolution than +c all of the models we run the tracker on AND a much better +c depiction of the hurricane vortex, we do not use a requirement +c as strict as theirs, and so make the requirement only half as +c strong as theirs. +c +c If the input cparm is v850, then check to see that there is +c a circulation at 850 mb. We will do this by calculating the +c tangential wind of all points within a specified radius of +c the 850 minimum wind center, and seeing if there is a net +c average tangential wind speed of at least 5 m/s. +c +c UPDATE APRIL 2000: I've relaxed the thresholds slightly from +c 0.005 mb/km to 0.003 mb/km, and the wind threshold from +c 5 m/s to 3 m/s. Also, note that a special case for GDAS has +c been hardwired in that is weaker (0.002 mb/km and 2 m/s). +c That weaker GDAS requirement is for Qingfu's relocation stuff. +c +c UPDATE JULY 2001: The relaxed requirement put in place in +c April 2000 for the GDAS relocation has also been put in place +c for the GFS ensemble relocation. + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the loop. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, ilonfix= ',ilonfix + & ,' jlatfix= ',jlatfix + print *,'ibeg jbeg iend jend = ',ibeg,jbeg,iend,jend + print *,'cparm= ',cparm,' parmlon parmlat = ',parmlon,parmlat + print *,'parmval= ',parmval + print *,' ' + endif + + vtavg = 0.0 + ivt = 0 + + xmaxpgrad = -999.0 + + jloop: do jix = jbeg,jend,bskip + iloop: do iix = ibeg,iend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine is_it_a_storm' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + i = iix - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine ' + print *,'!!! is_it_a_storm for a non-global grid.' + print *,'!!! STOPPING....' + print *,'!!! i= ',i,' imax= ',imax + print *,' ' + endif + + stop 97 + endif + endif + + call calcdist(parmlon,parmlat,glon(i),glat(j),dist,degrees) + + if (dist > radinf .or. dist == 0.0) cycle + + if (defined_pt(i,j)) then + + if (cparm == 'slp') then + pgradient = (slp(i,j) - parmval) / dist + if (pgradient > xmaxpgrad) xmaxpgrad = pgradient + + if ( verb .ge. 3 ) then + write (6,93) i,j,glon(i),glat(j),dist,slp(i,j),pgradient + endif + + if (pgradient > pthresh) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In is_it_a_storm, valid pgradient found.' + print '(a23,f8.5)',' pgradient threshold = ',pthresh + print '(a23,f8.5)',' pgradient found = ',pgradient + print *,'mslp center = ',parmlon,parmlat,parmval + print *,'pgrad loc = ',glon(i),glat(j),slp(i,j) + endif + + stormcheck = 'Y' + exit jloop + endif + endif + + if (cparm == 'v850') then + call getvrvt (parmlon,parmlat,glon(i),glat(j) + & ,u(i,j,nlev850),v(i,j,nlev850),vr,vt,igvtret) + if ( verb .ge. 3 ) then + write (6,91) i,j,glon(i),glat(j),u(i,j,nlev850) + & ,v(i,j,nlev850),vr,vt + endif + + vtavg = vtavg + vt + ivt = ivt + 1 + endif + + endif + + enddo iloop + enddo jloop + + 91 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' u= ',f8.4,' v= ',f8.4,' vr= ',f9.5,' vt= ',f9.5) + + 93 format (1x,'i= ',i4,' j= ',i4,' glon= ',f7.2,' glat= ',f6.2 + & ,' dist= ',f8.2,' slp= ',f10.2,' pgradient= ',f8.5) + + if (stormcheck /= 'Y' .and. cparm == 'slp') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, valid pgradient NOT FOUND.' + write (6,94) '!!! (Max pgradient less than ',pthresh,' mb/km)' + 94 format (1x,a29,5x,f8.5,a7) + write (6,95) '!!! Max pgradient (mb/km) found = ',xmaxpgrad + 95 format (1x,a34,f8.5) + print *,' ' + endif + + endif + + if (cparm == 'v850') then + + if (ivt > 0) then + vtavg = vtavg / float(ivt) + else + vtavg = 0.0 + endif + + if (parmlat > 0) then + if (vtavg >= vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (>= +',vthresh,' m/s for a NH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed +',vthresh + & ,' m/s (NH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + else + if (vtavg <= -vthresh) then + stormcheck = 'Y' + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In is_it_a_storm, average 850 tangential' + & ,' winds are OKAY (<= -',vthresh,' m/s for a SH storm).' + print *,' Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In is_it_a_storm, average 850 tangential' + print *,'!!! winds did NOT exceed -',vthresh + & ,' m/s (SH storm).' + print *,'!!! Avg 850 tangential winds = ',vtavg,' m/s' + print *,' ' + endif + + endif + endif + + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_phase (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm + & ,cps_vals,wcore_flag,igpret) +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure or phase of a cyclone. Initially, we +c will just have it use the Hart cyclone phase space (CPS) scheme. + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trkrparms; USE grid_bounds + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character wcore_flag*1,okay_to_call_cps_routines*1 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real cps_vals(3) + real dx,dy,paramb,vtl_slope,vtu_slope + integer imax,jmax,igpret,igcpret,ist,ifh,maxstorm + integer igvpret,igcv1ret,igcv2ret + logical(1) valid_pt(imax,jmax) +c + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,611) + write (6,613) + write (6,615) + write (6,*) ' ' + + 611 format(1x,'#-----------------------------------------------#') + 613 format(1x,'# start of routine to determine cyclone phase...#') + 615 format(1x,'#-----------------------------------------------#') + endif + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + if (ifh > 1 .or. (ifh == 1 .and. trkrinfo%type == 'tracker')) + & then + + ! This condition that ifh > 1 is so that we *not* do the cps + ! stuff for fhour=0 if it's a tcgen or midlat case, since we + ! don't know the model storm motion direction for the + ! analysis. For a regular case where type = 'tracker', we + ! have the observed storm's heading direction from tc vitals, + ! so we can use that (even though the model's storm direction + ! may differ slightly from the observed storm). This current + ! if statement and the ones below carefully check for these + ! various instances. + + okay_to_call_cps_routines = 'n' + + if (ifh > 1) then + if (fixlon(ist,ifh-1) > -990.0 .and. + & fixlat(ist,ifh-1) > -990.0) then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level ' + print *,' >< since the fixlon and fixlat at the ' + print *,' >< previous lead time are undefined.' + print *,' >< This is likely the first found position' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + endif + endif + elseif (ifh == 1 .and. trkrinfo%type == 'tracker') then + okay_to_call_cps_routines = 'y' + else + if (verb >= 3) then + print *,' ' + print *,' >< CPS diagnostics were requested but will' + print *,' >< NOT be performed for this time level.' + print *,' >< The likely reason is that ifh=0 and' + print *,' >< this is a genesis case, so we do not ' + print *,' >< know the storm motion direction.' + print *,' >< for a genesis (tcgen or midlat) case.' + print *,' >< ifh= ',ifh + print *,' >< trkrinfo%type ',trkrinfo%type + endif + endif + + if (okay_to_call_cps_routines == 'y') then + + ! Similarly, these next two conditions (previous lat and + ! previous lon > -999) are in there in case we're doing a + ! tcgen or midlat case and this is the *first* time level + ! within a forecast that the storm has been detected (again, + ! we don't yet know the storm heading). + + call get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'lower',vtl_slope + & ,maxstorm,igcv1ret) + + call get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,'upper',vtu_slope + & ,maxstorm,igcv2ret) + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,73) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh) + & ,paramb,vtl_slope,vtu_slope + endif + + cps_vals(1) = paramb + cps_vals(2) = vtl_slope + cps_vals(3) = vtu_slope + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diagnostics were requested but will NOT' + print *,' >< be performed for this time level since we ' + print *,' >< are either at the first time level for a ' + print *,' >< genesis case (type = midlat or tcgen), or' + print *,' >< we are at any time level in which for some' + print *,' >< reason the fixlon and fixlat at the' + print *,' >< previous time level are not defined.' + print *,' >< ifh= ',ifh + endif + + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' >< CPS diags were requested but will NOT be' + print *,' >< performed for this time level since we are at' + print *,' >< time level 1 for a genesis case ' + print *,' >< (type = midlat or tcgen) and we cannot' + print *,' >< diagnose the model direction of storm' + print *,' >< movement. ifh= ',ifh + endif + + endif + + endif + + 73 format ('cps_stats: ',a4,' lead time= ',i3,':',i2,' paramb= ' + & ,f8.2,' vtl= ',f9.2,' vtu= ',f9.2) + + + if (phasescheme == 'vtt' .or. phasescheme == 'both') then + call get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + + 631 format(1x,'#-------------------------------------------------#') + 633 format(1x,'# End of routine to determine cyclone phase... #') + 635 format(1x,'#-------------------------------------------------#') + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_paramb (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,paramb,maxstorm,igcpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines "Parameter B", which determines +c the degree of thermal symmetry between the "left" and "right" +c hemispheres of a storm, in the layer between 900 and 600 mb. +c We evaluate only those points that are within 500 km of the +c storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zthicksum(2) + real rlonc,rlatc,rlonb,rlatb,xdist,degrees,d,cosarg + real st_heading,st_heading_rad,ricps,dx,dy + real pt_dir,pt_dir_rad,zthick,hemval,paramb + real zthick_right_mean,zthick_left_mean + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer left_ct,right_ct,hemis,icount,maxstorm,ip + logical(1) valid_pt(imax,jmax) +c + ricps = 500.0 + +c ----------------------------------------------------------------- +c First, determine the angle that the storm took getting from the +c last position to the current one. If this is for ifh=1 for a +c regular type=tracker case, we will just use the storm direction +c as read from the tcvitals card. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + if (d == 0.0) then + + ! Storm is stationary... + st_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + print '(a43,f9.3)' + & ,' In get_cps_paramb, model storm heading = ' + & ,st_heading + print *,' ' + endif + +c ----------------------------------------------------------------- +c Now call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_paramb from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + igcpret = 92 + return + endif + +c ----------------------------------------------------------------- +c Now loop through all of the points of the subdomain. If the +c point is further than 500 km from the storm center, discard it. +c Otherwise, evaluate the angle from the storm center to this point +c to determine the hemisphere of the point, that is, if the point +c is to the left or the right of the storm track. +c ----------------------------------------------------------------- + + ! We will want to speed things up for finer resolution grids. + ! We can do this by skipping some of the points in the + ! loop for the evaluation of parameter B. + + if ((dx+dy)/2. > 0.20) then + bskip = 1 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then + bskip = 2 + else if ((dx+dy)/2. > 0.05 .and. (dx+dy)/2. <= 0.10) then + bskip = 3 + else if ((dx+dy)/2. > 0.03 .and. (dx+dy)/2. <= 0.05) then + bskip = 5 + else if ((dx+dy)/2. <= 0.03) then + bskip = 10 + endif + + left_ct = 0 + right_ct = 0 + zthicksum = 0 + icount = 0 + +c print *,'CPS CORE: ibeg= ',ibeg,' iend= ',iend +c print *,'CPS CORE: jbeg= ',jbeg,' jend= ',jend + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + icount = icount + 1 + +c print *,'CPS CORE: ist= ',ist,' ifh= ',ifh,' j= ',j,' i= ',i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_paramb, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Parameter B will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_paramb' + print *,'!!! for a non-global grid.' + print *,'!!! Parameter B will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_PARAMB....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon= ',glon(ip),' glat= ',glat(j) + print *,'!!! Parameter B will not be computed.' + print *,'!!! EXITING GET_CPS_PARAMB....' + print *,' ' + endif + + paramb = -9999.99 + igcpret = 95 + return + endif + + !---------------------------------------------------------- + ! Calculate angle from storm center to point, in a 0-360 + ! framework, clockwise positive. + !---------------------------------------------------------- + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-fixlon(ist,ifh)) * dtr + rlatb = fixlat(ist,ifh) * dtr + d = degrees * dtr + + if (d > 0.) then + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_dir_rad = acos(cosarg) + else + pt_dir_rad = 2*pi - acos(cosarg) + endif + else + pt_dir_rad = 0.0 + endif + + pt_dir = pt_dir_rad / dtr + + !------------------------------------------------------------ + ! Based on the angle that the point is from the storm center, + ! determine if the point is to the left or the right of the + ! storm track. + !------------------------------------------------------------ + + if (st_heading >= 180.0) then + if ((st_heading - pt_dir) > 0.0 .and. + & (st_heading - pt_dir) <= 180) then + hemis = 2 + left_ct = left_ct + 1 + else + hemis = 1 + right_ct = right_ct + 1 + endif + else + if ((pt_dir - st_heading) > 0.0 .and. + & (pt_dir - st_heading) <= 180) then + hemis = 1 + right_ct = right_ct + 1 + else + hemis = 2 + left_ct = left_ct + 1 + endif + endif + + !------------------------------------------------------------ + ! Calculate the 600-900 mb thickness at this point and add + ! the thickness value to the array for the correct "storm + ! hemisphere". + !------------------------------------------------------------ + + zthick = cpshgt(ip,j,7) - cpshgt(ip,j,1) + zthicksum(hemis) = zthicksum(hemis) + zthick + + if ( verb .ge. 3 ) then + write (6,51) rlonb/dtr,rlatb/dtr,rlonc/dtr,rlatc/dtr + & ,st_heading,pt_dir,hemis,zthick + endif + + enddo iloop + enddo jloop + + 51 format (1x,'stlon stlat = ',2(f6.2,2x),' ptlon ptlat = ' + & ,2(f6.2,2x),' sthead= ',f6.2,' ptdir= ',f6.2,' hemis= ' + & ,i1,' zthick= ',f7.2) + +c ------------------------------------------------------------------ +c Now calculate parameter B. The hemval parameter = +1 for storms +c in the Northern Hemisphere and -1 for Southern Hemisphere storms. +c ------------------------------------------------------------------ + + zthick_right_mean = zthicksum(1) / float(right_ct) + zthick_left_mean = zthicksum(2) / float(left_ct) + + if (fixlat(ist,ifh) < 0.0) then + hemval = -1.0 + else + hemval = 1.0 + endif + + paramb = hemval * (zthick_right_mean - zthick_left_mean) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_cps_paramb, lead time= ',ifhours(ifh),':' + & ,ifclockmins(ifh) + & ,' ',storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' right_ct= ',right_ct,' left_ct= ',left_ct + print *,' zthicksum(1)= ',zthicksum(1) + print *,' zthicksum(2)= ',zthicksum(2) + print *,' zthick_right_mean= ',zthick_right_mean + print *,' zthick_left_mean= ',zthick_left_mean + print *,' hemval= ',hemval + print *,' END of get_cps_paramb, paramb= ',paramb + endif + +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_cps_vth (imax,jmax,inp,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,clayer,vth_slope,maxstorm,igcvret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. For Hart's cyclone phase +c space, this subroutine determines the thermal wind profile for +c either the lower troposphere (i.e., between 600 and 900 mb) or the +c upper troposphere (i.e., between 300 and 600 mb). We evaluate +c only those points that are within 500 km of the storm center. + + USE inparms; USE phase; USE set_max_parms; USE trig_vals + USE grid_bounds; USE tracked_parms; USE def_vitals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + character clayer*5 + real tmp1,tmp2,tmp3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real zmax(7),zmin(7),zdiff(7),xlolevs(7),xhilevs(7),plev(7) + real dlnp(7),dzdlnp(7),dz(7),lnp(7) + real vth_slope,xdist,degrees,d,cosarg + real ricps,dx,dy,R2 + integer imax,jmax,igpret,igcpret,ist,ifh,npts,bskip,i,j,k,kix + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igcvret,igiret + integer kbeg,kend,maxstorm,ip + logical(1) valid_pt(imax,jmax) + + data xlolevs /900.,850.,800.,750.,700.,650.,600./ + data xhilevs /600.,550.,500.,450.,400.,350.,300./ +c data xlolevs /90000.,85000.,80000.,75000.,70000.,65000.,60000./ +c data xhilevs /60000.,55000.,50000.,45000.,40000.,35000.,30000./ +c + ricps = 500.0 + plev = 0.0 + + if (clayer == 'lower') then + kbeg = 1 + kend = 7 + plev = xlolevs + else + kbeg = 7 + kend = 13 + plev = xhilevs + endif + +c ----------------------------------------------------------------- +c First, call get_ij_bounds to get the boundaries for a smaller +c subdomain, or subset of gridpoints, in which to evaluate the +c parameter B statistic. We will only include points within +c 500 km of the storm center for evaluation. +c ----------------------------------------------------------------- + + npts = ceiling(ricps/(dtk*(dx+dy)/2.)) + + call get_ij_bounds (npts,0,ricps,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_cps_vtl from call to' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + igcvret = 92 + return + endif + +c ------------------------------------------------------------------ +c Now loop through all of the points of the subdomain at each level. +c If a point is further than 500 km from the storm center, discard +c it. Otherwise, evaluate the gp height at the point to determine +c if it is a max or a min for the given level. Store the max and +c min height at each level in an array. +c ------------------------------------------------------------------ + +c ! We will want to speed things up for finer resolution grids. +c ! We can do this by skipping some of the points in the +c ! loop for the evaluation of parameter B. +c +c if ((dx+dy)/2. > 0.20) then +c bskip = 1 +c else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.20) then +c bskip = 2 +c else if ((dx+dy)/2. <= 0.10) then +c bskip = 3 +c endif + + bskip = 1 ! Don't do any skipping for now.... + + zmax = -9999999.0 + zmin = 9999999.0 + zdiff = 0.0 + lnp = 0.0 + + levloop: do k = kbeg,kend + + if (kbeg == 7) then + ! processing upper layers (600-300 mb) + kix = k - 6 + else + ! processing lower layers (900-600 mb) + kix = k + endif + + lnp(kix) = log(plev(kix)) + + jloop: do j=jbeg,jend,bskip + iloop: do i=ibeg,iend,bskip + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_cps_vth, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_cps_vth' + print *,'!!! for a non-global grid.' + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! i= ',i + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + endif + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh),glon(ip) + & ,glat(j),xdist,degrees) + + if (xdist > ricps) cycle iloop + + if (valid_pt(ip,j)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! UNDEFINED PT OUTSIDE OF GRID ' + print *,'!!! IN GET_CPS_VTH....' + print *,'!!! i= ',i,' ip= ',ip,' j= ',j,' k= ',k + & ,' clayer= ',clayer + print *,'!!! fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,'!!! glon(ip)= ',glon(ip),' glat= ',glat(j) + print *,'!!! Thermal wind parm will not be computed.' + print *,'!!! EXITING GET_CPS_VTH....' + print *,' ' + endif + + vth_slope = -9999.99 + igcvret = 95 + return + endif + + tmp1 = zmax(kix) + tmp2 = cpshgt(ip,j,k) + tmp3 = zmin(kix) + + zmax(kix) = max(tmp1,tmp2) + zmin(kix) = min(tmp3,tmp2) + +c zmax(kix) = max(zmax(kix),cpshgt(ip,j,k)) +c zmin(kix) = min(zmin(kix),cpshgt(ip,j,k)) + + enddo iloop + enddo jloop + + zdiff(kix) = zmax(kix) - zmin(kix) + + enddo levloop + +c ------------------------------------------------------------------ +c Now calculate the vertical derivative of the gp height, that is, +c d(dz)/d(ln(p)). Here, zdiff is the gp height perturbation at a +c given level, calculated in the loop above; dz is the vertical +c change in that perturbation from one level to the next. +c ------------------------------------------------------------------ + + dz = 0.0 + dlnp = 0.0 + dzdlnp = 0.0 + + do k = 2,7 + dz(k) = zdiff(k) - zdiff(k-1) + dlnp(k) = log(plev(k)) - log(plev(k-1)) + dzdlnp(k) = dz(k) / dlnp(k) + enddo + +c ------------------------------------------------------------------ +c Now call a correlation routine to get the slope of a regression +c line. The independent variable that we input is dlnp, the change +c in log of pressure with height. The dependent variable is +c dzdlnp, the vertical change in the height perturbation with +c respect to the change in pressure. The slope that is returned +c defines whether we've got a cold core or warm core system. +c See Hart (MWR, April 2003, Vol 131, pp. 585-616) for more +c details, specifically his Fig. 3 and the discussion surrounding. +c Note that in the call to calccorr, we are sending only 6 of the +c 7 elements of the dlnp and dzdlnp arrays, beginning with the +c 2nd element of each. That's because the first array value for +c each of those arrays is empty, since in the loop just above, we +c start with kbeg+1, not kbeg. +c ------------------------------------------------------------------ + + call calccorr(lnp(2),zdiff(2),6,R2,vth_slope) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ In get_cps_vth, values for vth follow for ' + & ,'lead time= ',ifhours(ifh),':',ifclockmins(ifh),' ' + & ,storm(ist)%tcv_storm_id,' ',storm(ist)%tcv_storm_name + print *,' ... clayer = ',clayer + print *,' ' + endif + + do k = kbeg,kend + + if (kbeg == 7) then + kix = k - 6 + else + kix = k + endif + + if ( verb .ge. 3 ) then + print *,' ' + write (6,31) k,plev(kix),zmax(kix),zmin(kix),zdiff(kix) + if (kix > 1) then + write (6,32) plev(kix),log(plev(kix)) + & ,plev(kix-1),log(plev(kix-1)) + write (6,33) dz(kix),dlnp(kix),dzdlnp(kix) + else + write (6,34) + endif + endif + + enddo + + 31 format (1x,' +++ k= ',i2,' press= ',f8.1,' zmax= ',f7.2 + & ,' zmin= ',f7.2,' zdiff= ',f7.2) + 32 format (1x,' ln(',f7.1,')= ',f9.6,' ln(',f7.1,')= ',f9.6) + 33 format (1x,' dz= ',f10.2,' dlnp= ',f13.6,' dzdlnp= ',f12.3) + 34 format (1x,' --- First level... no derivatives done...') +c + return + end +c +C---------------------------------------------------- +C +C---------------------------------------------------- + subroutine calccorr(xdat,ydat,numpts,R2,slope) +c +c This subroutine is the main driver for a series of +c other subroutines below this that will calculate the +c correlation between two input arrays, xdat and ydat. +c +c INPUT: +c xdat array of x (independent) data points +c ydat array of y (dependent) data points +c numpts number of elements in each of xdat and ydat +c +c OUTPUT: +c R2 R-squared, the coefficient of determination +c slope Slope of regression line +c +c xdiff array of points for xdat - xmean +c ydiff array of points for ydat - ymean +c yestim array of regression-estimated points +c yresid array of residuals (ydat(i) - yestim(i)) + + USE verbose_output + + implicit none + + real xdat(numpts),ydat(numpts) + real xdiff(numpts),ydiff(numpts) + real yestim(numpts),yresid(numpts) + real xmean,ymean,slope,yint,R2 + integer numpts,i + +c + call getmean(xdat,numpts,xmean) + call getmean(ydat,numpts,ymean) +c + call getdiff(xdat,numpts,xmean,xdiff) + call getdiff(ydat,numpts,ymean,ydiff) +c + call getslope(xdiff,ydiff,numpts,slope) + yint = ymean - slope * xmean +c + call getyestim(xdat,slope,yint,numpts,yestim) + call getresid(ydat,yestim,numpts,yresid) +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * CPS Thermal wind regression details * ' + print *,' *--------------------------------------------------* ' + endif + + call getcorr(yresid,ydiff,numpts,R2) + + if ( verb .ge. 3 ) then + print *,' i ydat xdat ydiff xdiff e' + & ,' e2 ydiff2' + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + do i = 1,numpts + write(6,'(2x,i3,2x,f7.2,2x,f7.4,2x,f7.2,2x,f7.4,3(2x,f7.2))') + & i,ydat(i),xdat(i),ydiff(i) + & ,xdiff(i),yresid(i),yresid(i)*yresid(i) + & ,ydiff(i)*ydiff(i) + enddo + + print *,' ---- ----- ----- ----- ----- ----- ' + & ,' ----- -----' + print *,' ' + write (6,'(1x,a13,f9.3,3x,a5,f7.2)') ' means: y: ',ymean + & ,' x: ',xmean + + write (6,*) ' ' + write (6,30) 'slope= ',slope,' y-intercept = ',yint + 30 format (2x,a7,f10.3,a23,f10.3) + if (slope .gt. 0.0) then + write(6,40) 'Regression equation: Y = ',yint,' + ',slope + else + write(6,40) 'Regression equation: Y = ',yint,' - ' + & ,abs(slope) + endif + 40 format (2x,a27,f8.2,a3,f8.2,'X') +c + print *,' ' + write (6,'(1x,a17,f7.4,5x,a7,f7.4)') ' R2(r_squared) = ',R2 + & ,' r = ',sqrt(R2) + print *,' ' + print *,' *--------------------------------------------------* ' + print *,' * End of regression details * ' + print *,' *--------------------------------------------------* ' + endif + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getmean(xarr,inum,zmean) +c +c This subroutine is part of the correlation calculation, +c and it simply returns the mean of the input array, xarr. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c +c OUTPUT: +c zmean mean of data values in xarr + + implicit none + + real xarr(inum) + real xsum,zmean + integer i,inum +c + xsum = 0.0 + do i = 1,inum + xsum = xsum + xarr(i) + enddo +c + zmean = xsum / float(MAX(inum,1)) +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getdiff(xarr,inum,zmean,zdiff) +c +c This subroutine is part of the correlation calculation, +c and it returns in the array zdiff the difference values +c between each member of the input array xarr and the +c mean value, zmean. +c +c INPUT: +c xarr input array of data points +c inum number of data points in xarr +c zmean mean of input array (xarr) +c +c OUTPUT: +c zdiff array containing xarr(i) - zmean + + implicit none + + real xarr(inum),zdiff(inum) + real zmean + integer i,inum +c + do i = 1,inum + zdiff(i) = xarr(i) - zmean + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + + subroutine getslope(xarr,yarr,inum,slope) +c +c This subroutine is part of the correlation calculation, +c and it returns the slope of the regression line. +c +c INPUT: +c xarr input array of xdiffs (x - xmean) +c yarr input array of ydiffs (y - ymean) +c inum number of points in x & y arrays +c +c OUTPUT: +c slope slope of regression line + + real xarr(inum),yarr(inum) + real slope,sumxy,sumx2 + integer i,inum + +c First sum up the xarr*yarr products.... + + sumxy = 0.0 + do i = 1,inum + sumxy = sumxy + xarr(i) * yarr(i) + enddo + +c Now sum up the x-squared terms.... + + sumx2 = 0.0 + do i = 1,inum + sumx2 = sumx2 + xarr(i) * xarr(i) + enddo + +c Now get the slope.... + + slope = sumxy / sumx2 + + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getyestim(xarr,slope,yint,inum,yestim) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the predicted y-values using the +c regression equation that has been calculated. +c +c INPUT: +c xarr array of x data points +c slope slope of the calculated regression line +c yint y-intercept of the calculated regression line +c inum number of input points +c +c OUTPUT: +c yestim array of y pts estimated from regression eqn. + + implicit none + + real xarr(inum),yestim(inum) + real slope,yint + integer i,inum +c + do i = 1,inum + yestim(i) = yint + xarr(i) * slope + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getresid(yarr,yestim,inum,yresid) +c +c This subroutine is part of the correlation calculation, +c and it calculates all the residual values between the +c input y data points and the y-estim predicted y values. +c +c INPUT: +c yarr array of y data points +c yestim array of y pts estimated from regression eqn. +c inum number of input points +c +c OUTPUT: +c yresid array of residuals (ydat(i) - yestim(i)) + + implicit none + + real yarr(inum),yestim(inum),yresid(inum) + integer i,inum +c + do i = 1,inum + yresid(i) = yarr(i) - yestim(i) + enddo +c + return + end + +c-------------------------------------------c +c c +c-------------------------------------------c + subroutine getcorr(yresid,ydiff,inum,R2) +c +c This subroutine is part of the correlation calculation, +c and it does the actual correlation calculation. +c +c INPUT: +c yresid array of residuals (ydat(i) - yestim(i)) +c ydiff array of points for ydat - ymean +c inum number of points in the arrays +c +c OUTPUT: +c R2 R-squared, the coefficient of determination + + USE verbose_output + + implicit none + + real yresid(inum),ydiff(inum) + real R2,sumyresid,sumydiff + integer i,inum +c + sumyresid = 0.0 + sumydiff = 0.0 + + do i = 1,inum + sumyresid = sumyresid + yresid(i) * yresid(i) + sumydiff = sumydiff + ydiff(i) * ydiff(i) + enddo + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,30) 'Sum of y-residuals squared (e2) = ',sumyresid + write (6,30) 'Sum of y-diffs squared (ydiff2) = ',sumydiff + write (6,*) ' ' + 30 format (1x,a35,f15.2) + endif + +c if (sumydiff == 0.0) then +c R2=1.0 +c else +c R2 = 1 - sumyresid / sumydiff +c endif +c PENG 05/14/2018 Bug-fixed for R2 calculation with FENS job crashed. + if (sumyresid .lt. sumydiff) then + if (sumydiff .le. 0.000001) then + R2 = 1.0 + else + R2 = 1 - sumyresid / sumydiff + endif + else + R2=0.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_vtt_phase (inp,imax,jmax,dx,dy,ist,ifh,trkrinfo + & ,fixlon,fixlat,valid_pt,maxstorm,wcore_flag,igvpret) +c +c ABSTRACT: This subroutine is part of the algorithm for determining +c the structure, or phase, of a cyclone. Here, we are only looking +c at the mid-to-upper tropospheric warm anomaly at the center of +c the storm. The temperature data that we are searching through in +c the tmean array should be the 300-500 mb mean temperature data. +c The criteria in this algorithm are based loosely on Vitart's +c criteria for warm core checking, but the nuts & bolts of the +c subroutine use algorithms from this tracker, including the barnes +c analysis. First, we locate the warm core with the find_maxmin +c routine. Then we use the check_closed_contour routine to see if +c there is a closed temperature contour surrounding the warm core. +c +c INPUT: +c inp +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c inp contains input date and model number information +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c ist integer storm number (internal to the tracker) +c ifh integer index for lead time +c trkrinfo derived type containing grid info on user boundaries +c fixlon array containing found fix longitudes +c fixlat array containing found fix latitudes +c valid_pt Logical; bitmap indicating if valid data at that pt. +c maxstorm maximum # of storms to be handled +c +c OUTPUT: +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c igvpret Return code for this subroutine. +c +c LOCAL: +c wcore_mean_val barnes-averaged value of the temperature at the +c location where the tracker found the warm core. +c wcore_point_max max temperature found at a gridpoint near the +c location where the tracker found the warm core using +c barnes analysis. + + USE set_max_parms; USE grid_bounds; USE trkrparms; USE contours + USE tracked_parms; USE gen_vitals; USE def_vitals; USE inparms + USE phase + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo,wcore_trkrinfo + type (cint_stuff) wcore_contour_info + type (datecard) inp + + character*1 get_last_contour_flag,wcore_flag + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dx,dy,wcore_mean_val,wcore_mean_lon,wcore_mean_lat + real wcore_point_max,tlastcont,rlastcont,tlastout,rlastout + integer imax,jmax,igvpret,ist,ifh,npts,bskip,i,j + integer ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret + integer icount,maxstorm,ip,ifmret,ifilret,ifix,jfix,icccret + integer num_check_conts + logical(1) valid_pt(imax,jmax),compflag,wcore_mask(imax,jmax) + logical(1) output_file_open +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of get_vtt_phase *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for warm core at hour ',i4,':',i2.2) + write (6,103) wcore_depth + 103 format (1x,'* Warm core depth threshold (wcore_depth) = ',f7.2) + print *,'*-------------------------------------------------*' + endif + +c ------------------------------------------------------------ + wcore_mask = .false. + wcore_mean_lon = -999.0 + wcore_mean_lat = -999.0 + wcore_trkrinfo = trkrinfo ! set equal to values from trkrinfo... + wcore_trkrinfo%contint = wcore_depth ! ...except use the warm + ! core contour interval specified by + ! the user in the extrkr.sh script. + +c ------------------------------------------------------------ +c First, call find_maxmin to locate the warm core + + call find_maxmin (imax,jmax,dx,dy,'tmp' + & ,tmean,'max',ist,fixlon(ist,ifh),fixlat(ist,ifh) + & ,glon,glat,valid_pt,trkrinfo,compflag + & ,wcore_mean_lon,wcore_mean_lat,wcore_mean_val + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + + if (verb .ge. 3) then + print *,' ' + print *,'After call to find_maxmin for wcore, ifmret= ',ifmret + print *,' wcore_mean_val= ',wcore_mean_val + endif + +c ------------------------------------------------------------ +c Once find_maxmin returns a value and a location for the +c barnes-averaged value of a warm core, then make a call to +c fix_latlon_to_ij to (1) get the actual gridpoint value of the +c temperature (the value stored in wcore_mean_val is an +c area-averaged value coming from the barnes analysis), and +c (2) to get the (i,j) indeces for this gridpoint to be used in +c the call to check_closed_contour below. + + if (wcore_mean_lat > -99.0 .and. wcore_mean_lon > -990.0) then + call fix_latlon_to_ij (imax,jmax,dx,dy,tmean,'max' + & ,valid_pt,wcore_mean_lon,wcore_mean_lat + & ,wcore_mean_val,ifix,jfix,wcore_point_max,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Warm core stats: ' + write (6,105) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_mean_lon,360.-wcore_mean_lon + & ,wcore_mean_lat,wcore_mean_val + write (6,106) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,ifix,jfix,wcore_point_max + endif + + else + ! Search went out of regional grid bounds.... + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN get_vtt_phase. The call to ' + print *,'!!! fix_latlon_to_ij returned a non-zero return ' + print *,'!!! code, which means that the search for the fix' + print *,'!!! i and j went out of bounds for a regional ' + print *,'!!! grid. This should have been caught in a ' + print *,'!!! previous call to find_maxmin for one of the ' + print *,'!!! various fix parms. In any event, we will not' + print *,'!!! search for a warm core for this storm and ' + print *,'!!! lead time.' + print *,' ' + write (6,115) storm(ist)%tcv_storm_id + & ,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,'U',-999.99,-9999.99 + endif + + igvpret = 95 + wcore_flag = 'u' + return + endif + endif + + 105 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' mean_lon: ',f7.2,'E' + & ,1x,'(',f7.2,'W)',2x,'mean_lat: ',f7.2,2x + & ,'wcore_mean_val(K): ',f12.3) + 106 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2,' ifix: ',i5,2x + & ,' jfix: ',i5,2x,'wcore_point_max(K): ',f12.3) + + +c ------------------------------------------------------------ +c The Vitart scheme specifies that the temperature must decrease +c by at least 1.0C in all directions from the warm core center +c within a distance of 8 deg. A rigorous check of this criterion +c is performed here by utilizing the check_closed_contour routine. +c If we have a closed contour in the temperature field +c surrounding the warm core (using a 1 deg K interval), that +c criterion is satisfied. For diagnostic purposes, we set the +c value of num_check_conts to 999 in order to keep searching for +c all contours surrounding the warm core, and this allows us to +c get an idea of the "depth" or magnitude of the warm core when +c the tlastcont and rlastcont values are returned. + + wcore_contour_info%numcont = maxconts + num_check_conts = 999 + + get_last_contour_flag = 'y' + call check_closed_contour (imax,jmax,ifix,jfix,tmean + & ,valid_pt,wcore_mask,wcore_flag,'max',wcore_trkrinfo + & ,num_check_conts,wcore_contour_info,get_last_contour_flag + & ,tlastcont,rlastcont,icccret) + + if (wcore_flag == 'y') then + tlastout = tlastcont + rlastout = rlastcont/0.539638 + else + tlastout = -999.0 + rlastout = -9999.0 + endif + + if ( verb .ge. 3 ) then + write (6,115) storm(ist)%tcv_storm_id,gstorm(ist)%gv_gen_date + & ,gstorm(ist)%gv_gen_fhr,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + & ,ifhours(ifh),ifclockmins(ifh) + & ,wcore_flag,tlastout,rlastout + + 115 format (1x,' wcore: ',a4,1x,i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3,2x,i4,':',i2.2 + & ,' wcore_flag= ',a1,2x,' Temp of last contour(K) = ' + & ,f7.2,2x,'Radius of last contour(km) = ',f8.2) + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_sfc_center (xmeanlon,xmeanlat,clon + & ,clat,ist,ifh,calcparm,xsfclon,xsfclat + & ,maxstorm,igscret) +c +c ABSTRACT: This subroutine computes a modified lat/lon fix position +c to use as the input center position for the subroutines that +c follow which calculate surface-wind related values. The reason +c for this is that since we are concerned with the positioning of +c low-level wind features (e.g., rmax), we want the center position +c to be based solely on low-level features. We'll use mslp and the +c min in the sfc wind speed. If a center fix was unable to be made +c at this forecast hour for mslp and low-level winds, then we will +c stick with just using the mean position we got using all the other +c parameters. +c +c INPUT: +c xmeanlon The mean center longitude computed from all the various +c parameter fixes found in array clon +c xmeanlat The mean center latitude computed from all the various +c parameter fixes found in array clat +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Index for storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c (if a parameter fix could not be made at this forecast +c hour, then calcparm is set to false for this time for +c that parameter). +c maxstorm Maximum number of storms that can be tracked +c +c OUTPUT: +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c igscret Return code from this subroutine + + USE set_max_parms + USE verbose_output + + implicit none + + integer ist,ifh,ipct,igscret,maxstorm + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real xmeanlon,xmeanlat + real xsfclon,xsfclat,xlonsum,xlatsum + logical(1) calcparm(maxtp,maxstorm) + + ipct = 0 + xlonsum = 0.0 + xlatsum = 0.0 + + ! Do NOT include MSLP for the surface center at this time. +c if (calcparm(9,ist)) then +c ipct = ipct + 1 +c xlonsum = xlonsum + clon(ist,ifh,9) +c xlatsum = xlatsum + clat(ist,ifh,9) +c endif + + if (calcparm(10,ist)) then +c ! NOTE: Put double weighting on surface wind center if +c ! the tracker was able to find a center for it.... +c ipct = ipct + 2 +c xlonsum = xlonsum + 2.*clon(ist,ifh,10) +c xlatsum = xlatsum + 2.*clat(ist,ifh,10) + ! Just use single weighting for the sfc wcirc fix + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,10) + xlatsum = xlatsum + clat(ist,ifh,10) + endif + + if (calcparm(11,ist)) then + ! This is for the sfc vorticity center.... + ipct = ipct + 1 + xlonsum = xlonsum + clon(ist,ifh,11) + xlatsum = xlatsum + clat(ist,ifh,11) + endif + + if (ipct > 0) then + xsfclon = xlonsum / float(ipct) + xsfclat = xlatsum / float(ipct) + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In get_fract_wind_cov, CANNOT get modified fix ' + print *,'!!! position because the parameter fixes for mslp' + print *,'!!! and the sfc winds could not be obtained at this' + print *,'!!! forecast hour. ist= ',ist,' ifh= ',ifh + print *,'!!! We will use the fixlon and fixlat values for' + print *,'!!! this forecast hour.' + endif + + xsfclon = xmeanlon + xsfclat = xmeanlat + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ In get_sfc_center, modified fix (mslp + sfc_winds)' + print *,'+++ position follows: ' + print *,'+++ ' + print *,'+++ mslp: lon: ',clon(ist,ifh,9),' lat: ' + & ,clat(ist,ifh,9) + print *,'+++ sfc_winds: lon: ',clon(ist,ifh,10),' lat: ' + & ,clat(ist,ifh,10) + print *,'+++ sfc_vorticity: lon: ',clon(ist,ifh,11),' lat: ' + & ,clat(ist,ifh,11) + print *,'+++ multi-parm mean: lon: ',xmeanlon,' lat: ' + & ,xmeanlat + print *,'+++ sfc-only mean: lon: ',xsfclon,' lat: ',xsfclat + endif + + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_structure (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,er_wind,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm + & ,trkrinfo,igwsret) +c +c ABSTRACT: This subroutine is a driver subroutine for +c determining the structure of the low level winds of a cyclone. +c The algorithm will search out at specified distances from the +c storm center along arcs in each quadrant of the storm, +c evaluating the winds every 15 degrees along the arc. In each +c arc, start 7.5 degrees in, then make stops at 22.5, 37.5, +c 52.5, 67.5, and 82.5 degrees. At each of those points, we +c will bilinearly interpolate the winds to the points along those +c arcs. Then we compute a quadrant average of the wing magnitude, +c as well as the mean Vt and Vr values. This will be done +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 earth-relative +c quadrants: NE, SE, SW and NW. For the storm-relative estimates, +c these mean values of the wind will be computed for the same +c relative quadrants (front-right, back-right, back-left, front- +c left, but with respect (positive clockwise) to the +c direction of storm motion. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated +c er_wind: Quadrant winds in earth-relative framework +c sr_wind: Quadrant winds in storm-relative framework +c er_vr: Quadrant radial winds in earth-relative framework +c sr_vr: Quadrant radial winds in storm-relative framework +c er_vt: Quadrant tangential winds in earth-relative framework +c sr_vt: Quadrant tangential winds in storm-relative framework + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,num_qtr_azim=6 + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igvtret,ipct,maxstorm,iazim,azimuth_ct + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,xsfclon,xsfclat,wmag,wmag_sum + real vr,vt,vr_sum,vt_sum + logical(1) valid_pt(imax,jmax) +c + data rdist/10.,25.,50.,75.,100.,125.,150.,200.,250.,300.,350. + & ,400.,450.,500./ + + igwsret = 0 + + er_wind = 0.0 + sr_wind = 0.0 + er_vr = 0.0 + er_vt = 0.0 + sr_vr = 0.0 + sr_vt = 0.0 + +c ----------------------------------------------------------------- +c Now determine the angle that the storm took getting from the +c last position to the current one. If this is the initial time, +c use the observed direction of motion from the TC Vitals. This +c may not match up with the model storm's initial direction of +c motion, but it is all we have available to us in order to get +c a heading estimate for the initial time. This storm heading +c information will be used for the storm-relative profiles. +c ----------------------------------------------------------------- + + if (ifh == 1) then + + st_heading = float(storm(ist)%tcv_stdir) + + else + + call calcdist(fixlon(ist,ifh),fixlat(ist,ifh) + & ,fixlon(ist,ifh-1),fixlat(ist,ifh-1),xdist,degrees) + + rlonc = (360.-fixlon(ist,ifh)) * dtr + rlatc = fixlat(ist,ifh) * dtr + rlonb = (360.-fixlon(ist,ifh-1)) * dtr + rlatb = fixlat(ist,ifh-1) * dtr + d = degrees * dtr + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d))/(sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + st_heading_rad = acos(cosarg) + else + st_heading_rad = 2*pi - acos(cosarg) + endif + + st_heading = st_heading_rad / dtr + + if ( verb .ge. 3 ) then + print *,' ' + print *,' In get_wind_structure, fhr= ',fhreal(ifh) + & ,' ',storm(ist)%tcv_storm_id + & ,' ',storm(ist)%tcv_storm_name + print '(a25,a23,f9.3)',' In get_wind_structure, ' + & ,' model storm heading = ',st_heading + print *,' ' + endif + + endif + +c ----------------------------------------------------------------- +c Get the profiles for the earth-relative coordinate system. +c Start with NE, then SE, SW, and NW. First go through +c radiusloop, which goes from one radial distance to the next, +c then do the quadloop, which goes through each quadrant, and +c then within each quadrant, the qtr_azimloop goes through for +c six points along an arc, spaced 15 degrees apart, starting at +c 7.5 degrees clockwise from the north. +c ----------------------------------------------------------------- + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *****************************************************' + print *,' Wind Structure: distbear bilin interp starts here.' + print *,' *****************************************************' + print *,' ' + endif + + radiusloop1: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- ER structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop1: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + ! In each quadrant, run through six points along an + ! arc and evaluate the winds. + + qtr_azimloop1: do iazim = 1,num_qtr_azim + + bear = ((iquad-1) * 90.) + ((iazim-1) * 15.) + 7.5 + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' earth-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,' ' + print '(5(a10,f7.2))',' sfclat= ',xsfclat + & ,' sfclon= ',xsfclon + & ,' rdist= ',rdist(idist),' targlat= ',targlat + & ,' targlon= ',targlon + print '(19x,a8,f7.2,35x,a9,f7.2)','sfclon= ',360.-xsfclon + & ,'targlon= ',360.-targlon + endif + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop1 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + er_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + er_vr(iquad,idist) = vr_sum / float(azimuth_ct) + er_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + er_wind(iquad,idist) = -999.0 + er_vr(iquad,idist) = -999.0 + er_vt(iquad,idist) = -999.0 + endif + + enddo quadloop1 + + enddo radiusloop1 + +c ----------------------------------------------------------------- +c Get the profiles for the storm-relative coordinate system. +c Start with the front-right quadrant and go clockwise through +c back-right, back-left and front-left. +c ----------------------------------------------------------------- + + radiusloop2: do idist = 1,numdist + + if ( verb .ge. 3 ) then + print *,'-- SR structure idist= ',idist,' rdist= ' + & ,rdist(idist) + endif + + quadloop2: do iquad = 1,4 + + if ( verb .ge. 3 ) then + print *,' structure iquad= ',iquad + endif + + wmag_sum = 0.0 + vr_sum = 0.0 + vt_sum = 0.0 + azimuth_ct = 0 + + qtr_azimloop2: do iazim = 1,num_qtr_azim + +c temp_bear = st_heading + ((iquad-1) * 90.) + 45. + + temp_bear = st_heading + ((iquad-1) * 90.) + & + ((iazim-1) * 15.) + 7.5 + bear = mod(temp_bear,360.) + + if ( verb .ge. 3 ) then + print *,' structure iazim= ',iazim + & ,' storm-relative bear= ',bear + endif + + call distbear (xsfclat,xsfclon,rdist(idist) + & ,bear,targlat,targlon) + + ! NOTE: The 1020 in the call here is just a number/code to + ! indicate to the subroutine to process sfc winds.... + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,1020,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + wmag = sqrt (xintrp_u**2 + xintrp_v**2) + wmag_sum = wmag_sum + wmag + call getvrvt (xsfclon,xsfclat,targlon,targlat + & ,xintrp_u,xintrp_v,vr + & ,vt,igvtret) + vr_sum = vr_sum + vr + vt_sum = vt_sum + vt + azimuth_ct = azimuth_ct + 1 + + if ( verb .ge. 3 ) then + print '(2x,a21,f8.2,a14,f8.2,2(a11,f8.2))' + & ,' intrp wind speed= ' + & ,wmag,' (in kts)= ',wmag*1.9427 + & ,' vr(m/s)= ',vr,' vt(m/s)= ',vt + endif + + endif + + enddo qtr_azimloop2 + + if (azimuth_ct > 0) then + ! Compute quadrant-azimuthally-averaged winds at + ! this distance + sr_wind(iquad,idist) = wmag_sum / float(azimuth_ct) + sr_vr(iquad,idist) = vr_sum / float(azimuth_ct) + sr_vt(iquad,idist) = vt_sum / float(azimuth_ct) + else + sr_wind(iquad,idist) = -999.0 + sr_vr(iquad,idist) = -999.0 + sr_vt(iquad,idist) = -999.0 + endif + + enddo quadloop2 + + enddo radiusloop2 +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_fract_wind_cov (imax,jmax,inp,dx,dy + & ,ist,ifh,fixlon,fixlat,xsfclon,xsfclat,valid_pt + & ,calcparm,wfract_cov,pdf_ct_bin,pdf_ct_tot,maxstorm + & ,trkrinfo,igfwret) +c +c ABSTRACT: This subroutine determines the fractional areal coverage +c of winds exceeding various thresholds within specified arcs +c (e.g., 200 km, 400 km, etc) in each quadrant of a storm. The bins +c that are used go as follows: (1) 0-100; (2) 0-200; (3) 0-300; +c (4) 0-400; (5) 0-500. +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c rdist The radii (km) at which winds will be evaluated +c +c Arrays: +c rdist Radii (km) at which the winds will be evaluated + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real rdist(numdist) + real wfract_cov(numquad+1,numbin,numthresh) + real area_total_quad_bin(numquad,numbin) + real area_exceed_quad_bin(numquad,numbin,numthresh) + real xintlon,xintlat + real :: windthresh(numthresh) = (/17.5,25.74,32.94/) + real dx,dy,bear,targlat,targlon,xintrp_u,xintrp_v,st_heading + real d,cosarg,rlonc,rlatc,rlonb,rlatb,st_heading_rad,degrees + real temp_bear,xdist,conv_ms_knots,vmagkts + real rads,ri,dell,vmag,xarea,grdintincr,xsfclon,xsfclat + real sum_exceed_area(numbin,numthresh) + real sum_total_area(numbin,numthresh) + integer pdf_ct_bin(16) + integer imax,jmax,igwsret,ist,ifh,iquad,idist,ibiret1,ibiret2 + integer igfwret,ipct,i,j,numinterp,ixoa,ixaa,iq,ib,it,ii + integer jlatfix,ilonfix,npts,ibeg,iend,jbeg,jend,ngridint,ni,nj + integer itret,igiret,idistbin,ipdfbin,pdf_ct_tot,maxstorm + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) + character got_pdf*6 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*5 :: cbin(5) = + & (/'0-100','0-200','0-300','0-400','0-500'/) + character*2 :: cthresh(3) = (/'34','50','64'/) +c + igfwret = 0 + conv_ms_knots = 1.9427 + rads = 500.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + + wfract_cov = 0.0 + area_total_quad_bin = 0.0 + area_exceed_quad_bin = 0.0 + sum_exceed_area = 0.0 + sum_total_area = 0.0 + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_fract_wind_cov from call to ' + print *,'!!! get_ij_bounds, stopping processing for storm' + print *,'!!! number ',ist + endif + + igfwret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_fract_wind_cov calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igfwret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_fract_wind_cov, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + +c When evaluating the winds at a gridpoint, keep in mind that each +c gridpoint represents area around it. There are 2 special cases +c we need to watch out for. The first is for cases in which the +c area of a gridpoint straddles across a distance threshold, so +c that some of the gridpoint's area is in the "<200" bin, while +c some is in the "<100" bin. The other is for the case in which +c the area of a gridpoint straddles between 2 adjacent quadrants +c (e.g., a gridpoint exactly to the north of the center would have +c half its area in the NW quadrant and half in the NE quadrant). +c +c To properly "partition" and assign gridpoint areas, we need to +c interpolate the current grid down to a fine resolution. +c +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the guidelines that +c will be used, keeping in mind that we want the final grid spacing +c to be on the order of between 0.05 and 0.10 degree (finer than +c 0.05 deg is superfluous, and coarser than 0.10 deg is too coarse). +c +c Original grid size (deg) # of interps +c ------------------------- ------------ +c 0.8 <= g 4 +c 0.4 <= g < 0.8 3 +c 0.2 <= g < 0.4 2 +c 0.1 <= g < 0.2 1 +c g < 0.1 0 + + + if ((dx+dy)/2. >= 0.8) then + numinterp = 4 + else if ((dx+dy)/2. < 0.8 .and. (dx+dy)/2. >= 0.4) then + numinterp = 3 + else if ((dx+dy)/2. < 0.4 .and. (dx+dy)/2. >= 0.2) then + numinterp = 2 + else if ((dx+dy)/2. < 0.2 .and. (dx+dy)/2. >= 0.1) then + numinterp = 1 + else + numinterp = 0 + endif + + grdintincr = (dx+dy)/2. + do i = 1,numinterp + grdintincr = 0.5 * grdintincr + enddo + +c Now loop through the points in this subdomain, determine if any +c are within 500 km of the center, and then determine what quadrant +c the point is in relative to the center, and then calculate the +c fractional area coverage for winds. + + pdf_ct_tot = 0 + pdf_ct_bin = 0 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_fract_wind_cov, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_fract_wind_cov' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the fractional wind coverage' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igfwret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle iloop ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > (rads+(0.75*((dx+dy)/2.)*dtk*cos(glat(j)*dtr)))) + & then + + ! If the distance is greater than "rads" (500 km at initial + ! writing) plus another 3/4 of a gridpoint, then cycle. + ! The extra 3/4 of a gridpoint is to allow for the case of + ! some portion of the area around a gridpoint (whose + ! center point > 500 km) being within the 500 km arc... + ! although that is only factored in for grids with spacing + ! >= 0.1 deg. For smaller grids, where no interpolation is + ! done in this subroutine, then the distance to that point + ! is considered representative and the point is ignored if + ! it is not less than 500 km from the center. + + cycle iloop + + else + + ! First interpolate the area surrounding each grid point to + ! get fine resolution of lats & lons for determining how to + ! partition the area of a gridpoint among quadrants as well + ! as among distance thresholds. + + vmag = sqrt (u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + vmagkts = vmag * conv_ms_knots + + if (numinterp > 0) then + + grdintincr = ((dx+dy)/2.) / 2**numinterp ! "grid spacing" + ! of interpolated grid + ngridint = (2**numinterp) / 2 + + got_pdf = 'notyet' + + njloop: do nj= ngridint,-ngridint,-1 + + xintlat = glat(j) + float(nj) * grdintincr + + niloop: do ni= -ngridint,ngridint + + xintlon = glon(ii) + float(ni) * grdintincr + + call calcdist (xintlon,xintlat,xsfclon + & ,xsfclat,xdist,degrees) + + if (xdist <= 350. .and. got_pdf == 'notyet') then + ! The got_pdf flag is needed because in these loops + ! for niloop & njloop, we are actually looking at + ! tiny areas around the same grid point. So we + ! want to make sure we only count each gridpoint + ! once. + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + got_pdf = 'got_it' + endif + + if (xdist < 500.) then + + ! Compute area of this fraction of a grid box + xarea = (grdintincr * 111195) * + & (grdintincr * 111195 + & * cos(xintlat * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Go through a loop of the bins. The purpose of + ! this is that these "bins" all go from the + ! the center out to a specified radius, they are + ! NOT 100-km wide bins. So if we are dealing with + ! a point at r = 250 km, then that falls in the + ! 0-300 km bin, but it also falls in the 0-400 and + ! 0-500 km bins as well. So we need to run through + ! this binloop multiple times to get the area data + ! into multiple bins. Here are the bins & indices: + ! 1: 0-100 km + ! 2: 0-200 km + ! 3: 0-300 km + ! 4: 0-400 km + ! 5: 0-500 km + + binloop: do ib = idistbin,numbin + + if (xintlon >= xsfclon .and. + & xintlat >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (xintlon >= xsfclon .and. + & xintlat < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (xintlon < xsfclon .and. + & xintlat >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop + + endif + + enddo niloop + + enddo njloop + + else + + ! In this else statement is the case for a grid whose + ! resolution is already fine enough that we don't need + ! to interpolate any further. For example, we will have + ! the H*Wind data on a 0.05 degree grid, so that's already + ! fine enough. + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat + & ,xdist,degrees) + + if (xdist <= 350.) then + ipdfbin = min((int(vmagkts / 10.) + 1),16) + pdf_ct_bin(ipdfbin) = pdf_ct_bin(ipdfbin) + 1 + pdf_ct_tot = pdf_ct_tot + 1 + endif + + if (xdist < 500.) then + + ! Compute area of this grid box + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + + idistbin = int(xdist / 100.) + 1 + + ! Why the binloop2? See explanation above in the "if" + ! part of this if-then block, where binloop is. + + binloop2: do ib = idistbin,numbin + + if (glon(ii) >= xsfclon .and. + & glat(j) >= xsfclat) then + + ! NE quadrant + + area_total_quad_bin(1,ib) = + & area_total_quad_bin(1,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(1,ib,1) = + & area_exceed_quad_bin(1,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(1,ib,2) = + & area_exceed_quad_bin(1,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(1,ib,3) = + & area_exceed_quad_bin(1,ib,3) + xarea + endif + + else if (glon(ii) >= xsfclon .and. + & glat(j) < xsfclat) then + + ! SE quadrant + + area_total_quad_bin(2,ib) = + & area_total_quad_bin(2,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(2,ib,1) = + & area_exceed_quad_bin(2,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(2,ib,2) = + & area_exceed_quad_bin(2,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(2,ib,3) = + & area_exceed_quad_bin(2,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) < xsfclat) then + + ! SW quadrant + + area_total_quad_bin(3,ib) = + & area_total_quad_bin(3,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(3,ib,1) = + & area_exceed_quad_bin(3,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(3,ib,2) = + & area_exceed_quad_bin(3,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(3,ib,3) = + & area_exceed_quad_bin(3,ib,3) + xarea + endif + + else if (glon(ii) < xsfclon .and. + & glat(j) >= xsfclat) then + + ! NW quadrant + + area_total_quad_bin(4,ib) = + & area_total_quad_bin(4,ib) + xarea + if (vmag > windthresh(1)) then + area_exceed_quad_bin(4,ib,1) = + & area_exceed_quad_bin(4,ib,1) + xarea + endif + if (vmag > windthresh(2)) then + area_exceed_quad_bin(4,ib,2) = + & area_exceed_quad_bin(4,ib,2) + xarea + endif + if (vmag > windthresh(3)) then + area_exceed_quad_bin(4,ib,3) = + & area_exceed_quad_bin(4,ib,3) + xarea + endif + + endif + + enddo binloop2 + + endif + + endif + + endif + + enddo iloop + + enddo jloop + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different quadrants, bins and thresholds... +c ------------------------------------------------- + + if ( verb .ge. 3 ) then + write (6,109) ' ' + & ,' ' + & ,' ' + write (6,109) ' Quadrant Bin Wind_Thresh ' + & ,'Fract_coverage (%) Area_exceeded' + & ,' Area_total' + write (6,109) ' -------- --- ----------- ' + & ,'------------------ -------------' + & ,' ----------' + write (6,109) ' ' + & ,' ' + & ,' ' + + do iq = 1,numquad + do ib = 1,numbin + do it = 1,numthresh + wfract_cov(iq,ib,it) = area_exceed_quad_bin(iq,ib,it) / + & area_total_quad_bin(iq,ib) + write (6,117) cquad(iq),cbin(ib),cthresh(it) + & ,wfract_cov(iq,ib,it)*100.0 + & ,area_exceed_quad_bin(iq,ib,it) + & ,area_total_quad_bin(iq,ib) + enddo + enddo + enddo + endif + + + 109 format (1x,a33,a37,a16) + 117 format (5x,a2,5x,a5,7x,a2,13x,f6.2,10x,f16.1,2x,f16.1) + +c ------------------------------------------------- +c Now compute the fractional wind coverage for all +c the different bins and thresholds, but for the +c entire "disc" of the storm, that is, summing all +c quadrants together. +c ------------------------------------------------- + + do it = 1,numthresh + do ib = 1,numbin + do iq = 1,numquad + sum_total_area(ib,it) = sum_total_area(ib,it) + & + area_total_quad_bin(iq,ib) + sum_exceed_area(ib,it) = sum_exceed_area(ib,it) + & + area_exceed_quad_bin(iq,ib,it) + enddo + wfract_cov(5,ib,it) = sum_exceed_area(ib,it) + & / sum_total_area(ib,it) + enddo + enddo + + if ( verb .ge. 3 ) then + do ib = 1,numbin + do it = 1,numthresh + write (6,117) 'TT',cbin(ib),cthresh(it) + & ,wfract_cov(5,ib,it)*100.0 + & ,sum_exceed_area(ib,it) + & ,sum_total_area(ib,it) + enddo + enddo + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_ike_stats (imax,jmax,inp,dx,dy,ist,ifh + & ,fixlon,fixlat,xsfclon,xsfclat,valid_pt,calcparm + & ,ike,sdp,wdp,maxstorm,trkrinfo,igisret) +c +c ABSTRACT: This subroutine computes the Integrated Kinetic Energy +c (IKE) and Storm Surge Damage Potential (SDP) values, based on +c Powell (BAMS, 2007). At this time, we are only computing the IKE +c values for TS threshold (17.5 m/s) and above. We are not yet +c computing wind damage potential (WDP) since, per Mark Powell +c (4/2008), he is currently re-formulating an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c sdp Storm surge damage potential + + USE inparms; USE phase; USE set_max_parms; USE tracked_parms + USE def_vitals; USE trig_vals; USE grid_bounds; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (datecard) inp + type (trackstuff) trkrinfo + + integer, parameter :: numdist=14,numquad=4 + integer npts,ipct,igisret,imax,jmax,ist,ifh,ilonfix,jlatfix + integer ibeg,jbeg,iend,jend,igiret,i,j,maxstorm,ii + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real ike(max_ike_cats) + real dx,dy,degrees,rads,ri,dell,xdist,vmag,xarea + real xsfclon,xsfclat,sdp,wdp + logical(1) calcparm(maxtp,maxstorm) + logical(1) valid_pt(imax,jmax) +c + igisret = 0 + ike = 0.0 + sdp = 0.0 + wdp = 0.0 + + rads = 400.0 + ri = 300.0 + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + +c Call get_ij_bounds in order to get the dimensions for a smaller +c subdomain of grid points to search over. + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,xsfclon,xsfclat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_ike_stats from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for storm ' + print *,'!!! number ',ist + endif + + igisret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, but our gridtype is' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping as we go.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the ibeg returned' + print *,'!!! from get_ij_bounds is < 1, and our gridtype is' + print *,'!!! NOT global, so we are going to abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_ike_stats calculating ibeg, iend,' + print *,'jbeg or jend. ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' imax= ',imax,' jmax= ',jmax + print *,'fractional wind coverage processing will not be ' + print *,'performed for this time.' + endif + + igisret = 94 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, but our gridtype' + print *,'!!! is global, so we are going to leave it as is ' + print *,'!!! and account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_ike_stats, the iend returned' + print *,'!!! from get_ij_bounds is > imax, and our gridtype' + print *,'!!! is NOT global, so we will abort the ' + print *,'!!! fractional wind coverage processing for' + print *,'!!! this time.' + print *,' ' + endif + + igisret = 94 + return + endif + endif + +c Search a grid of points near the storm center, evaluate if the +c storm is within the "rads" distance threshold. If so, compute +c the IKE values for all applicable thresholds (10, 18, 33 m/s). + + do j = jbeg,jend + do i = ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ii = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_ike_stats, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + else + ii = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ii = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in get_ike_stats' + print *,'!!! for a non-global grid. We will not ' + print *,'!!! perform the ike stats' + print *,'!!! processing for this storm & time.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + igisret = 94 + return + endif + endif + + if (.not. valid_pt(ii,j)) then + cycle ! Only an issue for regional grids + endif + + call calcdist (glon(ii),glat(j),xsfclon,xsfclat,xdist,degrees) + + if (xdist > rads) then + cycle + else + + vmag = sqrt(u(ii,j,levsfc)**2 + v(ii,j,levsfc)**2) + + if (vmag > 10.0) then + ! Add gridpoint to IKE_10. Compute area first... + xarea = (dy * 111195) * + & (dx * 111195 * cos(glat(j) * dtr)) + ike(1) = ike(1) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 18.0) then + ! Add gridpoint to IKE_ts. Area already computed for 10 + ike(2) = ike(2) + (0.5 * (vmag**2) * xarea) + endif + + if (vmag > 33.0) then + ! Add gridpoint to IKE_h. Area already computed for 10 + ike(3) = ike(3) + (0.5 * (vmag**2) * xarea) + endif + + endif + + enddo + enddo + + ike(1) = ike(1) * 1.e-12 ! Convert from J to TJ + ike(2) = ike(2) * 1.e-12 ! Convert from J to TJ + ike(3) = ike(3) * 1.e-12 ! Convert from J to TJ + +c Compute the storm surge damage potential (sdp) + + if (ike(2) >= 0.0) then + sdp = 0.676 + (0.43 * sqrt(ike(2))) + & - (0.0176 * ((sqrt(ike(2)) - 6.5)**2) ) + else + sdp = -99.0 + endif + +c Print out the IKE and SDP statistics... + + if ( verb .ge. 3 ) then + print *,' IKE_10 (storm energy) = ',ike(1) + print *,' IKE_TS (tropical storm) = ',ike(2) + print *,' IKE_H (hurricane) = ',ike(3) + print *,' SDP = ',sdp + endif + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine distbear (xlatin,xlonin,dist,bear,xlatt,xlont) +c +c ABSTRACT: Given an origin at latitude, longitude=xlato,xlono, +c this subroutine will locate a target point at a distance dist in +c km or nautical miles (depends on what you use for "rad_earth..." +c below), at bearing bear (degrees clockwise from north). +c Returns latitude xlatt and longitude xlont of target point. +c +c *** NOTE *** +c This subroutine was written to handle input lats & lons as this: +c All latitudes are in degrees, north positive and south negative. +c All longitudes are in degrees, west positive and east negative. +c *** **** *** +c +c However, for the longitudes, the rest of the tracker uses all +c 0-360 longitudes. Therefore, we need to convert the input lons +c and then once again convert the lons that are returned back to +c the calling routine. +c +c NOTE-- When origin is at north or south pole, bearing is no +c longer measured from north. Instead, bearing is measured +c clockwise from the longitude opposite that specified in xlono. +c Example-- if xlato=90., xlono=80., the opposite longitude is +c -100 (100 East), and a target at bearing 30. will lie on the +c -70. (70 East) meridian. +c +c AUTHOR: The core of this subroutine was written by Albion +c Taylor, another NOAA employee, in 1981. +c + USE trig_vals + + implicit none +c + real, parameter :: rad_earth_nm = 3440.170 ! radius of earth + real, parameter :: rad_earth_km = 6372.797 ! radius of earth + real xlato,xlono,dist,bear,xlatt,xlont,xlatin,xlonin + real cdist,sdist,clato,slato,clono,slono,cbear,sbear + real z,y,x,r,xlattz,xlontz,ddist,dbear,dxlato,dxlono +c + xlato = xlatin + xlono = xlonin + +cstr print *,' ' +cstr print *,'+++ At top of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlon= ',xlono,'E ',360.-xlono +cstr & ,'W xlat=',xlato +cstr print '(a6,f7.2,a8,f7.2)','dist= ',dist,' bear= ',bear + + if (xlono > 180.) then + ! Longitude input for this subroutine must be positive west + xlono = 360. - xlono + else + ! Longitude input for this subroutine must be negative east + xlono = -1. * xlono + endif + +cstr print '(a31,a8,f8.2)','After conversion for distbear, ' +cstr & ,' xlono= ',xlono + + ddist = dist + dbear = bear + dxlato = xlato + dxlono = xlono + + cdist = cos(ddist/rad_earth_km) + sdist = sin(ddist/rad_earth_km) + clato = cos(dtr*dxlato) + slato = sin(dtr*dxlato) + +cstr print *,'cdist= ',cdist,' sdist= ',sdist,' clato= ',clato +cstr & ,' slato= ',slato + + clono = cos(dtr*dxlono) + slono = sin(dtr*dxlono) + +cstr print *,'dxlono= ',dxlono,' clono= ',clono +cstr & ,' slono= ',slono + + cbear = cos(dtr*dbear) + sbear = sin(dtr*dbear) + +cstr print *,'cbear= ',cbear,' sbear= ',sbear + + z=cdist*slato + clato*sdist*cbear + y=clato*clono*cdist + sdist*(slono*sbear - slato*clono*cbear) + x=clato*slono*cdist - sdist*(clono*sbear + slato*slono*cbear) + +cstr print *,'z= ',z,' y= ',y,' x= ',x + + r = sqrt(x**2 + y**2) + +cstr print *,'r = sqrt(x**2 + y**2) = ',r + + xlattz = atan2(z,r)/dtr + +cstr print *,'xlattz = datan2(z,r)/dtr = ',xlattz + + xlatt = xlattz + + if (r <= 0.) go to 20 + + xlontz = atan2(x,y)/dtr + +cstr print *,'xlontz = atan2(x,y)/dtr = ',xlontz + +c xlont = xlontz + + ! Return the target longitude back to the calling routine + ! as a 0-360 positive east longitude.... + + xlont = mod(360.-xlontz,360.) + +c xlont = mod(360.+xlontz,360.) + +cstr print *,' ' +cstr print *,'At end of distbear....' +cstr print '(a6,f7.2,a3,f7.2,a9,f7.2)','xlont= ',xlont,'E ' +cstr ,360.-xlont,'W xlatt=',xlatt + + return + 20 xlont=0. +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_uneven (targlat,targlon,dx,dy + & ,imax,jmax,trkrinfo,level,cparm,xintrp_val,ibiret) +c +c ABSTRACT: This subroutine performs a bilinear interpolation to get +c a data value at a given lat/lon that may be anywhere within a box +c defined by the four surrouding grid points. In the diagram below, +c remember that for our grids we are using in the tracker, the +c latitude index starts at the north pole and increases southward. +c The point "X" indicates the target lat/lon location of the value +c for which we are bilinearly interpolating. The values to and ta +c below are ratios that determine how geographically close the +c target location is to the point of origin (pt.1 (i,j)) in terms +c of both longitude (to) and latitude (ta). +c +c +c pt.1 pt.2 +c (i,j) (i+1,j) +c +c +c +c X +c +c pt.4 pt.3 +c (i,j+1) (i+1,j+1) +c + + USE grid_bounds; USE tracked_parms; USE level_parms + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + character cparm*1 + real targlat,targlon,xintrp_val,dx,dy + real to,ta,d1,d2,d3,d4,z,eastlon + integer ie,iw,jn,js,ibiret,imax,jmax,level,nlev + + ibiret = 0 + +c -------------------------------------------------------------- +c For the latitudes and longitudes surrounding our target +c lat/lon location, convert the lat/lon values into i- and +c j-indices. +c -------------------------------------------------------------- + +c Find the j-indices for the points just to the north and the +c south of targlat.... + + if (targlat >= 0.0) then + ! For a northern hemisphere storm, jn is the j-index for the + ! point just to the *NORTH* (poleward) of targlat. + jn = int((glatmax - targlat)/dy + 1.) + js = jn + 1 + else + ! For a southern hemisphere storm, js is the j-index for the + ! point just to the *SOUTH* (poleward) of targlat. + js = ceiling((glatmax - targlat)/dy + 1.) + jn = js - 1 + endif + + ! Check to make sure that points are not being requested beyond + ! the northern or southern boundaries of the grid. This is most + ! likely to happen for a smaller, regional grid. + + if (jn > jmax .or. js > jmax) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jmax exceeded in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + + if (jn < 1 .or. js < 1) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: jn < 0 or js < 0 in subroutine ' + print *,'!!! bilin_int_uneven. Returning to calling ' + print *,'!!! routine after assigning wind value of -99.' + print *,'!!! jn= ',jn,' js= ',js,' jmax= ',jmax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + +c Find the i-indices for the points just to the east and the +c west of targlon.... + + ie = int((targlon - glonmin)/dx + 2.) + iw = ie - 1 + + ! Check for GM wrapping. Check ie to see if it is between the + ! most eastward gridpoint and the GM (i.e., on a 1-deg global + ! grid (360x181), it would be if targlon was between 359.0 (i=360) + ! and the GM (i=1, not i=361)). Similarly then, if we adjust ie + ! to then be 1, then we have a problem with iw, + ! since iw = 1 - 1 = 0. + + if (ie > imax) then + if (trkrinfo%gridtype == 'global') then + ie = ie - imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ie > imax in subroutine ' + print *,'!!! bilin_int_uneven for a non-global grid. ' + print *,'!!! Returning to calling routine after ' + print *,'!!! assigning missing wind value of -99.' + print *,'!!! ie= ',ie,' imax= ',imax + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + + if (iw < 1) then + if (trkrinfo%gridtype == 'global') then + iw = iw + imax + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: iw < 1 in subroutine bilin_int_uneven' + print *,'!!! for a non-global grid. Returning to calling ' + print *,'!!! routine after assigning missing wind value ' + print *,'!!! of -99. iw= ',iw + print *,' ' + endif + xintrp_val = -999.0 + ibiret = 85 + return + endif + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' +++ Interpolating winds for cparm= ',cparm +ctmwc print '(6x,4(a4,i3))','jn= ',jn,' js= ',js,' iw= ',iw,' ie= ',ie +ctmwc endif + +c ---------------------------------------------------------------- +c Calculate the longitude (to) and latitude (ta) location ratios. +c Check for GM wrapping, as we can run into a problem here if +c interpolating for points that are just west of the GM, since we +c would be interpolating using values of longitude just west of +c GM (say, glon(iw)=359.5) and the GM (glon(ie) = 0.0). This +c makes for an incorrect "to" ratio below, with 0-359.5 in the +c denominator. We have to account for this.... +c ---------------------------------------------------------------- + + if (glon(iw) > 300.0 .and. + & (glon(ie) < 10. .and. glon(ie) >= 0.)) then + eastlon = 360. - glon(ie) + else + eastlon = glon(ie) + endif + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,'glat(js)= ',glat(js),' glat(jn)= ',glat(jn) +ctmwc endif + + to = (targlon - glon(iw)) / (eastlon - glon(iw)) + ta = (targlat - glat(jn)) / (glat(js) - glat(jn)) + +c -------------------------------------------------------------- +c Copy the data values at the 4 known points into simple scalar +c variables +c -------------------------------------------------------------- + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cparm == 'u') then + d1 = u(iw,jn,nlev) + d2 = u(ie,jn,nlev) + d3 = u(ie,js,nlev) + d4 = u(iw,js,nlev) + else if (cparm == 'v') then + d1 = v(iw,jn,nlev) + d2 = v(ie,jn,nlev) + d3 = v(ie,js,nlev) + d4 = v(iw,js,nlev) + else if (cparm == 'm') then + d1 = lsmask(iw,jn) + d2 = lsmask(ie,jn) + d3 = lsmask(ie,js) + d4 = lsmask(iw,js) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in bilin_int_uneven.' + print *,'!!! Input cparm not recognized.' + print *,'!!! cparm= ',cparm + print *,'!!! EXITING....' + endif + + stop 95 + endif + + z = 1.9427 + +cstr print '(2x,4(a4,f8.2))',' d1= ',d1*z,' d2= ',d2*z +cstr & ,' d3= ',d3*z,' d4= ',d4*z + +c ------------------------------------------------------------- +c Compute the interpolated value +c ------------------------------------------------------------- + + xintrp_val = (1.-to) * (1.-ta) * d1 + & + to * (1.-ta) * d2 + & + to * ta * d3 + & + (1.-to) * ta * d4 + +cstr print '(2x,2(a11,f8.2))',' xintrp= ',xintrp_val,' (in kts)= ' +cstr & ,xintrp_val*z +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine sort_storms_by_pressure (gridprs,ifh,maxstorm,sortindex + & ,issret) +c +c ABSTRACT: This subroutine sorts storms by mslp. It is called by +c subroutine tracker just before the loop for "stormloop" is done +c for all the storms at a particular forecast hour. It is only +c called for the "midlat" and "tcgen" cases. The end result of +c this sort is an array (prsindex) that contains the indeces of +c the storms, arranged from lowest pressure to highest (and note +c that the "undefined" storms have a pressure of 9999.99 mb and +c thus get sorted to the bottom of the array). The purpose of +c doing this is so that we track the most intense storms first. +c Why go to the trouble? Imagine a scenario in which we are +c tracking a complex system in which there are 2 low pressure +c centers. Let's say that one is becoming dominant and +c intensifying, while the other is weakening. Now, let's assume +c that the weakening one eventually gets absorbed into the +c stronger, more dominant low. Now we only have 1 low, but if in +c the tracker stormloop, we first process the data for the +c weakening low, we will attribute the track to that storm, and +c then when we get to the point in the loop where we are trying +c to get the track for the stronger storm, we will (erroneously) +c stop the tracking for that storm since the storm center has +c already been attributed to the weaker storm. But by using this +c subroutine, we will track the stronger storm first, and thus +c avoid this problem. +c +c NOTE: The pressures used in the sort are those obtained at the +c previous forecast hour. At forecast hour = 0, just use the +c values as they were input to this routine, since they were +c found in first_ges_center from strongest to weakest already. +c +c INPUT: +c gridprs real array of storm mslp values +c ifh integer index for the current forecast hour +c maxstorm max num of storms that can be handled in this run +c +c OUTPUT: +c sortindex contains a sorted array of indeces. The orders +c sort routine does NOT rearrange the data. Rather, it +c returns this array of sorted indeces which point to +c the correct order of data values in the data array. +c issret return code from this subroutine +c + USE set_max_parms + USE verbose_output + + real, allocatable :: iwork(:) + real gridprs(maxstorm,maxtime) + integer ifh,maxstorm + integer sortindex(maxstorm) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: prstemp(:) +c + allocate (prstemp(maxstorm),stat=iva) + allocate (iwork(maxstorm),stat=iwa) + if (iva /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub sort_storms_by_pressure allocating' + print *,'!!! prstemp or iwork arrays: ' + print *,'!!! iva= ',iva,' iwa= ',iwa + endif + + STOP 94 + return + endif + + if (ifh > 1) then + +c print *,' ' +c print *,'--- Before sort, original prs values follow:' +c print *,' ' + + do ist = 1,maxstorm + prstemp(ist) = gridprs(ist,ifh-1) +c write (6,81) ist,prstemp(ist)/100.0 + enddo + + imode = 2 + sortindex = 0 + call qsort (prstemp,sortindex,maxstorm) + +ccccc call orders (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) +ccccc call orders_4byte (imode,iwork,prstemp,sortindex,maxstorm,1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Pressure-sorted storm list:' + print *,' ' + + do ist = 1,maxstorm + if (prstemp(sortindex(ist))/100.0 < 9999.0) then + write (6,82) ist,sortindex(ist) + & ,prstemp(sortindex(ist))/100.0 + endif + enddo + + 81 format (1x,'ist= ',i5,' Original (unsorted) prstemp= ',f7.2) + 82 format (1x,'ist= ',i5,' sortindex(ist)= ',i5 + & ,' prstemp= ',f7.2) + endif + + else + do ist = 1,maxstorm + sortindex(ist) = ist + enddo + endif + + deallocate (prstemp); deallocate (iwork) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getvrvt (centlon,centlat,xlon,xlat + & ,udat,vdat,vr,vt,igvtret) +c +c ABSTRACT: This subroutine takes as input a u-wind and v-wind value +c at an input (xlon,xlat) location and returns the tangential and +c radial wind components relative to the input center lat/lon +c position (centlon,centlat). The only trick to this whole +c subroutine is figuring out the angle from the center point to the +c data point, and we do this by creating a triangle with the leg +c from the center point to the data point being the hypotenuse. +c +c NOTE: All longitudes must be in positive degrees east (0-360) !!! +c +c INPUT: +c centlon Longitude of center point +c centlat Latitude of center point +c xlon Longitude of pt at which vr & vt will be computed +c xlat Latitude of pt at which vr & vt will be computed +c udat u-value of wind at the point (xlon,xlat) +c vdat v-value of wind at the point (xlon,xlat) +c +c OUTPUT: +c vr Radial wind component at (xlon,xlat) wrt (centlon,centlat) +c vt Tang wind component at (xlon,xlat) wrt (centlon,centlat) +c igvtret Return code from this subroutine +c + USE trig_vals + USE verbose_output + + implicit none + + real centlon,centlat,xlon,xlat,udat,vdat,vr,vt,degrees,tmpxlon + real angle,xlondiff,xlatdiff,opp_dist,hyp_dist,sin_value + real cos_value,adj_dist,tmpangle,sin_angle,cos_angle + real uvrcomp,vvrcomp,uvtcomp,vvtcomp + integer igvtret +c + call calcdist(centlon,centlat,xlon,xlat,hyp_dist,degrees) + +c xxxx + + tmpxlon = xlon + + if (centlon > 330.0) then + + if (xlon > 360.0) then + + tmpxlon = xlon ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (xlon < 30.0) then + + tmpxlon = xlon + 360. ! In this case, the fix center is just + ! to the west of the GM with a lon (centlon) + ! > 330, while the point being evaluated + ! (xlon) is just east of the GM, but with a + ! lon (centlon) < 30. Need to adjust here to + ! to get the xlon in the 330+ frame of + ! reference. + + endif + + elseif (centlon >= 0 .and. centlon < 30.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = 360. - xlon + + endif + + elseif (centlon < 0.0) then + + if (xlon >= 360.0) then + + tmpxlon = xlon - 360. + + elseif (xlon > 330. .and. xlon < 360.) then + + tmpxlon = -1 * (360. - xlon) + + endif + + endif + + xlatdiff = abs(centlat - xlat) + xlondiff = abs(centlon - tmpxlon) + + if (centlon > 355.0) then + write (6,91) centlon,tmpxlon,hyp_dist,degrees,xlondiff + 91 format (1x,'centlon= ',f8.3,' tmpxlon= ',f8.3,' hyp_dist= ' + & ,f10.2,' degrees= ',f10.2,' xlondiff= ',f12.2) + endif + + if (xlondiff == 0 .and. xlatdiff > 0) then + + if (centlat > xlat) angle = 180 ! pt directly south of ctr + if (centlat < xlat) angle = 0 ! pt directly north of ctr + + else if (xlondiff > 0 .and. xlatdiff == 0) then + + if (centlon > tmpxlon) angle = 270 ! pt directly west of ctr + if (centlon < tmpxlon) angle = 90 ! pt directly east of ctr + + else + + ! This next part figures out the angle from the center point + ! (centlon,centlat) to the data point (tmpxlon,xlat). It does + ! this by setting up a triangle and then using inverse trig + ! functions to get the angle. Since this is a kludgy way to + ! do it that doesn't account for the curvature of the earth, + ! we'll do it 2 ways, using asin and then acos, then take the + ! average of those 2 for the angle. hyp_dist, calculated just + ! above, is the distance from the center pt to the data pt. + + opp_dist = xlatdiff/360. * ecircum + sin_value = opp_dist / hyp_dist + if (sin_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, sin_value > 1, setting to 1.' + print *,'!!! opp_dist= ',opp_dist,' hyp_dist= ',hyp_dist + print *,'!!! sin_value = ',sin_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + sin_value = 0.99999 + endif + sin_angle = asin(sin_value) / dtr + + call calcdist(centlon,centlat,tmpxlon,centlat,adj_dist,degrees) + cos_value = adj_dist / hyp_dist + if (cos_value > 1.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In getvrvt, cos_value > 1, setting to 1.' + print *,'!!! adj_dist= ',adj_dist,' hyp_dist= ',hyp_dist + print *,'!!! cos_value = ',cos_value + print *,'!!! centlon= ',centlon,' centlat= ',centlat + print *,'!!! tmpxlon= ',tmpxlon,' xlat= ',xlat + print *,' ' + endif + + cos_value = 0.99999 + endif + cos_angle = acos(cos_value) / dtr + + tmpangle = 0.5 * (sin_angle + cos_angle) + + ! The previous lines of code just calculated an angle between + ! 0 and 90. This next if structure adjusts that angle to + ! instead be between 0 and 360. + + if (centlat <= xlat .and. centlon <= tmpxlon) then + angle = 90 - tmpangle + else if (centlat > xlat .and. centlon <= tmpxlon) then + angle = 90 + tmpangle + else if (centlat >= xlat .and. centlon >= tmpxlon) then + angle = 270 - tmpangle + else if (centlat < xlat .and. centlon >= tmpxlon) then + angle = 270 + tmpangle + endif + + endif + + uvrcomp = udat * sin(angle * dtr) + vvrcomp = vdat * cos(angle * dtr) + vr = uvrcomp + vvrcomp + + uvtcomp = (-udat) * cos(angle * dtr) + vvtcomp = vdat * sin(angle * dtr) + vt = uvtcomp + vvtcomp + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcfunix (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,plastbar,rlastbar,rmax,cps_vals + & ,wcore_flag,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in the new ATCF UNIX format. +c Unlike the old atcf DOS format in which you waited until the +c whole tracking was over to write the output for all forecast +c hours, with this atcfunix format, each time we are calling this +c subroutine, it is to only write out 1 record, which will be the +c fix info for a particular storm at a given time. Also, even +c though we have some data (GFS, NAM) at 6-hour intervals, Jim +c Gross informed me that TPC does not need the positions at such +c frequency, and keeping the reporting at 12 hour intervals is fine. +c +c While this new atcfunix format contains much more information than +c the old 1-line atcf dos message, for our purposes we will use the +c slots for mslp and wind radii. An example set of output records +c will look like the following: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c plastbar pressure of the outermost closed isobar +c rlastbar radius (nm) of the outermost closed isobar +c rmax radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c cps_vals real array with the values for the 3 cyclone phase +c space parameters: (1) is for Parameter B (thermal +c asymmetry); (2) is for lower level (600-900 mb) thermal +c wind; (3) is for upper level (300-600 mb) thermal wind. +c wcore_flag character for value of 300-500 mb warm core: y, n, or +c 'u' for undetermined. +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE phase + USE verbose_output + + type (datecard) inp + type (trackstuff) trkrinfo + + real cps_vals(3) + real outlon,outlat,rmax,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,irmax,output_fhr,ic,iplastbar,irlastbar + integer vradius(3,4),icps_vals(3) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + character comma_fill1*48,comma_fill2*31,comma_filler*79 + + if ( verb .ge. 3 ) then + print *,'TTT top of atcfunix, ist= ',ist,' ifh= ',ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcfunix. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'in output_atcfunix, tcv_storm_id= ' + & ,storm(ist)%tcv_storm_id + print *,'in output_atcfunix, tcv_storm_id(3:3)= ' + & ,storm(ist)%tcv_storm_id(3:3) + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' +!zhang case ('A','a'); basinid = 'NA' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + if (trkrinfo%want_oci) then + if (plastbar > 0.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -99 + endif + if (rlastbar > 0.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -99 + endif + else + iplastbar = -99 + irlastbar = -99 + endif + + if ( verb .ge. 3 ) then + print *, 'output: rlastbar=',rlastbar,' irlastbar=',irlastbar + print *, 'output: plastbar=',plastbar,' iplastbar=',iplastbar + endif + +c Now convert all of the cyclone phase space parameter values from +c real to integer. + + do ic = 1,3 + if (cps_vals(ic) > -9999.0) then + if (cps_vals(ic) >= 0.0) then + icps_vals(ic) = int(cps_vals(ic)*10. + 0.5) + else + icps_vals(ic) = int(cps_vals(ic)*10. - 0.5) + endif + else + icps_vals(ic) = -9999 + endif + enddo + + if (wcore_flag == 'y'.or. wcore_flag == 'Y') then + wcore_flag = 'Y' + elseif (wcore_flag == 'n' .or. wcore_flag == 'N') then + wcore_flag = 'N' + elseif (wcore_flag == 'u' .or. wcore_flag == 'U') then + wcore_flag = 'U' + else + wcore_flag = 'U' + endif + + comma_fill1 = ', 0, 0, , 0, , 0, 0, ,' + comma_fill2 = ' , , , 0, 0, 0, 0' + comma_filler = comma_fill1//comma_fill2 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,91) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,0,0,stcvtype(ist) + endif + + else + + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (64,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,comma_filler,icps_vals(1) + & ,icps_vals(2),icps_vals(3),wcore_flag,int(wcore_depth*10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,'rmax= ',rmax,' irmax= ',irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,a79,', THERMO PARAMS' + & ,3(', ',i7),', ',a1,', ',i2,', DT, -999') + 91 format (a2,', ',a4,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,2(', ',i4),', ',i3,2(', ',i3),', ',a3) + +c bug fix for IBM: flush the output stream so it actually writes + flush(64) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_all (fixlon,fixlat,inp,maxstorm,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each +c storm. This message contains the model identifier, the forecast +c initial date, and the positions for 0, 12, 24, 36, 48, 60 and 72 +c hours. In the case of the regional models (NGM, Eta), which +c only go out to 48h, zeroes are included for forecast hours +c 60 and 72. +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by Steve Lord for plotting purposes, and his +c plotting routines need the longitudes in 0 - 360, increasing +c westward. Thus, a necessary adjustment is made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + print *,'top of output_all' + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + intlon(ifh) = 0 + intlat(ifh) = 0 + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + endif + + enddo ifhloop + + print *,'before select case, atcfname= ' + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(5),intlon(5),intlat(9),intlon(9),intlat(13) + & ,intlon(13),intlat(17),intlon(17),intlat(21),intlon(21) + & ,0,0,storm(ist)%tcv_storm_id + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(3),intlon(3),intlat(5),intlon(5),intlat(7) + & ,intlon(7),intlat(9),intlon(9),intlat(11),intlon(11) + & ,intlat(13),intlon(13),storm(ist)%tcv_storm_id + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3),intlat(4) + & ,intlon(4),intlat(5),intlon(5),intlat(6),intlon(6) + & ,intlat(7),intlon(7),storm(ist)%tcv_storm_id + + case ('GDA','HDA') ! GDAS, HDAS + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),0,0,0,0,0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + case default +c print *,'!!! ERROR in subroutine output_all. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + print *,'!!! Model name is not identified: ',atcfname + + write (61,81) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(1),intlon(1) + & ,intlat(2),intlon(2),intlat(3),intlon(3) + & ,intlat(4),intlon(4),0,0,0,0,0,0 + & ,storm(ist)%tcv_storm_id + + end select + + enddo stormloop + + 81 format (i2,a4,4i2.2,14i4,1x,a3) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf (fixlon,fixlat,inp,xmaxwind,maxstorm + & ,ifhmax,ioaret) +c +c ABSTRACT: This subroutine outputs a 1-line message for each storm +c in ATCF format. This message contains the model identifier, the +c forecast initial date, and the positions for 12, 24, 36, 48 +c and 72 hours. This message also contains the intensity +c estimates (in knots) for those same hours. The conversion for +c m/s to knots is to multiply m/s by 1.9427 (3.281 ft/m, +c 1 naut mile/6080 ft, 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The output of this +c subroutine is used by the atcf system at TPC for plotting +c purposes, and the atcf plotting routines need the longitudes in +c 0 - 360, increasing westward. Thus, a necessary adjustment is +c made. +c + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms +c + type (datecard) inp +c + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real xmaxwind(maxstorm,maxtime) + real conv_ms_knots + integer modelnum(maxmodel) + integer intlon(maxtime),intlat(maxtime) + character modelchar(maxmodel)*4,basinid*4 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0 - 360, increasing westward. + + conv_ms_knots = 1.9427 + + stormloop: do ist = 1,maxstorm + + if (stormswitch(ist) == 3) cycle stormloop + intlon = 0; intlat = 0 + + ifhloop: do ifh = 1,maxtime + + if (ifh <= ifhmax) then + if (ifhours(ifh) == 99) then + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + else + intlon(ifh) = 0 + intlat(ifh) = 0 + cycle ifhloop + endif + + if (fixlon(ist,ifh) < -998.0 .or. fixlat(ist,ifh) < -998.0) + & then + + intlon(ifh) = 0 + intlat(ifh) = 0 + + else + intlon(ifh) = 3600 - int(fixlon(ist,ifh) * 10. + 0.5) + intlat(ifh) = int(abs(fixlat(ist,ifh)) * 10. + 0.5) + if (fixlat(ist,ifh) < 0.0) then + intlat(ifh) = intlat(ifh) * (-1) + endif + + endif + + enddo ifhloop + + basinid = ' ' + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid(1:2) = 'AL' + case ('E','e'); basinid(1:2) = 'EP' + case ('C','c'); basinid(1:2) = 'CP' + case ('W','w'); basinid(1:2) = 'WP' + case ('O','o'); basinid(1:2) = 'SC' + case ('T','t'); basinid(1:2) = 'EC' + case ('U','u'); basinid(1:2) = 'AU' + case ('P','p'); basinid(1:2) = 'SP' + case ('S','s'); basinid(1:2) = 'SI' + case ('B','b'); basinid(1:2) = 'BB' +cPENG case ('A','a'); basinid(1:2) = 'NA' + case ('A','a'); basinid(1:2) = 'AA' + case default; basinid(1:2) = '**' + end select + basinid(3:4) = storm(ist)%tcv_storm_id(1:2) + + + select case (atcfname(1:3)) + + case ('SEC','SEN','SEP','SKC','SKN','SKP','SRC','SRN','SRP') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(5),intlon(5) + & ,intlat(9),intlon(9),intlat(13),intlon(13),intlat(17) + & ,intlon(17),0,0 + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,17)*conv_ms_knots) + 0.5) + & ,0 + & ,basinid,inp%byy + + case ('AVN','NGM','ETA','GFD','AP0','AN0','AP1','AN1','AC0' + & ,'AMM','CMC','HWR') + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('MRF','UKX','NGX','EP0','EP1','EP2','EN0','EN1','EN2' + & ,'CP0','CN0','CC0','EC0','EMX') + ! MRF, UKMET, NAVGEM, ECMWF Ensemble, ECMWF hi-res + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4),intlat(5) + & ,intlon(5),intlat(7),intlon(7) + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + case ('GDA','HDA') ! GDAS, HDAS + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,intlat(3),intlon(3),intlat(4),intlon(4) + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case ('WP0','WP1','WN0','WN1','XP0','XP1','XN0','XN1' + & ,'YP0','YP1','YN0','YN1','ZP0','ZP1','ZN0','ZN1') + ! Ensemble RELOCATION ONLY + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh + & ,intlon(1),intlat(1),intlat(2),intlon(2) + & ,0,0,0,0 + & ,0,0 + & ,int((xmaxwind(ist,2)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,4)*conv_ms_knots) + 0.5) + & ,0,0,basinid,inp%byy + + case default +c print *,'!!! ERROR in subroutine output_atcf. ' +c print *,'!!! Model name is not identified.' +c print *,'!!! Model name = ',atcfname +c print *,'!!! ist = ',ist,' Model number = ',atcfnum + print *,' ' + write (62,82) atcfnum,atcfname + & ,inp%byy,inp%bmm,inp%bdd,inp%bhh,intlat(3),intlon(3) + & ,intlat(5),intlon(5),intlat(7),intlon(7),intlat(9) + & ,intlon(9),intlat(13),intlon(13) + & ,int((xmaxwind(ist,3)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,5)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,7)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,9)*conv_ms_knots) + 0.5) + & ,int((xmaxwind(ist,13)*conv_ms_knots) + 0.5) + & ,basinid,inp%byy + + end select + + enddo stormloop + + 82 format (i2,a4,4i2.2,10i4,5i3,1x,a4,i2.2) +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_hfip (outlon,outlat,inp,ist + & ,ifh,vmaxwind,xminmslp,vradius,rmax,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified ATCF UNIX format. +c The modification is to allow for sub-hourly output. That is, +c instead of just integer output hours, we can have output at +c 10, 15 or 20 past an hour. This necessitates a change in the +c "forecast hour" placeholder in the ATCF format. Instead of it +c being an I3, we'll make it an I5, with something like a lead time +c of 36.25h being rounded and truncated to 03625 for output. +c +c An example set of output records using the standard atcf format +c looks like the following: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c An example set of modified output records will look like the +c following, for the case of a lead time of 36:15 (36.25): +c +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, AVNO, 03625, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifh index for the lead time array +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c rmax Radius of max winds (n mi).... it was already converted +c from km to n mi in subroutine get_max_wind +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE tracked_parms + USE verbose_output + + type (datecard) inp + + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,rmax + integer intlon,intlat,output_fhr,irmax,ileadtime + integer vradius(3,4) + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_hfip. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + ! ST: ifcsthour does not exist, so output_fhr is always + ! filled with invalid data here. However, output_fhr is + ! never used, so it is safe to remove. + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + ! output_fhr = ifcsthour + 3 + ileadtime = nint((fhreal(ifh) + 3.0) * 100.0) + else + ! output_fhr = ifcsthour + ileadtime = nint(fhreal(ifh) * 100.0) + endif + + if (rmax == -99.0) then + irmax = -99 + else + irmax = int(rmax + 0.5) + endif + + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4),irmax + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4),irmax + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (69,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),ileadtime,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4),irmax + endif + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i5.5,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', 0, 0, ',i3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(69) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_fract_wind (outlon,outlat,xsfclon,xsfclat + & ,inp,ist,ifcsthour,vmaxwind,xminmslp,wfract_cov + & ,wfract_type,pdf_ct_bin,pdf_ct_tot,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values for the fractional areal coverage of various wind +c thresholds. In addition, this subroutine also writes out +c records to a file containing data on the PDF of wind magnitudes +c within r=350 km. +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with areal coverage thresholds. +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, NEE, 981, 857, 629, 810 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, NEE, 874, 732, 319, 610 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, NEE, 454, 327, 99, 270 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 34, AAE, 721, 721, 721, 721 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 50, AAE, 465, 465, 465, 465 +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 000, 100, 64, AAE, 298, 298, 298, 298 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the pctgs for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind pctg info; all the +c other info is identical for each entry. +c +c Listed after the "XX" in each record is the radius from which +c the coverage is valid (000 km in this case); Next is the radius +c at which the coverage stops (100 km in this case). Next is the +c wind threshold (34, 50, 64). Next is an identifier for which +c quadrant the coverage starts in (first 2 characters are NE, SE, +c SW, NW); the last character indicates if the coverages are +c computed in the quadrants as earth-relative ("E") or +c storm-motion relative ("R"). The ones listed there as "AAE" +c are for the full disc (i.e., 4-quadrant average), earth-relative. +c Next are the wind coverage percentages, listed as percentage * 10 +c (e.g., 981 = 98.1%). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c wfract_cov percent areal coverage for various wind thresholds +c wfract_type 'earth' or 'storm' relative analysis +c pdf_ct_bin array for pdf of wind magnitudes within r=350 km +c pdf_ct_tot total count of pdf points for r < 350 km +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,pdfval + real wfract_cov(numquad+1,numbin,numthresh) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer :: windthresh(numthresh) = (/34,50,64/) + integer pdf_ct_bin(16) + integer intlon,intlat,output_fhr,intlon100,intlat100,pdf_ct_tot + integer maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (wfract_type == 'earth') then + wt = 'E' + else if (wfract_type == 'storm') then + wt = 'R' + else + wt = 'X' + endif + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'NE',wt + & ,int((1000.*wfract_cov(1,ib,it))+0.5) + & ,int((1000.*wfract_cov(2,ib,it))+0.5) + & ,int((1000.*wfract_cov(3,ib,it))+0.5) + & ,int((1000.*wfract_cov(4,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + do ib = 1,numbin + do it = 1,numthresh + + write (73,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, ',0,ib*100,windthresh(it),'AA',wt + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,int((1000.*wfract_cov(5,ib,it))+0.5) + & ,intlat100,clatns,intlon100,clonew + + enddo + enddo + + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a6,i3.3,', ',i3.3,', ' + & ,i3,', ',a2,a1,4(', ',i4),', ',i4,a1,', ',i5,a1) + +c -------------------------------------------------- +c Now compute and write out the pdf values for the +c wind magnitude.... +c -------------------------------------------------- + + do ip = 1,16 + pdfval = float(pdf_ct_bin(ip)) / float(pdf_ct_tot) + write (76,85) atcfymdh,basinid,storm(ist)%tcv_storm_id(1:2) + & ,output_fhr,10*(ip-1),10*ip,pdf_ct_bin(ip) + & ,pdf_ct_tot,pdfval + enddo + + 85 format (1x,i10.10,3x,a2,a2,3x,i3,3x,i3.3,'_',i3.3,3x,i7,2x,i7 + & ,2x,f6.3) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(73) + + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_wind_structure (outlon,outlat,xsfclon + & ,xsfclat,inp,ist,ifcsthour,vmaxwind,xminmslp,er_wind + & ,sr_wind,er_vr,sr_vr,er_vt,sr_vt,maxstorm,iofwret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the +c values of the winds at specified distances along 45-degree +c radials in each storm quadrant. These are output +c twice -- First, for an earth-relative coordinate system, and +c second, for a storm-relative coordinate system. For the +c earth-relative estimates, we will always have 4 radials: NE, SE, +c SW and NW (45,135,225,315). For the storm-relative estimates, +c these radials will be computed at the same relative angles (i.e., +c 45,135,225,315), but with respect (positive clockwise) to the +c direction of storm motion. For example, for a storm moving with +c a heading of 280, the wind structure is evaluated at these +c radials: 325 (front-right; 45 deg CW from heading), 55 (back- +c right; 135 deg CW from heading), 145 (back-left; 225 deg CW from +c heading), 235 (front-left; 315 deg CW from heading). +c +c LOCAL: +c numdist Number of discrete radii at which the winds will +c be evaluated +c +c +c This format will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with wind values at the 13 specified distances +c (10, 25, 50, 75, 100, 150, 200, 250, 300, 350, 400, 450, 500 km) +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NEE, 1137, 1221, 854, 655, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SEE, 947, 982, 474, 396, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, SWE, 645, 683, 328, 277, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 71, NWE, 725, 753, 619, 429, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FRR, 1134, 1224, 852, 654, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BRR, 944, 984, 472, 393, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, BLR, 649, 686, 321, 272, etc., ... out to 500 km +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, +c 72, FLR, 729, 756, 613, 421, etc., ... out to 500 km +c +c NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text. +c NOTE: These winds are in m/s coming into this routine and will +c be converted to knots*10 for output (e.g., 1221 = 122.1 kts) +c +c The "71" ID indicates earth-relative winds, the "72" ID indicates +c storm-relative winds. Here are the other IDs that will be used: +c 81: Tangential winds, earth-relative (m/s) +c 82: Tangential winds, storm-relative (m/s) +c 91: Radial winds, earth-relative (m/s) +c 92: Radial winds, storm-relative (m/s) +c +c Note that in this example, for this 36h forecast hour, there are +c 8 entries. This is so that we can include the wind values for +c the 4 different quadrants, for both the earth relative analyses +c (NEE, SEE, SWE, NWE) and the storm-relative analyses (FRR, BRR, +c BLR, FLR). +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c er_wind Quadrant winds in earth-relative framework +c sr_wind Quadrant winds in storm-relative framework +c er_vr Quadrant radial winds in earth-relative framework +c sr_vr Quadrant radial winds in storm-relative framework +c er_vt Quadrant tangential winds in earth-relative framework +c sr_vt Quadrant tangential winds in storm-relative framework +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + integer ioutwind(numdist) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real er_wind(numquad,numdist) + real sr_wind(numquad,numdist) + real er_vr(numquad,numdist) + real er_vt(numquad,numdist) + real sr_vr(numquad,numdist) + real sr_vt(numquad,numdist) + real outlon,outlat + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,id,intlon100,intlat100,ir + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1 + character*2 :: cquad(4) = (/'NE','SE','SW','NW'/) + character*2 :: crel(4) = (/'FR','BR','BL','FL'/) + + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + +c Total wind (converted to knots*10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 71, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Total wind (converted to knots*10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_wind(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_wind(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 72, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 81, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Tangential wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vt(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vt(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 82, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), earth relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (er_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((er_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, ',cquad(iq),'E' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo + +c Radial wind (m/s * 10), storm relative.... + + do iq = 1,numquad + do ir = 1,numdist + if (sr_vr(iq,ir) < -998.0) then + ioutwind(ir) = -999 + else + ioutwind(ir) = int((sr_vr(iq,ir)*conv_ms_knots*10)+0.5) + endif + enddo + write (72,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 92, ',crel(iq),'R' + & ,(ioutwind(it),it=1,numdist) + & ,intlat100,clatns,intlon100,clonew + enddo +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a10,a2,a1,14(', ',i4) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(72) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_ike (outlon,outlat,xsfclon,xsfclat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,ike,sdp,wdp,maxstorm + & ,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the Integrated Kinetic Energy (IKE) and Storm Surge Damage +c Potential (SDP), based on Powell (BAMS, 2007). At this time, we +c are only computing the IKE values for TS threshold (17.5 m/s) and +c above. We are not yet computing wind damage potential (WDP) +c since, per Mark Powell (4/2008), he is currently re-formulating +c an algorithm for it. +c +c LOCAL: +c +c Arrays: +c +c ike Integrated kinetic energy: +c ike(1) = IKE_10m/s (storm energy) +c ike(2) = IKE_18m/s (IKE_ts, tropical storm) +c ike(3) = IKE_33m/s (IKE_h, hurricane) +c ike(4) = IKE_25_40 m/s (Not currently computed) +c ike(5) = IKE_41_54 m/s (Not currently computed) +c ike(6) = IKE_55 m/s (Not currently computed) +c +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with WDP, SDP and IKE values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, 340, 560, 212, 174, 42, 93, 12, 0 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 91, +c IKE, WDP, SDP, I10, ITS, IH ,I2540,I4154, I55 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c Values for WDP and SDP are multiplied by 10 in this routine +c before being written out. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c xsfclon low-level longitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c xsfclat low-level latitude estimate for this storm & time, +c computed ideally from mean of mslp & low-level winds. +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c ike integrated kinetic energy, in units of TJ +c sdp storm surge damage potential +c wdp wind damage potential +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp +c + integer, parameter :: numdist=14,numquad=4,numbin=5,numthresh=3 + real outlon,outlat,sdp,wdp + real ike(max_ike_cats) + real vmaxwind,conv_ms_knots,xminmslp,xsfclon,xsfclat + integer intlon,intlat,output_fhr,intlon100,intlat100,maxstorm + character basinid*2,clatns*1,clonew*1,wfract_type*5,wt*1,cquad*2 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + intlon100 = 0 + intlat100 = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + intlon100 = 36000 - int(outlon * 100. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + intlon100 = int(outlon * 100. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + intlat100 = int(abs(outlat) * 100. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (74,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 91, IKE',int((wdp*10)+0.5),int((sdp*10)+0.5) + & ,int(ike(1)+0.5),int(ike(2)+0.5),int(ike(3)+0.5) + & ,int(ike(4)+0.5),int(ike(5)+0.5),int(ike(6)+0.5) + & ,intlat100,clatns,intlon100,clonew +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,a14,8(',',i5) + & ,', ',i4,a1,', ',i5,a1) + +c bug fix for IBM: flush the output stream so it actually writes + flush(74) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_phase (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,paramb,vtl_slope + & ,vtu_slope,ioiret) +c +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour. This message contains the values +c for the three parameters that comprise Bob Hart's cyclone phase +c space (CPS). These parameters are his "parameter B", which +c assesses the left-right thermal asymmetry, and the upper +c troposphere (300-600 mb) and lower troposphere (900-600 mb) +c thermal wind values. +c +c LOCAL: +c +c Arrays: +c +c The format used will mimic the current atcfunix format with the +c difference coming late in the record, where the various wind radii +c will be replaced with paramb, vtl_slope and vtu_slope values: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, 340, 560, 212 +c +c Where the places are identified as follows: +c +c AL, 13, 2000092500, 03, AVNO, 036, 243N, 675W, 42, 995, XX, 95, +c CPS, B, VTL, VTU +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.) +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c storm An array of type tcvcard. Use this for the storm ID +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c paramb thermal asymmetry +c vtl_slope thermal wind value for lower troposphere (900-600 mb) +c vtu_slope thermal wind value for upper troposphere (600-300 mb) +c +c OUTPUT: +c ioiret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE verbose_output + + type (datecard) inp + + real outlon,outlat,paramb,vtl_slope,vtu_slope + real vmaxwind,conv_ms_knots,xminmslp + integer intlon,intlat,output_fhr + character basinid*2,clatns*1,clonew*1 + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = '**' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (71,81) basinid,storm(ist)%tcv_storm_id(1:2) + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/100.0 + 0.5) + & ,', XX, 95, CPS',int(paramb+0.5),int(vtl_slope+0.5) + & ,int(vtu_slope+0.5) +c + 81 format (a2,', ',a2,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a14,3(',',i6)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(71) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf_gen (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,plastbar,rlastbar,rmax + & ,cps_vals,wcore_flag,imeanzeta,igridzeta,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different. Here's an example of the +c TPC standard atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c be the one that is pulled from the tcvitals or gen_vitals +c record: +c +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 50, NEQ, 155, 000, 000, 000 +c 2000092500_230N_0658W_13L, 2000092500, 03, GFSO, 036, 243N, 675W, +c 42, 995, XX, 64, NEQ, 000, 000, 000, 000 +c +c +c Note that in this example, for this 36h forecast hour, there are +c 3 entries. This is so that we can include the radii for the +c 3 different wind thresholds (34kt, 50kt and 64kt). So the only +c thing different in each entry is the wind radii info; all the +c other info is identical for each entry. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hours The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd storm translation speed +c istmdir direction of storm movement +c plastbar pressure of last closed isobar +c rlastbar radius of last closed isobar +c rmax radius of max winds +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c wcore_flag 'u'=undetermined, 'y'=yes, 'n'=no +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals; USE level_parms + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real outlon,outlat,plastbar,rlastbar,rmax + real vmaxwind,conv_ms_knots,xminmslp,mslp_outp_adj + real cps_vals(3) + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar,irmax + integer ivtl,ivtu,iparamb,output_fhr + integer vradius(3,4) + integer imeanzeta(nlevgrzeta),igridzeta(nlevgrzeta) + character basinid*2,clatns*1,clonew*1,wcore_flag*1 + + if ( verb .ge. 3) then + print *,'+++ Top of output_atcf_gen, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (rmax > -90.0) then + irmax = int(rmax + 0.5) + else + irmax = -99 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (66,87) basinid,adjustr(storm(ist)%tcv_storm_id) + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,irmax,iparamb,ivtl,ivtu,wcore_flag + & ,istmdir,istmspd + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + endif + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',3(i4,', '),3(i6,', '),a1,2(', ',i4),4(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(66) + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_atcf_sink (outlon,outlat,inp,ist + & ,ifcsthour,vmaxwind,xminmslp,vradius,maxstorm + & ,trkrinfo,istmspd,istmdir,imeanzeta,igridzeta + & ,cps_vals,plastbar,rlastbar,ioaxret) + +c ABSTRACT: This subroutine outputs a 1-line message for a given +c storm at an input forecast hour in a modified atcfunix format. +c The "sink" in the subroutine name indicates that this output +c contains the whole kitchen sink of forecast storm info. +c The reason that it's called "modified" is that the format is +c slightly different from the standard TPC-accepted atcfunix +c format that they use for TCs. Specifically, the first part that +c identifies the storm is different, and the part after the radii +c data is different. Here's an example of the TPC standard +c atcfunix format: +c +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 34, +c NEQ, 242, 163, 124, 208 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 50, +c NEQ, 155, 000, 000, 000 +c AL, 13, 2000092500, 03, GFSO, 036, 243N, 675W, 42, 995, XX, 64, +c NEQ, 000, 000, 000, 000 +c +c (NOTE: Each of the above lines beginning with "AL" is output as +c a single line of text.... they're just broken up into 2 +c lines here for readability.) +c +c Here's an example of the modified output format for the same +c storm. Note that the lat/lon identifier in the new storm id at +c the beginning of the record is different from that shown later +c in the record. The reason is that the lat/lon identifier will +c indicate the lat/lon at which the storm was *first* found in +c the model. The position may be either found within this run +c of the tracker, or that position may have been pulled from the +c tcvitals or gen_vitals record: +c +c 2000092500_F000_206N_0623W_13L, 2000092500, 03, GFSO, 036 +c , 243N, 675W, 42, 995, XX, 34, NEQ, 242, 163, 124, 208 +c , PLAS, RLAS, RMX, DIR, SPD, B, VTU, VTL +c , Z8MN, Z8MX, Z7MN, Z7MX +c +c As noted above, there is extra info at the end, after the +c "34, NEQ, 242, 163, 124, 208" radii info. Here is a key +c to indicate what these items are: +c +c PLAS: Pressure (mb) of last closed isobar +c RLAS: Radius of the last closed isobar in nm, 0 - 9999 nm. +c RMX: Radius of max winds, 0 - 999 nm. +c DIR: Direction of storm motion. +c SPD: Speed of storm motion (m/s * 10). +c B: Hart's CPS "Parameter B" thickness asymmetry value (m). +c VTL: Hart's CPS thermal wind (Lower, 900-600) value. +c VTU: Hart's CPS thermal wind (Upper, 600-300) value. +c Z8MN: Mean value of 850 mb zeta surrounding storm. +c Z8MX: Max value of 850 mb zeta near storm. +c Z7MN: Mean value of 700 mb zeta surrounding storm. +c Z7MX: Max value of 700 mb zeta near storm. +c +c This message also contains the intensity estimates (in knots) +c for every forecast hour. The conversion for m/s to knots is +c to multiply m/s by 1.9427 (3.281 ft/m, 1 naut mile/6080 ft, +c 3600s/h). +c +c NOTE: The longitudes that are passed into this subroutine are +c given in 0 - 360, increasing eastward. The format for the +c atcfunix system requires that the output be 0-180E or +c 0-180W, so we must adjust the values, if needed. Also, the +c values for southern latitudes must be positive (use 'N' and +c 'S' to distinguish Northern/Southern Hemispheres). +c +c INPUT: +c outlon longitude fix position for this storm at this time +c which is to be written out to the output file +c outlat latitude fix position for this storm at this time +c which is to be written out to the output file +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c ifcsthr the current forecast hour being output +c vmaxwind the max surface wind for this storm at this fcst hour +c xminmslp the min mslp for this storm at this fcst hour +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c maxstorm max # of storms that can be handled +c istmspd speed of storm translation +c istmdir direction of storm motion +c cps_vals Hart's cyclone phase space values: (1) is for parameter +c B (thickness asymmetry), (2) and (3) are for thermal +c wind values. +c imeanzeta array with values of mean 850 & 700 zeta +c igridzeta array with values of max (gridpoint) 850 & 700 zeta +c plastbar pressure of last closed isobar (pa) +c rlastbar radius of last closed isobar (nm) +c +c OUTPUT: +c ioaxret integer return code from this subroutine +c +c LOCAL: +c intlon integer that holds the value of outlon*10 +c intlat integer that holds the value of outlat*10 +c storm An array of type tcvcard. Use this for the storm ID +c + + USE def_vitals; USE inparms; USE set_max_parms; USE atcf + USE trkrparms; USE gen_vitals + USE verbose_output + + type (gencard) gstm + type (datecard) inp + type (trackstuff) trkrinfo +c + real cps_vals(3) + real outlon,outlat,mslp_outp_adj + real vmaxwind,conv_ms_knots,xminmslp,plastbar,rlastbar + integer intlon,intlat,istmspd,istmdir,iplastbar,irlastbar + integer iparamb,ivtl,ivtu,output_fhr + integer vradius(3,4) + integer imeanzeta(2),igridzeta(2) + character basinid*2,clatns*1,clonew*1 + + if ( verb .ge. 3 ) then + print *,'+++ Top of output_atcf_sink, ist= ',ist,' ifh= ' + & ,ifcsthour + endif + + if (xminmslp == 999999.0) xminmslp = 0.0 + + if (xminmslp < 1100.0) then + ! Pressure units are in mb... + mslp_outp_adj = 1.0 + elseif (xminmslp >80000.0) then + ! Pressure units are in Pa... + mslp_outp_adj = 100.0 + else + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Something wrong in subroutine' + print *,' output_atcf_gen. The mslp value' + print *,' (xminmslp) is not in range.' + print *,' xminmslp = ',xminmslp + print *,' EXITING....' + print *,' ' + stop 95 + endif + endif + +c First convert all of the lat/lon values from reals into integers. +c These integer values must be 10x their real value (eg. 125.4 will +c be written out as 1254). Convert the lon values so that they go +c from 0-180E or 0-180W, and convert the lat values so that they are +c positive and use 'N' or 'S' to differentiate hemispheres. + + conv_ms_knots = 1.9427 + + if (outlon < -998.0 .or. outlat < -998.0) then + intlon = 0 + intlat = 0 + clonew = ' ' + clatns = ' ' + else + if (outlon >= 180.0) then + intlon = 3600 - int(outlon * 10. + 0.5) + clonew = 'W' + else + intlon = int(outlon * 10. + 0.5) + clonew = 'E' + endif + intlat = int(abs(outlat) * 10. + 0.5) + if (outlat < 0.0) then + clatns = 'S' + else + clatns = 'N' + endif + endif + + select case (storm(ist)%tcv_storm_id(3:3)) + case ('L','l'); basinid = 'AL' + case ('E','e'); basinid = 'EP' + case ('C','c'); basinid = 'CP' + case ('W','w'); basinid = 'WP' + case ('O','o'); basinid = 'SC' + case ('T','t'); basinid = 'EC' + case ('U','u'); basinid = 'AU' + case ('P','p'); basinid = 'SP' + case ('S','s'); basinid = 'SI' + case ('B','b'); basinid = 'BB' + case ('A','a'); basinid = 'AA' + case ('Q','q'); basinid = 'SL' + case default; basinid = 'HC' + end select + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + + if (stcvtype(ist) == 'FOF') then + ! If this is a TC vitals-described storm (i.e., one that is + ! numbered by JTWC or NHC), then leave the basinid as is. + ! Otherwise, we want to use the "basinid" location as a + ! label to identify what type of run this is. + if (trkrinfo%type == 'midlat') basinid = 'ML' + if (trkrinfo%type == 'tcgen') basinid = 'TG' + endif + endif + +c Unlike the regular atcfunix output, in which we output a record +c at forecast time = 00h even if the storm cannot be found, here +c we don't want to do that. So check the lat & lon positions and +c exit this subroutine now if they're both zero. + + if (intlat == 0 .and. intlon == 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Currently inside output_atcf_gen. The reported' + print *,'+++ longitude and latitude are both zero, so that ' + print *,'+++ means that the tracker could not get a fix ' + print *,'+++ for this storm at this hour. Therefore, we will' + print *,'+++ NOT write out an atcf_gen record for this' + print *,'+++ storm & forecast hour.' + print *,'+++ ' + print *,'+++ ist= ',ist + print *,'+++ gstorm= ',gstorm(ist) + print *,' ' + endif + + return + endif + +c Initially, set all "gstm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the modified atcfunix record. + + if (gstm%gv_gen_date /= 99999) then + + continue ! Just use the info off the genesis vitals record + + else + + ! This storm was found on the fly during + ! this run and there was no previous vitals record for + ! this system. The information that will be used to + ! identify the genesis location is the same exact info + ! as the tracker-found position for this time. + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = ifcsthour + gstm%gv_gen_lat = intlat + gstm%gv_gen_latns = clatns + gstm%gv_gen_lon = intlon + gstm%gv_gen_lonew = clonew + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + if (plastbar > -990.0) then + iplastbar = int(plastbar/mslp_outp_adj + 0.5) + else + iplastbar = -999 + endif + + if (rlastbar > -990.0) then + irlastbar = int(rlastbar + 0.5) + else + irlastbar = -999 + endif + + if (cps_vals(1) > -9999.0) then + if (cps_vals(1) >= 0.0) then + iparamb = int(cps_vals(1)*10 + 0.5) + else + iparamb = int(cps_vals(1)*10 - 0.5) + endif + else + iparamb = -999 + endif + + if (cps_vals(2) > -9999.0) then + if (cps_vals(2) >= 0.0) then + ivtl = int(cps_vals(2)*10 + 0.5) + else + ivtl = int(cps_vals(2)*10 - 0.5) + endif + else + ivtl = -9999 + endif + + if (cps_vals(3) > -9999.0) then + if (cps_vals(3) >= 0.0) then + ivtu = int(cps_vals(3)*10 + 0.5) + else + ivtu = int(cps_vals(3)*10 - 0.5) + endif + else + ivtu = -9999 + endif + + if (atcfname(1:2) == 'SP') then + ! Add 3 for SREF to account for the 3-hour off-synoptic + ! time offset.... + output_fhr = ifcsthour + 3 + else + output_fhr = ifcsthour + endif + + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 34, NEQ' + & ,vradius(1,1),vradius(1,2),vradius(1,3),vradius(1,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + + if (vradius(2,1) > 0 .or. vradius(2,2) > 0 .or. + & vradius(2,3) > 0 .or. vradius(2,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 50, NEQ' + & ,vradius(2,1),vradius(2,2),vradius(2,3),vradius(2,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + + if (vradius(3,1) > 0 .or. vradius(3,2) > 0 .or. + & vradius(3,3) > 0 .or. vradius(3,4) > 0) then + write (68,87) basinid,storm(ist)%tcv_storm_id + & ,gstm%gv_gen_date,gstm%gv_gen_fhr,gstm%gv_gen_lat + & ,gstm%gv_gen_latns,gstm%gv_gen_lon + & ,gstm%gv_gen_lonew,gstm%gv_gen_type + & ,atcfymdh + & ,adjustr(atcfname),output_fhr,intlat,clatns,intlon,clonew + & ,int((vmaxwind*conv_ms_knots) + 0.5) + & ,int(xminmslp/mslp_outp_adj + 0.5) + & ,'XX, 64, NEQ' + & ,vradius(3,1),vradius(3,2),vradius(3,3),vradius(3,4) + & ,iplastbar,irlastbar,-99,istmdir,istmspd + & ,iparamb,ivtl,ivtu + & ,imeanzeta(1),igridzeta(1),imeanzeta(2),igridzeta(2) + & ,storm(ist)%tcv_storm_name + endif + +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4)) + +c 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 +c & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) +c & ,', ',2(i4,', '),4(i3,', '),2(i5,', '),4(i4,', '),a9) + + 87 format (a2,', ',a4,', ',i10.10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1 + & ,'_',a3,', ',i10.10,', 03, ',a4,', ',i3.3,', ',i3,a1 + & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,4(', ',i4.4) + & ,', ',2(i4,', '),i3,', ',2(i4,', '),3(i6,', '),4(i6,', ') + & ,a9) + +c write (68,87) gstm%gv_gen_date,gstm%gv_gen_lat +c & ,gstm%gv_gen_latns,gstm%gv_gen_lon +c & ,gstm%gv_gen_lonew,gstm%gv_gen_type +c & ,inp%bcc,inp%byy,inp%bmm,inp%bdd,inp%bhh +c & ,adjustr(atcfname),ifcsthour,intlat,clatns,intlon,clonew +c & ,int((vmaxwind*conv_ms_knots) + 0.5) +c & ,int(xminmslp/100.0 + 0.5) +c & ,'XX, 34, NEQ' +c & ,istmspd,istmdir,imeanzeta(1),igridzeta(1) +c & ,imeanzeta(2),igridzeta(2) +c +c 87 format (i10.10,'_',i3.3,a1,'_',i4.4,a1,'_',a3,', ',5i2.2 +c & ,', 03, ',a4,', ',i3.3,', ',i3,a1 +c & ,', ',i4,a1,', ',i3,', ',i4,', ',a12,6(', ',i4)) + +c bug fix for IBM: flush the output stream so it actually writes + flush(68) + + return + end + +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_tcvitals (xlon,xlat,inp,ist,iovret) +c +c ABSTRACT: This subroutine outputs a tcvitals record. The +c lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE inparms; USE set_max_parms + USE verbose_output + + type (tcvcard) stm + type (datecard) inp + real xlon,xlat +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "storm" +c components for this storm, then we will change the specific +c components that we need to. + + stm = storm(ist) + + stm%tcv_center = 'AEAR' + + stm%tcv_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + stm%tcv_latns = 'S' + else + stm%tcv_latns = 'N' + endif + + if (xlon >= 180.) then + stm%tcv_lon = 3600 - int(xlon * 10. + 0.5) + stm%tcv_lonew = 'W' + else + stm%tcv_lon = int(xlon * 10. + 0.5) + stm%tcv_lonew = 'E' + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) stm + endif + + write (65,21) stm + + 21 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + +c +c bug fix for IBM: flush the output stream so it actually writes + flush(65) + + return + end + +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine output_gen_vitals (xlon,xlat,inp,ist,istmspd,istmdir + & ,iovret) +c +c ABSTRACT: This subroutine outputs a modified vitals record. +c The lat/lon location is given by the xlon and xlat that are +c input to this subroutine. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c The storm identifier is different than that for a standard +c tcvitals. The storm identifier contains the date/time that +c the storm was first identified, and the lat/lon position at +c which it was first identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c +c INPUT: +c xlon longitude of storm position to be output +c xlat latitude of storm position to be output +c inp contains input date and model number information +c ist the number storm that we're processing (can be 1-15) +c +c OUTPUT: +c iovret return code from this subroutine +c +c OTHER: +c storm contains the tcvitals info (from module def_vitals) +c + USE def_vitals; USE gen_vitals; USE inparms; USE set_max_parms + USE verbose_output + + implicit none + + type (gencard) gstm + type (datecard) inp + real xlon,xlat + integer ist,iovret,istmspd,istmdir +c + iovret = 0 + +c Initially, set all "stm" components equal to the input "gstorm" +c components for this storm, then we will change the specific +c components that we need to. + + gstm = gstorm(ist) + +c If the "gv_gen_date" for this storm does not equal 99999, +c then that means that a vitals was read in for this storm in +c subroutine read_gen_vitals, so be sure to use the genesis +c date, genesis latitude and genesis longitude for the storm +c identifier at the beginning of the vitals record. + + if (gstm%gv_gen_date /= 99999) then + + if (gstm%gv_gen_type /= 'FOF') then + ! If this is not a 'FOF' storm (found on the fly storm), then + ! it must be a TC vitals storm, or a tropical cyclone, and we + ! don't want to create a vitals record for a tropical cyclone, + ! since we will rely on reading them from the TC Vitals + ! database instead. + return + endif + + else + + ! This storm is new in this forecast/analysis and was found on + ! the fly in the first time level for this run and there was no + ! previous vitals record for this system + + gstm%gv_gen_date = inp%bcc * 100000000 + & + inp%byy * 1000000 + & + inp%bmm * 10000 + & + inp%bdd * 100 + & + inp%bhh + + gstm%gv_gen_fhr = 0 + + gstm%gv_gen_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_gen_latns = 'S' + else + gstm%gv_gen_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_gen_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'W' + else + gstm%gv_gen_lon = int(xlon * 10. + 0.5) + gstm%gv_gen_lonew = 'E' + endif + + gstm%gv_gen_type = 'FOF' + + ! Transfer all this local "gstm" data back into the + ! saved "gstorm" array for use in subsequent fcst hrs... + + gstorm(ist) = gstm + + endif + + gstm%gv_obs_ymd = inp%bcc * 1000000 + & + inp%byy * 10000 + & + inp%bmm * 100 + & + inp%bdd + + gstm%gv_obs_hhmm = inp%bhh * 100 + + gstm%gv_obs_lat = int(abs(xlat) * 10. + 0.5) + if (xlat < 0.0) then + gstm%gv_obs_latns = 'S' + else + gstm%gv_obs_latns = 'N' + endif + + if (xlon >= 180.) then + gstm%gv_obs_lon = 3600 - int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'W' + else + gstm%gv_obs_lon = int(xlon * 10. + 0.5) + gstm%gv_obs_lonew = 'E' + endif + + gstm%gv_stdir = istmdir + gstm%gv_stspd = istmspd + + gstm%gv_depth = 'U' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,21) gstm + endif + + write (67,21) gstm + + 21 format (i10,'_F',i3.3,'_',i3.3,a1,'_',i4.4,a1,'_',a3,1x,i8,1x + & ,i4.4,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x + & ,i3,4(1x,i4),1x,a1) +c +c bug fix for IBM: flush the output stream so it actually writes + flush(67) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine output_tracker_mask (masked_outc,lb,ifh,ifcsthour + & ,imax,jmax,iotmret) +c +c ABSTRACT: This subroutine outputs a GRIB record that contains the +c "mask" used to mask out areas surrounding low pressure centers +c that have been found during the search at each forecast hour. This +c mask is written out purely for diagnostic purposes. The GRIB +c identifier given to the mask in the pds is 850 mb height (you can +c make it anything you want). This is only done for the "midlat" +c and "tcgen" cases, since the runs for those cases use a mask while +c the regular "tracker" run (that is, the run which strictly tracks +c only those storms in the TC vitals file) does not. +c +c INPUT: +c masked_outc logical array containing mask +c ifh integer counter for current forecast hour +c ifcsthour integer current forecast hour +c imax num points is i-direction of input grid +c jmax num points is j-direction of input grid +c +c OUTPUT: +c iotmret return code from this subroutine + + implicit none +c + integer ifh,imax,jmax,iotmret,kf,igoret,iix,jjx,ipret + integer ifcsthour + integer kpds(200),kgds(200) + logical(1) masked_outc(imax,jmax),lb(imax,jmax) + real xmask(imax,jmax) +c + if (ifh == 1) then + call baopenw (77,"fort.77",igoret) + print *,'baopenw: igoret= ',igoret + + if (igoret /= 0) then + print *,' ' + print *,'!!! ERROR in sub output_tracker_mask opening' + print *,'!!! **OUTPUT** grib files. baopenw return codes:' + print *,'!!! grib file 1 return code = igoret = ',igoret + STOP 95 + return + endif + endif + + xmask = 0.0 + do jjx = 1,jmax + do iix = 1,imax + if (masked_outc(iix,jjx)) then + xmask(iix,jjx) = 1.0 + else + xmask(iix,jjx) = 0.0 + endif + enddo + enddo + + kf = imax * jmax + +c kpds(5) = 7 +c kpds(6) = 100 +c kpds(7) = 850 +c kpds(22) = 0 + + kpds(1) = 7 ; kpds(2) = 80 + kpds(3) = 255 ; kpds(4) = 192 + kpds(5) = 7 ; kpds(6) = 100 + kpds(7) = 850 ; kpds(8) = 99 + kpds(9) = 7 ; kpds(10) = 20 + kpds(11) = 12 ; kpds(12) = 0 + kpds(13) = 1 ; kpds(14) = ifcsthour + kpds(15) = 0 ; kpds(16) = 10 + kpds(17) = 0 ; kpds(18) = 1 + kpds(19) = 2 ; kpds(20) = 0 + kpds(21) = 20 ; kpds(22) = 0 + kpds(23) = 0 ; kpds(24) = 0 + kpds(25) = 0 + kgds(1) = 0 ; kgds(2) = imax + kgds(3) = jmax ; kgds(4) = -90000 + kgds(5) = 0 ; kgds(6) = 128 + kgds(7) = 90000 ; kgds(8) = 359750 + kgds(9) = 250 ; kgds(10) = 250 + kgds(11) = 64 ; kgds(12) = 0 + kgds(13) = 0 ; kgds(14) = 0 + kgds(15) = 0 ; kgds(16) = 0 + kgds(17) = 0 ; kgds(18) = 0 + kgds(19) = 0 ; kgds(20) = 255 + + write(*,980) kpds(1),kpds(2) + write(*,981) kpds(3),kpds(4) + write(*,982) kpds(5),kpds(6) + write(*,983) kpds(7),kpds(8) + write(*,984) kpds(9),kpds(10) + write(*,985) kpds(11),kpds(12) + write(*,986) kpds(13),kpds(14) + write(*,987) kpds(15),kpds(16) + write(*,988) kpds(17),kpds(18) + write(*,989) kpds(19),kpds(20) + write(*,990) kpds(21),kpds(22) + write(*,991) kpds(23),kpds(24) + write(*,992) kpds(25) + write(*,880) kgds(1),kgds(2) + write(*,881) kgds(3),kgds(4) + write(*,882) kgds(5),kgds(6) + write(*,883) kgds(7),kgds(8) + write(*,884) kgds(9),kgds(10) + write(*,885) kgds(11),kgds(12) + write(*,886) kgds(13),kgds(14) + write(*,887) kgds(15),kgds(16) + write(*,888) kgds(17),kgds(18) + write(*,889) kgds(19),kgds(20) + write(*,890) kgds(21),kgds(22) +c + 980 format('tmow kpds(1) = ',i7,' kpds(2) = ',i7) + 981 format('tmow kpds(3) = ',i7,' kpds(4) = ',i7) + 982 format('tmow kpds(5) = ',i7,' kpds(6) = ',i7) + 983 format('tmow kpds(7) = ',i7,' kpds(8) = ',i7) + 984 format('tmow kpds(9) = ',i7,' kpds(10) = ',i7) + 985 format('tmow kpds(11) = ',i7,' kpds(12) = ',i7) + 986 format('tmow kpds(13) = ',i7,' kpds(14) = ',i7) + 987 format('tmow kpds(15) = ',i7,' kpds(16) = ',i7) + 988 format('tmow kpds(17) = ',i7,' kpds(18) = ',i7) + 989 format('tmow kpds(19) = ',i7,' kpds(20) = ',i7) + 990 format('tmow kpds(21) = ',i7,' kpds(22) = ',i7) + 991 format('tmow kpds(23) = ',i7,' kpds(24) = ',i7) + 992 format('tmow kpds(25) = ',i7) + 880 format('tmow kgds(1) = ',i7,' kgds(2) = ',i7) + 881 format('tmow kgds(3) = ',i7,' kgds(4) = ',i7) + 882 format('tmow kgds(5) = ',i7,' kgds(6) = ',i7) + 883 format('tmow kgds(7) = ',i7,' kgds(8) = ',i7) + 884 format('tmow kgds(9) = ',i7,' kgds(10) = ',i7) + 885 format('tmow kgds(11) = ',i7,' kgds(12) = ',i7) + 886 format('tmow kgds(13) = ',i7,' kgds(14) = ',i7) + 887 format('tmow kgds(15) = ',i7,' kgds(16) = ',i7) + 888 format('tmow kgds(17) = ',i7,' kgds(18) = ',i7) + 889 format('tmow kgds(19) = ',i7,' kgds(20) = ',i7) + 890 format('tmow kgds(20) = ',i7,' kgds(22) = ',i7) +c + print *,'just before call to putgb, kf= ',kf + call putgb (77,kf,kpds,kgds,lb,xmask,ipret) + print *,'just after call to putgb, kf= ',kf + if (ipret == 0) then + print *,' ' + print *,'+++ IPRET = 0 after call to putgb' + print *,' ' + else + print *,' ' + print *,'!!!!!! ERROR: IPRET NE 0 AFTER CALL TO PUTGB !!!' + print *,' ' + endif +c +c bug fix for IBM: flush the output stream so it actually writes + flush(6) + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_next_ges (fixlon,fixlat,ist,ifh,imax,jmax + & ,dx,dy,modelid,valid_pt,readflag,maxstorm,istmspd + & ,istmdir,ctype,trkrinfo,ignret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. It does this by using two different +c methods and averaging the results from those two. The +c first method is a simple linear extrapolation made by +c basically drawing a line from the previous position +c through the current fix position and assuming straight +c line motion. The second method is to do a barnes +c smoothing of u & v in the vicinity of the storm at 850, +c 700 & 500 mb to get an average environmental wind +c vector at each level, and then move the storm according +c to the vector at each level. Then a weighted average is +c taken of all these positions from methods 1 & 2 to get +c the consensus for the guess position. NOTE: For a +c regional model and a storm that is relatively close to +c the model boundary, there is a strong possibility that +c the barnes analysis subroutine will fail due to trying +c to access grid points beyond the model's lateral +c boundary. In this case, the redlm & ridlm are halved +c and barnes is called again. If it still fails, then +c just use the result from method 1 as a default. +c +c INPUT: +c fixlon Array with longitudes of fix positions +c fixlat Array with latitudes of fix positions +c ist Storm number currently being processed +c ifh Forecast hour currently being processed +c imax Max number of pts in x-direction for this grid +c jmax Max number of pts in y-direction for this grid +c dx grid-spacing of the model in the i-direction +c dy grid-spacing of the model in the j-direction +c modelid Integer indicating what model's data is being processed +c valid_pt Logical; bitmap indicating if valid data at that pt. +c readflag Logical; Tells whether or not a variable was read in +c for this model +c maxstorm Max # of storms that can be handled in this run +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, eventually +c in the barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c istmspd The speed that the storm would have to move to get from +c the current position to the next guess position +c istmdir The direction in which the storm would have to move to +c get from the current position to the next guess position +c +c LOCAL: +c dt Number of seconds between successive forecast times +c for this particular model. +c dtkm Distance in meters of 1 degree latitude +c icutmax Max number of times to cut the ridlm and redlm in half, +c for use in calling barnes. If you're using a regional +c model and on the first call to barnes you try to access +c a point that's outside the model grid boundary, we'll +c call barnes again, but not before cutting the redlm and +c ridlm in half. icutmax says how many times to allow +c this cutting in half before giving up and just going +c with the extrapolation method. At first writing, we'll +c set icutmax to 2, so that it will allow the ridlm to +c get down to 500 km (originally 2000 km) and the redlm +c to 125 km (originally 500 km). +c *** NOTE: After testing the system, it was found that if +c we cut these radii, the u and v values that are +c calculated from barnes are too strongly influenced by +c the near-storm environment and, especially for asymmetric +c systems, resulted in u and v values being much too strong. +c As such, we will not allow these values to be cut, and if +c we hit the boundaries in barnes, we'll just use the +c extrapolation method, which has seemed to work just fine. +c +c OTHER: (slonfg, slatfg & storm defined in module def_vitals) +c slonfg Array containing first guess longitude positions +c slatfg Array containing first guess latitude positions +c storm Contains tcvitals information +c + USE radii; USE def_vitals; USE set_max_parms; USE grid_bounds + USE tracked_parms; USE level_parms; USE trig_vals; USE trkrparms + USE gen_vitals + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer icutmax,istmspd,istmdir,bskip,ileadtime,ifcsthour + integer ifh,ist,npts,ilonfix,jlatfix,ibeg,jbeg,iend,jend + integer igiret,ignret,icut,iuret,ivret,ibarnct,n,ix1,ix2 + integer icount,imax,jmax,modelid,maxstorm + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real barneswt,extrapwt,dtkm,dt,ucomp,vcomp,xdist,ydist,ydeg + real extraplat,avglat,cosfac,xdeg,extraplon,ylatdegmove_last + real xlondegmove_last,xnumh_last,ylatdegmove_last_perhour + real xlondegmove_last_perhour,xnumh_next,yoldavglat + real yoldcosfac,xdistmove_last,xdistmove_last_perhour + real ynewavglat,ynewcosfac,xdegnew,dx,dy,re,ri,ubar,vbar + real wgttot,uavg,vavg,reold,riold,barnlat,barnlon,wt_total + real tmp_fix_lon_curr,tmp_fix_lon_prev + character*1 :: in_grid, extrap_flag, barnes_flag + character(*) ctype + logical(1) valid_pt(imax,jmax),readflag(14) +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c For updating the first guess, if Method 1 and Method 2 are both +c able to be done, give the following weights to the 2 methods. +c + data barneswt /0.50/, extrapwt /0.50/ +c +c ------------------------------- +c METHOD 1: LINEAR EXTRAPOLATION +c ------------------------------- +c First, just do a simple linear extrapolation from the previous +c fix position through the current fix position. If it's the +c first time (vt=0), then use the storm motion vector and storm +c speed information from the TC Vitals card. +c + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(ist)%tcv_stdir == -99 .or. + & storm(ist)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GET_NEXT_GES, at fcst hour = 0, either ' + print *,'!!! storm motion or storm speed = -99 on TCV card.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(ist)%tcv_stdir + print *,'!!! storm motion speed= ',storm(ist)%tcv_stspd + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + extrap_flag = 'n' + else + ucomp = sin(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + vcomp = cos(float(storm(ist)%tcv_stdir) * dtr) * + & float(storm(ist)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(ist,ifh) + ydeg + avglat = 0.5 * (extraplat + fixlat(ist,ifh)) + if (avglat > 89.5) avglat = 89.0 + if (avglat < -89.5) avglat = -89.0 + cosfac = cos(avglat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(ist,ifh) + xdeg + endif + else + +c Do a simple linear extrapolation of the current motion of the +c storm. Follow a line from the fix position from the last fix +c through the current fix and extrapolate out. To figure out the +c new latitude, just see how many deg lat the storm moved since +c last time and add it to the current fix latitude. To calculate +c the new fix longitude, though, we need to see how many deg lon +c the storm moved since the last time, convert that to the +c distance (km) the storm travelled in the x-direction (at an +c average latitude between the current and previous latitudes), +c and then add that distance on to the current longitude and +c convert that distance to the num of degrees the storm has +c travelled in the x-direction (at an average latitude between +c the current and next(extrap) latitudes). +c +c UPDATE Feb 2009: To account for the possibility of using +c irregularly spaced forecast hours (e.g., 6,10,10.5,...etc), +c I had to modify this linear extrapolation. + + print *,' ' + print *,'xxxx get_next_ges, prev fix lon= ',fixlon(ist,ifh-1) + print *,'xxxx get_next_ges, curr fix lon= ',fixlon(ist,ifh) + print *,' ' + + if (fixlat(ist,ifh-1) > -900.0 .and. + & fixlon(ist,ifh-1) > -900.0) then + + ylatdegmove_last = fixlat(ist,ifh) - fixlat(ist,ifh-1) + + tmp_fix_lon_curr = fixlon(ist,ifh) + tmp_fix_lon_prev = fixlon(ist,ifh-1) + + if (tmp_fix_lon_prev < 0.0 .and. tmp_fix_lon_prev > -25.0) + & then + ! previous lon position is within 25 deg west of the GM + ! and is listed in negative degrees. + if (tmp_fix_lon_curr < 0.0 .and. tmp_fix_lon_curr > -25.0) + & then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 1 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both negative. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr < 25.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 2 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous time' + print *,' is negative, while lon for current time' + print *,' is positive, but within 0-25 deg East.' + print *,' All ok!' + endif + endif + + elseif (tmp_fix_lon_prev > 335.0 .and. + & tmp_fix_lon_prev <= 360.0) then + ! previous lon position is within 25 deg west of the GM + ! and is listed in positive degrees. + if (tmp_fix_lon_curr > 335.0 .and. + & tmp_fix_lon_curr <= 360.0) then + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 3 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous and ' + print *,' current time are both positive. All ok!' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr > 0.0 .and. + & tmp_fix_lon_curr <= 25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 4 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between 0 & 25. Current tmp_lon' + print *,' has been adjusted to be > 360 for the' + print *,' purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < 0.0 .and. + & tmp_fix_lon_curr >= -25.0) then + tmp_fix_lon_curr = tmp_fix_lon_curr + 360.0 + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 5 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is west of the GM and' + print *,' is between 0 & -25. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + elseif (tmp_fix_lon_curr < -335.0 .and. + & tmp_fix_lon_curr >= -360.0) then + tmp_fix_lon_curr = 720.0 - tmp_fix_lon_curr + if (verb .ge. 3) then + print *,' ' + print *,'+++ GM WRAP ALERT 6 in get_next_ges.' + print *,'+++ In get_next_ges, lon for previous ' + print *,' time is between 335 & 360, while lon' + print *,' for current time is east of the GM and' + print *,' is between -335 & -360. Current tmp_lon' + print *,' has been adjusted to be positive and ' + print *,' > 360 for the purpose of computation.' + print *,' tmp_fix_lon_prev= ',tmp_fix_lon_prev + print *,' tmp_fix_lon_curr= ',tmp_fix_lon_curr + endif + + endif + + endif + + xlondegmove_last = tmp_fix_lon_curr - tmp_fix_lon_prev + + xnumh_last = fhreal(ifh) - fhreal(ifh-1) + + ylatdegmove_last_perhour = ylatdegmove_last / xnumh_last + xlondegmove_last_perhour = xlondegmove_last / xnumh_last + + xnumh_next = fhreal(ifh+1) - fhreal(ifh) + + extraplat = fixlat(ist,ifh) + & + (ylatdegmove_last_perhour * xnumh_next) + + yoldavglat = 0.5 * (fixlat(ist,ifh) + fixlat(ist,ifh-1)) + yoldcosfac = cos (dtr * yoldavglat) + xdistmove_last = xlondegmove_last * dtk * yoldcosfac + + xdistmove_last_perhour = xdistmove_last / xnumh_last + + ynewavglat = 0.5 * (extraplat + fixlat(ist,ifh)) + ynewcosfac = cos(dtr * ynewavglat) + xdegnew = (xdistmove_last_perhour * xnumh_next) + & / (dtk * ynewcosfac) + extraplon = tmp_fix_lon_curr + xdegnew + + else + + if ( verb .ge. 3 ) then + print *,' ' + write(6,92) '!!! IN GET_NEXT_GES, at fcst hour = ' + & ,ifhours(ifh),ifclockmins(ifh) + print *,'!!! the lon and lat positions for the previous' + print *,'!!! forecast hour are -999, meaning that this is a' + print *,'!!! new storm, so we cannot use the extrap method.' + print *,'!!! Storm name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS !!!' + endif + + 92 format (1x,a36,i4,':',i2.2) + + extrap_flag = 'n' + + endif + + endif + +c ------------------------------- +c METHOD 2: Barnes analysis +c ------------------------------- +c Do a barnes analysis on the u & v components of the wind near the +c storm to get an average u & v, then advect the storm according to +c the average wind vector obtained. The call to get_ij_bounds is +c needed in order to restrict the number of grid points that are +c searched in the barnes subroutine. See Abstract from this +c subroutine for further details. + + npts = ceiling(ridlm/(dtk*((dx+dy)/2))) + + call get_ij_bounds (npts,0,ridlm,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,fixlon(ist,ifh),fixlat(ist,ifh) + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges from call to ' + print *,'!!! get_ij_bounds, STOPPING processing for ' + print *,'!!! storm number ',ist + endif + + ignret = 92 + return + endif + + if (verb >= 3) then + print *,' ' + print *,' +++ In get_next_ges after call to get_ij_bounds,' + print *,' getting bounds for the barnes analysis...' + print *,' glatmax= ',glatmax,' glatmin= ',glatmin + print *,' glonmax= ',glonmax,' glonmin= ',glonmin + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + print *,' ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + endif + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if ((dx+dy)/2 > 0.20) then + bskip = 1 + else if ((dx+dy)/2 > 0.10 .and. (dx+dy)/2 <= 0.20) then + bskip = 2 + else if ((dx+dy)/2 > 0.05 .and. (dx+dy)/2 <= 0.10) then + bskip = 3 + else if ((dx+dy)/2 > 0.03 .and. (dx+dy)/2 <= 0.05) then + bskip = 5 + else if ((dx+dy)/2 <= 0.03) then + bskip = 10 + endif + +c Calculate average wind at each level (currently: 850, 700 & 500) + + re = redlm + ri = ridlm + icut = 0 + + if (trkrinfo%type == 'midlat') then + icutmax = 2 + else + icutmax = 1 + endif + + radmaxloop: do while (icut <= icutmax .and. in_grid == 'n') + + ubar = 0.0; vbar = 0.0 + iuret = 0; ivret = 0 + wgttot = 0.0 + ibarnct = 0 + barnes_flag = 'n' + + levelloop: do n=1,nlevg + + select case (n) + case (1); ix1=3; ix2=4 ! For 850 mb readflags + case (2); ix1=5; ix2=6 ! For 700 mb readflags + case (3); ix1=12; ix2=13 ! For 500 mb readflags + end select + + if (readflag(ix1) .and. readflag(ix2)) then + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,u(1,1,n),valid_pt + & ,bskip,re,ri,uavg,icount,ctype,trkrinfo,iuret) + + call barnes (fixlon(ist,ifh),fixlat(ist,ifh),glon,glat + & ,imax,jmax,ibeg,jbeg,iend,jend,v(1,1,n),valid_pt + & ,bskip,re,ri,vavg,icount,ctype,trkrinfo,ivret) + + if (iuret /= 0 .or. ivret /= 0) then + +c ...barnes probably tried to access a pt outside the grid +c domain. So, reduce by half the distance from the center +c of the farthest pt that barnes tries to access, exit this +c loop, and try it again with the smaller re and ri. + + iuret = 96; ivret = 96 + reold = re + riold = ri + re = 0.5 * re + ri = 0.5 * ri + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: While attempting to use the barnes ' + print *,'method to update the first guess, the ' + print *,'algorithm tried to access a grid point that ' + print *,'does not have valid data, meaning that too ' + print *,'large a radius is being searched. So, the 2 ' + print *,'radii, re and ri, are being halved and, if the' + print *,'value of icutmax > 0, the algorithm will be ' + print *,'run again. Otherwise, if icutmax = 0, only ' + print *,'the extrapolation method will be used.' + print *,'iuret= ',iuret,' ivret= ',ivret,' icut= ',icut + print *,'Old re = ',reold,' New re = ',re + print *,'Old ri = ',riold,' New ri = ',ri + endif + + exit levelloop + + else + ubar = ubar + wgts(n) * uavg + vbar = vbar + wgts(n) * vavg + wgttot = wgttot + wgts(n) + ibarnct = ibarnct + 1 + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, ix1= ',ix1,' ix2= ',ix2 + print *,' uavg= ',uavg,' vavg= ',vavg + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' n= ',n,' wgts(n)= ',wgts(n),' wgttot= ' + & ,wgttot + print *,' ibarnct= ',ibarnct + print *,' ' + print *,' ' + endif + endif + + endif + + enddo levelloop + + if (ibarnct > 0 .and. wgttot > 0.0) then + barnes_flag = 'y' + in_grid = 'y' + ubar = ubar / wgttot + vbar = vbar / wgttot + barnlat = fixlat(ist,ifh) + (vbar * dt)/dtkm + cosfac = cos (dtr * 0.5 * (fixlat(ist,ifh) + barnlat)) + barnlon = fixlon(ist,ifh) + (ubar * dt)/(dtkm * cosfac) + + if (verb >= 3) then + print *,' ' + print *,' --- In get_next_ges, mean stats follow: ' + print *,' ubar= ',ubar,' vbar= ',vbar + print *,' wgttot= ',wgttot + print *,' fixlon= ',fixlon(ist,ifh),' fixlat= ' + & ,fixlat(ist,ifh) + print *,' barnlon= ',barnlon,' barnlat= ',barnlat + print *,' dt= ',dt,' dtkm= ',dtkm,' cosfac= ',cosfac + endif + + +c This next if statement says that if we've had to reduce the +c size of the barnes analysis domain twice already, then we've +c only done the analysis on a much smaller area, and this +c doesn't give us as good a picture of the average winds in the +c area of the storm, so reduce the emphasis we place on the +c barnes method. + + if (icut >= 2) barneswt = barneswt / 2. + + else + barnes_flag = 'n' + endif + + icut = icut + 1 + + enddo radmaxloop + +c --------------------- +c Average the results +c --------------------- +c Now do a weighted average of the positions obtained from the +c linear extrapolation and the barnes analysis methods. + + if (extrap_flag == 'y' .and. barnes_flag == 'y') then + wt_total = barneswt + extrapwt + slatfg(ist,ifh+1) = (barneswt * barnlat + extrapwt * extraplat) + & / wt_total + + ! Note that in any of these statements just below, in order for + ! any of these to be > 360, the original fixlon must be close + ! to 360, i.e., in the far eastern part of the grid, as opposed + ! to being in the far western part (e.g., 0-2 deg East or so). + ! Conversely, for any of these to be < 0, the original fixlon + ! must be close to 0, i.e., in the far *western* part of the + ! grid. + +c yyyy + + if (fixlon(ist,ifh) > 330.0) then + + ! In this part of the IF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being 330+, to be consistent with the fixlon for + ! this time. + + if (extraplon > 330. .and. barnlon > 330.) then + + continue ! All lons will be in the 300+ range, so for + ! consistency, we're ok. + + elseif (extraplon > 330. .and. + & (barnlon >= 0.0 .and. barnlon < 30.)) then + + ! extraplon > 330, but barnlon is in the 0-30 range, so + ! we need to convert the barnlon value to be 360+ + + barnlon = barnlon + 360. + + elseif (extraplon > 330. .and. barnlon < 0.) then + + ! extraplon > 330, but barnlon is < 0, so + ! we need to convert the barnlon value to be positive... + + barnlon = barnlon + 360. + + elseif (barnlon > 330. .and. + & (extraplon >= 0.0 .and. extraplon < 30.)) then + + ! barnlon > 330, but extraplon is in the 0-30 range, so + ! we need to convert the extraplon value to be 360+ + + extraplon = extraplon + 360. + + elseif (barnlon > 330. .and. extraplon < 0.) then + + ! barnlon > 330, but extraplon is < 0, so + ! we need to convert the extraplon value to be positive... + + extraplon = extraplon + 360. + + endif + + elseif (fixlon(ist,ifh) >= 0. and. fixlon(ist,ifh) < 30.0) then + + ! In this part of the ELSEIF, we will make sure that the two + ! guess lons (barnlon and extraplon) are consistent as + ! both being in the reference of >360 since that is what the + ! code below this is expecting with the computation of + ! slonfg for the next lead time. + + if ((extraplon >= 0. .and. extraplon < 60.) .and. + & (barnlon >= 0. .and. barnlon < 60.)) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon < 0. .and. extraplon > -60.) .and. + & (barnlon < 0. .and. barnlon > -60.)) then + + ! convert extraplon and barnlon to be positive + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon < 0.) then + + extraplon = extraplon + 360. + barnlon = barnlon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon < 0.) then + + extraplon = extraplon + 360. + + elseif ((barnlon >= 0. .and. barnlon < 60.) .and. + & extraplon > 330.) then + + barnlon = barnlon + 360. + + elseif (barnlon >= 330. .and. extraplon < 60.) then + + extraplon = extraplon + 360. + + elseif (extraplon >= 330. .and. barnlon < 60.) then + + barnlon = barnlon + 360. + + elseif ((extraplon >= 0. .and. extraplon < 60.) .and. + & barnlon > 330.) then + + extraplon = extraplon + 360. + + endif + + else + + continue ! extraplon and barnlon do not need to be modified + ! since there should be no way that a storm + ! currently east of 30E and west of 30W could make + ! it to the Greenwich Mer in one forecast interval + + endif + + print *,' ' + print *,'+++ In get_next_ges, before averaging the 2 methods, ' + print *,' Raw (no conversion for GM wrap) barnlon= ' + & ,barnlon + print *,' Raw (no conversion for GM wrap) extraplon= ' + & ,extraplon + + slonfg(ist,ifh+1) = (barneswt * barnlon + extrapwt * extraplon) + & / wt_total + + if (slonfg(ist,ifh+1) > 360.) then + ! If we've GM-wrapped past 360, adjust it to be 0-360... + slonfg(ist,ifh+1) = slonfg(ist,ifh+1) - 360. + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'y' .and. barnes_flag == 'n') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_next_ges, barnes method was not ' + print *,'!!! done for updating the first guess for this ' + print *,'!!! storm. Only the linear extrapolation method ' + print *,'!!! was used.' + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + endif + + slatfg(ist,ifh+1) = extraplat + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, extraplon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 0.0,0.0,0.0 + if (extraplon >= 360.) then + write (6,43) extraplon-360.,720.-extraplon,barnlat + elseif (extraplon >= 0. .and. extraplon < 360.) then + write (6,43) extraplon,360.-extraplon,barnlat + elseif (extraplon < 0.) then + write (6,43) extraplon+360.,-1.*extraplon,barnlat + endif + endif + + ignret = 0 + else if (extrap_flag == 'n' .and. barnes_flag == 'y') then + slatfg(ist,ifh+1) = barnlat + if (barnlon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon - 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon >360' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + elseif (barnlon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(ist,ifh+1) = barnlon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_next_ges, barnlon < 0' + print *,'!!! for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + ignret = 95 + return + endif + else + slonfg(ist,ifh+1) = barnlon + endif + + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,41) 360.-barnlon,barnlat + if (barnlon >= 360.) then + write (6,41) barnlon-360.,720.-barnlon,barnlat + elseif (barnlon >= 0. .and. barnlon < 360.) then + write (6,41) barnlon,360.-barnlon,barnlat + elseif (barnlon < 0.) then + write (6,41) barnlon+360.,-1.*barnlon,barnlat + endif + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_next_ges, new position guess not' + print *,'!!! made. Could not get guess using either barnes' + print *,'!!! method or extrapolation method.' + print *,'!!! extrap_flag = ',extrap_flag + print *,'!!! barnes_flag = ',barnes_flag + print *,'!!! Storm number = ',ist,' ifh = ',ifh + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + write (6,41) 0.0,0.0,0.0 + write (6,43) 0.0,0.0,0.0 + endif + + ignret = 95 + endif + + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| Current fix & updated fix positions |' + print *,'-------------------------------------------------- ' + print *,'| In get_next_ges, current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',ist + print *,'| Return code from get_next_ges = ',ignret + print *,'| Storm Name = ',storm(ist)%tcv_storm_name + print *,'| Storm ID = ',storm(ist)%tcv_storm_id + write (6,420) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + write (6,21) fixlat(ist,ifh) + write (6,23) 360.-fixlon(ist,ifh),fixlon(ist,ifh) + write (6,25) slatfg(ist,ifh+1) + write (6,27) 360.-slonfg(ist,ifh+1),slonfg(ist,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current fix lat is ',f7.2) + 23 format (' | Current fix lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') +c 41 format (' --- barnlon= ',f7.2,'W barnlat= ',f7.2) +c 43 format (' --- extraplon= ',f7.2,'W extraplat= ',f7.2) + + 41 format (' --- barnlon= ',f7.2,'E (',f7.2 + & ,'W) barnlat= ',f7.2) + 43 format (' --- extraplon= ',f7.2,'E (',f7.2 + & ,'W) extraplat= ',f7.2) + +c Now calculate the speed that the storm would have to move at in +c order to make it to the next forecast position. We will use +c this information in writing out the "gen_vitals" record, if this +c is requested. + + call calcdist (fixlon(ist,ifh),fixlat(ist,ifh) + & ,slonfg(ist,ifh+1),slatfg(ist,ifh+1),dist,degrees) + + ! convert distance from km to meters, then get speed in m/s. + + distm = dist * 1000. + stmspd = distm / dt + istmspd = int ((stmspd * 10) + 0.5) + + xincr = slonfg(ist,ifh+1) - fixlon(ist,ifh) + yincr = slatfg(ist,ifh+1) - fixlat(ist,ifh) + + if ( verb .ge. 3 ) then + print *,'iocheck, dist= ',dist,' distm= ',distm + print *,'iocheck, stmspd= ',stmspd,' istmspd= ',istmspd + print *,'iocheck, xincr= ',xincr,' yincr= ',yincr + endif + + if (xincr < 0.0 .and. slonfg(ist,ifh+1) < 30.0 .and. + & fixlon(ist,ifh) > 300.0) then + ! This means we have a storm moving east across the GM, and + ! so we are subtracting, for example, something like + ! 0.5 - 359.5, so redo xincr, but add 360 to slonfg first... + xincr = (slonfg(ist,ifh+1) + 360.0) - fixlon(ist,ifh) + else if (xincr > 300.0) then + ! This means we have a storm moving west across the GM, and + ! so we are subtracting, for example, something like + ! 359.5 - 0.5, so redo xincr, but add 360 to fixlon first... + xincr = slonfg(ist,ifh+1) - (fixlon(ist,ifh) + 360.0) + endif + + if (xincr == 0.0) then + if (yincr == 0.0) then + stmdir = 0.0 + else if (yincr > 0) then + stmdir = 360.0 + else if (yincr < 0) then + stmdir = 180.0 + endif + else if (xincr > 0.0) then + if (yincr == 0.0) then + stmdir = 90.0 + else + arct = atan(yincr/xincr) + stmdir = 90. - arct / dtr + endif + else if (xincr < 0.0) then + if (yincr == 0.0) then + stmdir = 270.0 + else + arct = atan(yincr/xincr) + stmdir = 270. - arct / dtr + endif + endif + + istmdir = int (stmdir + 0.5) + if (istmdir > 360) then + istmdir = 360 + else if (istmdir < 0) then + istmdir = 0 + endif + + if ( verb .ge. 3 ) then + print *,'iocheck, stmdir= ',stmdir,' istmdir= ',istmdir + endif + + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine advect_tcvitals_from_hour0 (fixlon,fixlat,maxstorm + & ,inctcv,ifh,trkrinfo,iatret) +c +c ABSTRACT: This subroutine calculates a guess position for the next +c forecast time. As of 11/2016, it is called only for the case in +c which we've got NetCDF data and no hour0 data, and so we want to +c simply take the TC Vitals data and advect the current position to +c a position at the next lead time. We can't use the code in +c subroutine get_next_ges because there are certain allocatable +c arrays in that subroutine that need to have been allocated first, +c and at this point prior to the first lead time in hour0, they +c haven't been allocated. +c +c INPUT: +c inctcv Index for storm number currently being processed +c ifh Forecast hour currently being processed +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c iatret Return code from this subroutine +c + USE def_vitals; USE trkrparms; USE tracked_parms + USE verbose_output; USE trig_vals; USE set_max_parms + USE gen_vitals + + type (trackstuff) trkrinfo + integer iatret,inctcv + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real dist,distm,xincr,yincr,stmspd,stmdir,atan,arct,degrees + real ucomp,vcomp,xdist,ydist,ydeg,dt,extraplat + real cosfac + real dtkm +c + in_grid = 'n' + extrap_flag = 'y' + + ileadtime = nint(fhreal(ifh) * 100.0) + ifcsthour = ileadtime / 100 +c +c ------------------------------------------------------------------ +c Using the storm motion vector and storm translation speed as read +c from the TC Vitals card, do a simple linear extrapolation from the +c current observed (TC Vitals) position and advect the storm to a +c position at the next lead time. +c ------------------------------------------------------------------ + + iatret = 0 + + dtkm = dtk * 1000. + dt = (fhreal(ifh+1) - fhreal(ifh)) * 3600.0 +c + if (ifh == 1) then + if (storm(inctcv)%tcv_stdir == -99 .or. + & storm(inctcv)%tcv_stspd == -99) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In advect_tcvitals_from_hour0, at fcst hour= 0' + print *,'!!! either storm motion or storm speed = -99 on ' + print *,'!!! TCV card, ist= inctcv= ',inctcv,' ifh= ',ifh + print *,'!!! Storm name = ',storm(inctcv)%tcv_storm_name + print *,'!!! Storm ID = ',storm(inctcv)%tcv_storm_id + print *,'!!! storm motion vector= ',storm(inctcv)%tcv_stdir + print *,'!!! storm motion speed= ',storm(inctcv)%tcv_stspd + print *,'... CANNOT USE LINEAR EXTRAP TO GET NEXT GUESS ...' + print *,' ' + print *,'... Instead, we will simply use the current ' + print *,'... observed position from TC Vitals and hope that' + print *,'... it is close enough at the next lead time for ' + print *,'... the tracker to be able to still track it.' + print *,' ' + endif + extraplat = slatfg(inctcv,ifh) + extraplon = slonfg(inctcv,ifh) + else + ucomp = sin(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + vcomp = cos(float(storm(inctcv)%tcv_stdir) * dtr) * + & float(storm(inctcv)%tcv_stspd)/10.0 + xdist = ucomp * dt + ydist = vcomp * dt + ydeg = ydist / dtkm + extraplat = fixlat(inctcv,ifh) + ydeg + cosfac = cos(extraplat * dtr) + xdeg = xdist / (dtkm*cosfac) + extraplon = fixlon(inctcv,ifh) + xdeg + endif + else + print *,' ' + print *,'!!! ERROR: In advect_tcvitals_from_hour0, the value of' + print *,' ifh is > 1, and this routine should only be called' + print *,' if ifh=1 (i.e., for hour0). STOPPING....' + print *,' ' + stop 95 + endif + + slatfg(inctcv,ifh+1) = extraplat + + if (extraplon > 360.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon - 360. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon >360 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + elseif (extraplon < 0.) then + if (trkrinfo%gridtype == 'global') then + slonfg(inctcv,ifh+1) = extraplon + 360. + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in advect_tcvitals_from_hour0,' + print *,'!!! extraplon < 0 for non-global grid. We only' + print *,'!!! do GM wrapping for global grids.' + endif + + iatret = 95 + return + endif + else + slonfg(inctcv,ifh+1) = extraplon + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'-------------------------------------------------- ' + print *,'| In advect_tcvitals_from_hour0, info on the ' + print *,'| positions for the current and next lead times ' + print *,'| follow: ' + print *,'-------------------------------------------------- ' + print *,'| current fcst hour = ',fhreal(ifh) + print *,'| current storm number = ',inctcv + print *,'| Return code from advect_tcvitals_from_hour0= ',iatret + print *,'| Storm Name = ',storm(inctcv)%tcv_storm_name + print *,'| Storm ID = ',storm(inctcv)%tcv_storm_id + write (6,420) gstorm(inctcv)%gv_gen_date + & ,gstorm(inctcv)%gv_gen_fhr + & ,gstorm(inctcv)%gv_gen_lat + & ,gstorm(inctcv)%gv_gen_latns,gstorm(inctcv)%gv_gen_lon + & ,gstorm(inctcv)%gv_gen_lonew,gstorm(inctcv)%gv_gen_type + write (6,21) fixlat(inctcv,ifh) + write (6,23) 360.-fixlon(inctcv,ifh),fixlon(inctcv,ifh) + write (6,25) slatfg(inctcv,ifh+1) + write (6,27) 360.-slonfg(inctcv,ifh+1),slonfg(inctcv,ifh+1) + print *,'-------------------------------------------------' + print *,' ' + endif + + 420 format (' | Gen ID (if available): ',i10.10,'_F',i3.3,'_' + & ,i3.3,a1,'_',i4.4,a1,'_',a3) + 21 format (' | Current TC Vitals lat is ',f7.2) + 23 format (' | Current TC Vitals lon is ',f7.2,'W (',f7.2,'E)') + 25 format (' | Updated guess lat for next fcst hour is ',f7.2) + 27 format (' | Updated guess lon for next fcst hour is ',f7.2 + & ,'W (',f7.2,'E)') + + + return + end +c +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getradii (xcenlon,xcenlat,imax,jmax,dx,dy,valid_pt + & ,cstormid,ifcsthr,vmaxwind,vradius,trkrinfo + & ,need_to_expand_r34,radmax + & ,first_time_thru_getradii,igrct,igrret) +c +c ABSTRACT: This subroutine looks through the wind data near an +c input storm center (fixlon,fixlat) and gets the radii of various +c surface winds in each of the 4 storm quadrants (NE,NW,SE,SW). +c The wind thresholds that are sought are gale force (34kt|17.5m/s), +c storm force (50kt|25.7m/s), and hurricane force (64kt|32.9m/s). +c This subroutine calls the Cray subroutine orders, which is a +c Cray-optimized sort routine. +c +c UPDATE (AUG 2001): The Cray subroutine orders was ported to the +c SP by NCEP personnel. On the SP version, some changes were +c apparently made so that the size of the arrays for calling +c arguments 2, 3 and 4 (iwork, dtemp and isortix in my calling +c routine) must be the same. This was not the case on the Crays, +c and this was causing the tracker to crash for cases far north +c on fine grids (GFDL 1/3 grid). +c +c UPDATE (AUG 2012): The call to the Cray subroutine orders was +c replaced with a call to qsort, which uses a quicksort sorting +c algorithm. While this is not the fastest sorting routine out +c there, we don't do a lot of sorting here, and qsort is simple +c and it is portable. +c +c UPDATE (April 2013): For the radii, we encountered a problem with +c radmax being too small. It was set at 650 km. Hurricane Sandy +c exceeded this in the models, so the values returned from getradii +c were close to the default radmax value of 650 km (350 nm), instead +c of much higher as they should have been. To fix it, we now use an +c iterative technique, where we start with radmax as a small value +c (450 km). If getradii returns a value for R34 in a quadrant that +c does not exceed 0.97*radmax, then that value is ok. If it does +c exceed 0.97*radmax, then we bump up radmax by 50 km and call +c getradii again, looking to diagnose radii only in those quadrants +c where the need_to_expand_r34 flag = 'n'. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c cstormid 3-character storm ATCF ID (e.g., 03L, 11E, etc) +c ifcsthr integer value for current forecast hour +c trkrinfo derived type containing various info on the storm +c need_to_expand_r34 1-character array that specifies which of the +c 4 quadrants still need to be expanded on this time +c through getradii in order to get an R34 value that is +c not right at the outermost boundary. +c vmaxwind max wind (in m/s) that was reported from the +c get_max_wind subroutine +c radmax input max radius (km) that will be used for this +c iteration of getradii. +c first_time_thru_getradii logical flag. It is used so that any +c checking for 50- or 64-kt radii is only done on the +c first time through getradii. Only the checking for +c 34-kt radii is done on multiple iterations. +c igrct integer that indicates what iteration of getradii this +c call is. +c +c OUTPUT: +c +c igrret return code from this subroutine +c vradius Contains the distance from the storm fix position to +c each of the various wind threshhold distances in each +c quadrant. (3,4) ==> (# of threshholds, # of quadrants) +c +c LOCAL: +c +c radmax the maximum radius to look for winds for the various +c thresholds. +c quadinfo This array contains the magnitude of the near-surface +c winds and the distance from the gridpoint to the fix +c position for each point in each quadrant that is within +c the maximum allowed radius, radmax. quadinfo is +c allocated within this subroutine, and is allocated as +c (quadrant, num_pts_in_quadrant, data_type), where +c data_type is either windspeed(1) or distance(2) from +c storm center to grid point. +c quadmax This array contains the max surface wind in each +c quadrant, plus the location of it and the distance from +c the storm center. This information is critical to +c identifying when this subroutine is malfunctioning. + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE level_parms + USE trkrparms + USE verbose_output + +c + type (trackstuff) trkrinfo +c + logical(1) valid_pt(imax,jmax) + logical(1) first_time_thru_getradii +c dimension iwork(257) + real, allocatable :: quadinfo(:,:,:),iwork(:) + real quadmax(4,4) + real exactdistnm,exactdistkm,radmax,degrees,cosarg + real rlonb,rlonc,rlatb,rlatc,vmaxwind + real pt_heading_rad,pt_heading,d + integer, allocatable :: isortix(:) + integer iwindix,ipoint,ifcsthr,igrct + integer quadct(4),vradius(3,4) + integer, parameter :: dp = selected_real_kind(12, 60) + real (dp), allocatable :: dtemp(:) + real :: windthresh(3) = (/17.5,25.7,32.9/) + character cstormid*3 + character :: need_to_expand_r34(4)*1 + + if ( verb .ge. 3 ) then + print *,' ' + print *,' *************************************************** ' + print *,' AT BEGINNING OF GETRADII, input radmax= ',radmax + print *,' *************************************************** ' + print *,' ' + print *,'xcenlon= ',xcenlon,' xcenlat= ',xcenlat + print *,'imax= ',imax,' jmax= ',jmax,' dx= ',dx,' dy= ',dy + endif + + igrret = 0 + +c ----------------------------------------------------------- +c PART 1: Define the maximum radius for which you'll search +c for the wind values, and then get the beginning and ending +c i and j points for that sub-region to search. Define this +c maximum radius (radmax) in terms of km. +c ----------------------------------------------------------- + +c radmax = 650.0 ! This value is in units of km. With April 2013 +c ! update, this is now defined in calling routine + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-A....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine getradii' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-B....' + stop 98 + endif + + igrret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmax is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmax/(dtk*dx))/cosfac) + numjpts = ceiling(radmax/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (ibeg < 1) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-C...' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jbeg < 1) jbeg = 1 + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in getradii calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Wind radii will not be calculated for this time.' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-D....' + stop 98 + endif + + igrret = 99 + return + endif + + if (iend > imax) then + + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below in this + ! getradii routine that can modify the indices + ! appropriately. So... do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ilonfix = ',ilonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Radii will not be computed for this time.' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-E....' + stop 98 + endif + + igrret = 99 + return + endif + + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getradii, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + +c ----------------------------------------------------------- +c PART 2: Within the area of grid points defined by jbeg, +c jend, ibeg and iend, (1) calculate all the wind speeds at +c each grid point, (2) calculate all of the distances from +c each grid point to the storm center, (3) assign each grid +c point to one of the 4 quadrants (NE,NW,SE,SW), (4) in each +c quadrant, sort the points, based on windspeed. +c ----------------------------------------------------------- + + jnum = jend - jbeg + 1 + inum = iend - ibeg + 1 +c numalloc = ((jnum * inum) / 2) + inum/2 + jnum/2 + numalloc = jnum * inum + inum/2 + jnum/2 + + if ( verb .ge. 3 ) then + print *,'in getradii, numalloc= ',numalloc,' radmax= ',radmax + endif + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + allocate (quadinfo(4,numalloc,2),stat=iqa) + + if (iqa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub getradii allocating quadinfo array.' + print *,'!!! iqa = ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-F....' + stop 98 + endif + + igrret = 94 + return + endif + + quadct = 0 + +c Calculate the distances and wind speeds at each grid point. If +c the distance is < radmax, include that wind info in the +c appropriate quadinfo array location for that quadrant. + + quadmax = 0.0 + + jloop: do j=jbeg,jend + iloop: do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In getradii, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point in question = ',i + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-G...' + stop 98 + endif + + igrret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub getradii' + print *,'!!! for a non-global grid. i= ',i + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Radii will not be computed for this time' + print *,' ' + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-H....' + stop 98 + endif + + igrret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + if (dist > radmax) cycle iloop + + if (valid_pt(ip,j)) then + + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + +cc print *,'i= ',i,' j= ',j,' dist= ',dist,' vmag= ',vmag + + ! Calculate the angle from the center point to this point + ! and then assign this point to the appropriate quadrant bin + + rlonc = (360.-glon(ip)) * dtr + rlatc = glat(j) * dtr + rlonb = (360.-xcenlon) * dtr + rlatb = xcenlat * dtr + d = degrees * dtr + +c write (6,59) 360.-xcenlon,xcenlat,360.-glon(ip),glat +c +c write (6,61) d/dtr,rlatc/dtr,360.-(rlonc/dtr),rlatb/dtr +c & ,360.-(rlonb/dtr),sin(rlatc),sin(rlatb),cos(d) +c & ,sin(d),cos(rlatb) +c +c +c 59 format (1x,'+++ gr, xcenlon= ',f8.3,'W xcenlat= ' +c & ,f8.3,' glon= ',f8.3,'W glat= ',f8.3) +c +c 61 format (1x,'+++ gr, d rlatc rlonc rlatb rlonb= ',5f9.4 +c & ,' sin(rlatc)= ',f8.6,' sin(rlatb)= ',f8.6 +c & ,' cos(d)= ',f8.6,' sin(d)= ',f8.6 +c & ,' cos(rlatb)= ',f8.6) + + if (d == 0.0) then + + pt_heading = 0.0 + + else + + cosarg = (sin(rlatc)-sin(rlatb)*cos(d)) / + & (sin(d)*cos(rlatb)) + if (cosarg > 1.0) cosarg = 1 + if (cosarg < -1.0) cosarg = -1 + + if (sin(rlonc-rlonb) < 0.0) then + pt_heading_rad = acos(cosarg) + else + pt_heading_rad = 2*pi - acos(cosarg) + endif + + pt_heading = pt_heading_rad / dtr + + endif + + if (pt_heading >= 0.0 .and. pt_heading < 90.) then + ! NE quadrant + iq = 1 + else if (pt_heading >= 90.0 .and. pt_heading < 180.) then + ! SE quadrant + iq = 2 + else if (pt_heading >= 180.0 .and. pt_heading < 270.) then + ! SW quadrant + iq = 3 + else if (pt_heading >= 270.0 .and. pt_heading <= 360.) then + ! NW quadrant + iq = 4 + endif + +c write (6,73) xcenlat,360.-xcenlon,j,i,ip,glat(j) +c & ,360.-glon(ip),pt_heading,iq + + 73 format (1x,'+++ getradii clat clon: ',f6.2,' ',f7.2,'W',3i4 + & ,' plat plon: ',f6.2,' ',f7.2,'W Dir: ',f7.2 + & ,' Quad: ',i2) + + quadct(iq) = quadct(iq) + 1 + quadinfo(iq,quadct(iq),1) = vmag + quadinfo(iq,quadct(iq),2) = dist + if (vmag > quadmax(iq,4)) then + quadmax(iq,1) = glon(ip) + quadmax(iq,2) = glat(j) + quadmax(iq,3) = dist + quadmax(iq,4) = vmag + endif + + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After loop, quadct(1)= ',quadct(1),' quadct(2)= ' + & ,quadct(2) + print *,' quadct(3)= ',quadct(3),' quadct(4)= ' + & ,quadct(4) + print *,' ' + + write (6,110) cstormid,ifcsthr,'NE',quadmax(1,1),quadmax(1,2) + & ,quadmax(1,3)*0.539638,quadmax(1,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SE',quadmax(2,1),quadmax(2,2) + & ,quadmax(2,3)*0.539638,quadmax(2,4)*1.9427 + write (6,110) cstormid,ifcsthr,'SW',quadmax(3,1),quadmax(3,2) + & ,quadmax(3,3)*0.539638,quadmax(3,4)*1.9427 + write (6,110) cstormid,ifcsthr,'NW',quadmax(4,1),quadmax(4,2) + & ,quadmax(4,3)*0.539638,quadmax(4,4)*1.9427 + print *,' ' + + 110 format (' quadmax: ',a3,1x,i3.3,1x,a2,1x,' lon: ',f6.2,'E',1x + & ,' lat: ',f6.2,' radius: ',f7.2,' nm',2x,' vmag: ' + & ,f6.2,' kts') + endif + +c Now go through each quadrant and put the wind speed distance info +c into a temporary array (dtemp), sort that array, and then scan +c through that array to find the various thresholds. + + quadrantloop: do k=1,4 + + if (need_to_expand_r34(k) == 'y') then + print *,'---> R34 search underway for quadrant ',k + & ,' radmax= ',radmax + continue + else + print *,'+ R34 okay for quadrant ',k,'... skipping...' + cycle quadrantloop + endif + + if (allocated(isortix)) deallocate (isortix) + if (allocated(dtemp)) deallocate (dtemp) + if (allocated(iwork)) deallocate (iwork) + allocate (isortix(quadct(k)),stat=iisa) + allocate (dtemp(quadct(k)),stat=idta) + allocate (iwork(quadct(k)),stat=iwa) + if (iisa /= 0 .or. idta /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii allocating isortix, dtemp' + print *,'!!! or iwork array for quadrant= ',k + print *,'!!! iisa = ',iisa,' idta= ',idta,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-I....' + stop 98 + endif + + itret = 94 + return + endif + +c ------------------- + + do m=1,quadct(k) + dtemp(m) = quadinfo(k,m,2) + enddo + + imode = 2 + isortix = 0 + + call qsort (dtemp,isortix,quadct(k)) + +ccccc call orders (imode,iwork,dtemp,isortix,quadct(k),1,8,1) +cccc call orders_4byte (imode,iwork,dtemp,isortix +cccc & ,quadct(k),1,8,1) + + if ( verb .ge. 3 ) then + print *,' ' +c ************************************************************** +c--- mf 20100609 +c CAUSE OF SEG FAULT!!!!!!!! -- not sure still an issue if dtemp +c properly allocated +c + !print *,' dtemp(isortix(1)) = ',dtemp(isortix(1)) + print *,' dtemp(isortix(quadct(k)))= ' + & ,dtemp(isortix(quadct(k))) + print *,' isortix(1) = ',isortix(1) + print *,' isortix(quadct(k)) = ',isortix(quadct(k)) + endif + +c ! Uncomment these next lines to see a listing in the output of +c ! all wind values & distances in this quadrant less than radmax +c do iqq = 1,quadct(k) +c print *,' iqq= ',iqq,' vmag= ',quadinfo(k,isortix(iqq),1) +c & ,' dist= ',quadinfo(k,isortix(iqq),2) +c enddo + +c ------------------- + + if (quadct(k) < 2) then ! not enough members in array + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN GETRADII, NOT ENOUGH MEMBERS IN ARRAY FOR' + print *,'!!! QUADRANT #',k,' .... # members = quadct(k)= ' + & ,quadct(k) + print *,'!!! SETTING ALL VRADII = 0 for quadrant = ',k + endif + + vradius(1,k) = 0 + vradius(2,k) = 0 + vradius(3,k) = 0 + cycle quadrantloop + endif + +c Within this quadrant, go through the sorted array of wind +c magnitudes and compare those wind values against the set +c wind thresholds to get the wind radii. The array has +c been sorted by distance from the storm center in order of +c closest (ipoint=1) to farthest (ipoint=quadct(k)). We +c analyze these wind values by starting at the farthest +c point and moving inward until we hit a point that has a +c wind value of at least 34-knot winds (17.5 m/s). When +c we find that point, we interpolate between that point and +c the next farthest out point to get the distance that would +c be for the exact 17.5 m/s value. We then continue searching +c through the wind values down closer to the storm center to +c see if we can find values for the 50- and 64-knot winds. + + iwindix = 1 + ipoint = quadct(k) + 1 + +c print *,'drp: quad= ',k,' quadct= ',quadct(k) + + threshloop: do while (iwindix <= 3 .and. ipoint > 1) + + if (iwindix > 1) then + if (first_time_thru_getradii) then + + ! We are only doing the wind radii for 50 and 64 kts on + ! the first time through subroutine getradii (we only + ! need to do the multiple call iterations for 34 kts). + ! + ! Make sure vmax for this lead time exceeds the radii + ! threshold being diagnosed. The check below avoids, + ! for example, reporting 50-kt wind radii when the max + ! wind diagnosed was only 44 kts. This can happen since + ! the radius for searching for radii is larger than the + ! radius for searching for the max wind. + if (vmaxwind >= windthresh(iwindix)) then + if (verb >= 3) then +c print *,' ' +c print *,' +++ vmaxwind of ',vmaxwind,' m/s exceeds' +c print *,' +++ threshold of ',windthresh(iwindix) +c print *,' +++ (m/s), so radii checking will continue' +c print *,' +++ for this threshold.' +c print *,' +++ igrct= ',igrct,' ipoint= ',ipoint +c & ,' iwindix= ',iwindix + continue + endif + continue + else + if (verb >= 3) then + print *,' ' + print *,' --- vmaxwind of ',vmaxwind,' m/s does NOT' + print *,' - - exceed threshold of ' + & ,windthresh(iwindix) + print *,' - - (m/s), so radii checking will NOT be ' + print *,' - - performed for this threshold.' + endif + iwindix = iwindix + 1 + cycle threshloop + endif + else + iwindix = iwindix + 1 + cycle threshloop + endif + endif + + ipoint = ipoint - 1 + + if (quadinfo(k,isortix(ipoint),1) < windthresh(iwindix)) then + cycle threshloop + else + if (ipoint == quadct(k)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In getradii, a max wind radius was' + print *,'!!! found at the maximum radius checked, so ' + print *,'!!! you may want to make sure that you are' + print *,'!!! checking at a far enough distance from ' + print *,'!!! the fix position, that is, you may want to' + print *,'!!! increase the value of radmax in subroutine' + print *,'!!! getradii. Currently, radmax (km) = ',radmax + print *,'!!! iwindix = ',iwindix,' quadrant= ',k + endif + + vradius(iwindix,k) = int( ((quadinfo(k,isortix(ipoint),2) + & * 0.5396) / 5.0) + 0.5) * 5 + else + +c Interpolate between the 2 closest distances to each wind +c threshold to get "exact" distance to that wind threshold +c radius, convert from km to nm, and then round to the +c nearest 5 nm (since TPC uses this precision). +c 7/23/98 UPDATE: Jim Gross has asked that values not be +c rounded to the nearest 5 nm, but rather only to the +c nearest 1 nm. + + exactdistkm = quadinfo(k,isortix(ipoint),2) + + & ( (quadinfo(k,isortix(ipoint),1) - windthresh(iwindix)) / + & (quadinfo(k,isortix(ipoint),1) - + & quadinfo(k,isortix(ipoint+1),1)) * + & ( (quadinfo(k,isortix(ipoint+1),2) - + & quadinfo(k,isortix(ipoint),2)) ) ) + + exactdistnm = exactdistkm * 0.5396 ! Convert km to nm + vradius(iwindix,k) = int(exactdistnm + 0.5) + +cc vradius(iwindix,k) = int( (exactdistnm / 5.0) + 0.5) * 5 + + + if ( verb .ge. 3 ) then + print *,'iwindix= ',iwindix,' exactdistnm = ' + & ,exactdistnm + print *,'vradius(iwindix,k) =',vradius(iwindix,k) + endif + + endif + +c The possibility exists, especially for coarse output +c grids, that there could be a jump over more than 1 wind- +c thresh category when going from 1 grid point to the next, so +c we need to account for this. For example, if 1 point has +c vmag = 15 m/s and the next point closer in has vmag = 28 +c m/s, then between those 2 points you have the thresholds +c for gale force AND storm force winds, so to be safe, we +c actually need to add 1 to ipoint and re-check the current +c point, if the wind value at that point is found to be +c greater than a wind threshold value (which it has if you've +c gotten to this point in threshloop). + + ipoint = ipoint + 1 + + iwindix = iwindix + 1 + + endif + + enddo threshloop + + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (idta /= 0 .or. iisa /= 0 .or. iwa /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating isortix or' + print *,'!!! dtemp or work for quadrant= ',k + print *,'!!! idta= ',idta,' iisa= ',iisa,' iwa= ',iwa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-J....' + stop 98 + endif + + itret = 94 + return + endif + + enddo quadrantloop + + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getradii deallocating quadinfo array.' + print *,'!!! iqa= ',iqa + endif + + idta=0; iisa=0; iwa=0; iqa=0 + if (allocated(dtemp)) deallocate (dtemp,stat=idta) + if (allocated(isortix)) deallocate (isortix,stat=iisa) + if (allocated(iwork)) deallocate (iwork,stat=iwa) + if (allocated(quadinfo)) deallocate (quadinfo,stat=iqa) + + if (iqa /= 0 .or. iwa /= 0 .or. idta /= 0 .or. iisa /= 0) + & then + print *,' ' + print *,'!!! ERROR in getradii deallocating arrays.' + print *,'!!! iqa= ',iqa,' idta= ',idta + print *,'!!! iisa= ',iisa,' iwa= ',iwa + print *,'!!! EXITING at GR-K....' + stop 98 + endif + + itret = 94 + return + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_max_wind (xcenlon,xcenlat,imax,jmax,dx,dy + & ,valid_pt,levsfc,vmax,trkrinfo,rmax,igmwret) +c +c ABSTRACT: This subroutine looks for the maximum near-surface wind +c near the storm center. This subroutine is only concerned with the +c value of the max wind, NOT where it's located radially with +c respect to the center. The value that's returned in vmax is the +c max wind speed in m/s, which are the units the data are stored in. +c However, when the max wind values are output in output_atcf, they +c will be converted from m/s to knots. +c +c INPUT: +c +c xcenlon fix longitude of storm center for current forecast hour +c xcenlat fix latitude of storm center for current forecast hour +c imax max i dimension of model grid +c jmax max j dimension of model grid +c dx grid spacing in i-direction of model grid +c dy grid spacing in j-direction of model grid +c valid_pt logical bitmap for valid data at a grid point +c levsfc integer holding the value of the array member that holds +c the near-surface winds in the u and v arrays (at orig +c writing, it's = 4). +c +c OUTPUT: +c +c vmax value of maximum near-surface wind near the storm ctr +c rmax radius of max winds +c igmwret return code from this subroutine +c +c LOCAL: +c +c radmaxwind the maximum radius to look for a max wind near the +c storm center. You have to allow this to be bigger for +c model grids with coarse resolution (ECMWF 2.5 degree). + + USE grid_bounds; USE tracked_parms; USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real radmaxwind,degrees,dx,dy,rmax + logical(1) valid_pt(imax,jmax) +c + igmwret = 0 + rmax = -99.0 + + if ((dx+dy)/2. <= 1.25) then + if ((dx+dy)/2. <= 0.25) then + radmaxwind = 300.0 + else + radmaxwind = 300.0 + endif + else + radmaxwind = 500.0 + endif + +c Roughly fix xcenlat to the grid point just poleward of xcenlat, +c and fix xcenlon to the grid point just EASTward of xcenlon. + + if (xcenlat >= 0.0) then + jlatfix = int((glatmax - xcenlat)/dy + 1.) + else + jlatfix = ceiling((glatmax - xcenlat)/dy + 1.) + endif + + ilonfix = int((xcenlon - glonmin)/dx + 2.) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + +c Calculate number of grid points to have surrounding the storm so +c that we are sure radmaxwind is within those points. + + cosfac = cos (xcenlat * dtr) + numipts = ceiling((radmaxwind/(dtk*dx))/cosfac) + numjpts = ceiling(radmaxwind/(dtk*dy)) + + jbeg = jlatfix - numjpts + jend = jlatfix + numjpts + 1 + ibeg = ilonfix - (numipts + 1) + iend = ilonfix + numipts + + if (jbeg > jmax .or. jbeg < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_max_wind calculating jbeg or jend.' + print *,'jbeg= ',jbeg,' jend= ',jend + print *,'Value of vmax will be set to 0 for this time.' + endif + + vmax = 0.0 + igmwret = 99 + return + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + + print *,' ' + print *,'In get_max_wind, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif + + vmax = 0.0 + do j=jbeg,jend + do i=ibeg,iend + + ip = i + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_max_wind, the ' + print *,'!!! user-requested point ' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! At location B in subroutine.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern point = ',i + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time.' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in sub get_max_wind' + print *,'!!! for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! At location C in subroutine.' + print *,'!!! ' + print *,'!!! Value of vmax will be set to 0 for ' + print *,'!!! this time' + print *,' ' + endif + + igmwret = 99 + return + endif + endif + + call calcdist (xcenlon,xcenlat,glon(ip),glat(j),dist,degrees) + + if (dist > radmaxwind) cycle + + if (valid_pt(ip,j)) then + vmag = sqrt (u(ip,j,levsfc)**2 + v(ip,j,levsfc)**2) + if (vmag > vmax) then + vmax = vmag + rmax = dist * 0.539638 ! convert from km to nm + endif + endif + + enddo + enddo + + if ( verb .ge. 3 ) then + print *,'At end of get_max_wind, vmax= ',vmax,' rmax= ',rmax + endif + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine fixcenter (clon,clat,ist,ifh,calcparm,geslon,geslat + & ,inp,stderr,fixlon,fixlat,xvalues,maxstorm,ifret) +c +c ABSTRACT: This subroutine loops through the different parameters +c for the input storm number (ist) and calculates the +c center position of the storm by taking an average of +c the center positions obtained for those parameters. +c First we check to see which parameters are within a +c max error range (errmax), and we discard those that are +c not within that range. Of the remaining parms, we get +c a mean position, and then we re-calculate the position +c by giving more weight to those estimates that are closer +c to this mean first-guess position estimate. +c +c INPUT: +c clon Center longitudes of tracked parms for this storm & ifh +c clat Center latitudes of tracked parms for this storm & ifh +c ist Storm number +c ifh Index for forecast hour +c calcparm Logical; Use this parm's location for this storm or not +c geslon Initial guess longitude for this storm at this fcst hour +c geslat Initial guess latitude for this storm at this fcst hour +c inp contains the input date and model number information +c xvalues The actual max or min data values for each parameter +c maxstorm max # of storms to be handled in this run +c +c INPUT/OUTPUT: +c stderr Standard deviation of the position "error" of the parms +c relative to the guess storm position. As long as the +c distance of a parm center to the guess center is <= +c errpmax, it is included in the std dev calculation. +c +c OUTPUT: +c fixlon Best approximation of storm center's longitude +c fixlat Best approximation of storm center's latitude +c ifret Return code from this subroutine +c +c LOCAL: +c storm Contains tcvitals info for the storms (def_vitals) +c trkerr_avg Sum/avg of the track errors for all parms for this +c fcst hour, regardless of whether or not the error was +c > errmax. It's used for getting the std deviation of +c the position error for this forecast time, to be used +c as part of the errmax calculation for the next fcst +c time. +c iclose Number of parameters whose position estimates are +c found to be within a distance errmax of the guess pos +c wtpos The weight given to each position estimate. It's +c based on the distance from the average position. +c errdist The "error" of the parameter center position relative +c to the storm's guess position. +c avgerr Average "error" of the parameter center positions +c relative to the storm's guess position. +c use4next Logical; If a parm center has been calculated but its +c distance from the guess position is > errmax, we don't +c use this center in calculating the new guess position, +c however we will use this position in calculating the +c standard deviation of the current time's guess +c positions, to be used in calculating the new errmax +c for the next forecast time. So in this subroutine, +c calcparm may be set to FALSE if errdist > errmax, but +c use4next will not be set to FALSE (Actually, it is +c only set to FALSE if errdist > errpmax, which is +c defined in error_parms and is roughly 600km). +c stderr_close Standard deviation of position errors for parms that +c have center estimates that are within a distance +c errmax of the guess position. +c clon_fguess These are the first-guess mean position estimates, +c clat_fguess which are the means of the position estimates that +c are within a distance errmax. These first-guess mean +c positions will be refined by giving more weight to +c individual parameter estimates that are closer to +c this first-guess mean position. +c dist_from_mean Contains the "error" distance of each parameter +c from the first-guess mean position (clon_fguess, +c clat_fguess). NOTE: If a parameter is not within +c a distance errmax of the guess position for this +c time (geslon,geslat), then there will be NO +c dist_from_mean calculated for that parm. +c + USE error_parms; USE set_max_parms; USE inparms; USE def_vitals + USE atcf; USE gen_vitals; USE tracked_parms + USE verbose_output + + type (datecard) inp + + real clon(maxstorm,maxtime,maxtp),temp_clon(maxtp) + real clat(maxstorm,maxtime,maxtp),temp_clat(maxtp) + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real trkerr(maxtp),errdist(maxtp),xvalues(maxtp) + real stderr(maxstorm,maxtime),devia(maxtp),wtpos(maxtp) + real dist_from_mean(maxtp) + real degrees,errtmp + integer gt345_ct,lt15_ct + logical(1) calcparm(maxtp,maxstorm),use4next(maxtp) + character charparm(maxtp)*8,charmaxmin(maxtp)*8 +c + data charparm /'zeta 850','zeta 700','circ 850','NOT USED' + & ,'circ 700','NOT USED',' gph 850',' gph 700',' MSLP' + & ,'circ sfc','zeta sfc',' thk 5-8',' thk 2-5',' thk 2-8'/ + data charmaxmin /' Max ',' Max ',' Min ','NOT USED' + & ,' Min ','NOT USED',' Min ',' Min ',' Min ' + & ,' Min ',' Max ',' Max ',' Max ',' Max '/ +c + ifret=0 +c +c We need to judge whether each parameter position is reasonable, +c so we'll check to make sure that the dist from each parameter's +c estimate to the guess position is less than a maximum allowable +c error. If it's the first forecast time, use the initial error max +c (defined as errinit in error_parms) as errmax. Otherwise, the +c max error criterion is that the distance error must not exceed 3 +c times the previous forecast time's standard deviation (after a +c small growth factor has been applied). +c UPDATE 3/5/98: During testing, it was found that just using the +c previous time's stdev made errmax too "jumpy" (i.e., at vt=48h, +c errmax could = 380, and then at vt=54h, errmax could jump down +c to 190, so we've changed it so that it uses an average of the +c stdev's from the 3 previous forecast times to maintain some +c continuity between successive forecast times). +c + if (ifh == 1) then + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR' ) then + errmax = err_gfs_init + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errmax = err_ecm_max + errinit = err_ecm_max + else + errmax = err_reg_init + errinit = err_reg_init + endif + else + if (atcfname == 'GFSO' .or. atcfname == 'MRFO' .or. + & atcfname == 'GDAS' .or. atcfname == 'GFDT' .or. + & atcfname(1:3) == 'AP0' .or. atcfname(1:3) == 'AN0' .or. + & atcfname(1:3) == 'AP1' .or. atcfname(1:3) == 'AN1' .or. + & atcfname(1:3) == 'AC0' .or. atcfname == 'AEAR') then + errinit = err_gfs_init + else if (atcfname == 'EMX ' .or. atcfname == 'FV3 ') then + errinit = err_ecm_max + else + errinit = err_reg_max + endif + + if (ifh >= 4) then + xavg_stderr = (stderr(ist,ifh-3) + stderr(ist,ifh-2) + & + stderr(ist,ifh-1)) / 3.0 + else if (ifh == 3) then + xavg_stderr = (stderr(ist,ifh-2) + stderr(ist,ifh-1)) / 2.0 + else if (ifh == 2) then + xavg_stderr = stderr(ist,ifh-1) + endif + +c The following errmax statement was replaced by the ensuing 4 +c lines due to a compiler bug on some other platforms: +c errmax = amin1(amax1(3.0*xavg_stderr*errpgro,errinit) +c & ,errpmax) + + errtmp = 3.0*xavg_stderr*errpgro + errmax = max(errtmp,errinit) + errtmp = errpmax + errmax = min(errmax,errtmp) + + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (ifh > 1) then + print '(a42,f8.2,a15,f8.2)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = ' + & ,stderr(ist,ifh-1),' xavg_stderr= ',xavg_stderr + else + print '(a45,a18)' + & ,' At beg of fixcenter, stderr(ist,ifh-1) = N/A' + & ,' xavg_stderr= N/A' + endif + print *,'At beg of fixcenter, errpgro = ',errpgro + print *,'At beg of fixcenter, errinit = ',errinit + print *,'At beg of fixcenter, errpmax = ',errpmax + print *,'At beg of fixcenter, ifh= ',ifh,' errmax= ',errmax + endif + + trkerr_avg = 0.0 + iclose = 0; itot4next = 0 + clonsum = 0.0; clatsum = 0.0 + errdist = 0.0 + use4next = .FALSE. + gt345_ct = 0 + lt15_ct = 0 + +c For each parm, check to see if the estimated center is within +c distance errmax of the guess center. If it's within errmax, +c then use that parm for locating the center. If it's NOT +c within errmax, but IS within errpmax, then we still use this +c in calculating the standard deviation of the parameters for +c helping to determine the errmax for the next forecast hour. + +c OLD NOTE: For calculating the std dev to be used for the next +c OLD forecast hour, do NOT use vmag 850, vmag 700 or vmag sfc, since +c OLD those parms are always guaranteed to be within a short range of +c OLD the guess, due to the nature of the algorithm (see subroutine +c OLD get_uv_center for further details on that). + + do ip=1,maxtp + + if (ip == 4 .or. ip == 6) then ! Parms 4 & 6 not defined. + calcparm(ip,ist) = .FALSE. + cycle + endif + if (calcparm(ip,ist)) then + call calcdist (geslon,geslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + errdist(ip) = dist + if (dist <= errpmax) then + use4next(ip) = .TRUE. + trkerr_avg = trkerr_avg + dist + itot4next = itot4next + 1 + endif + if (dist <= errmax) then + iclose = iclose + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 + endif + clonsum = clonsum + clon(ist,ifh,ip) + clatsum = clatsum + clat(ist,ifh,ip) + else + calcparm(ip,ist) = .FALSE. + endif + endif + + enddo + + if (iclose > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (clonsum) + clon_fguess = (clonsum + (360.*float(lt15_ct)))/ iclose + else + clon_fguess = clonsum / float(iclose) + endif + if (clon_fguess >= 360.0) then + clon_fguess = clon_fguess - 360. + endif + clat_fguess = clatsum / float(iclose) + endif + +c Print out a table listing of the locations of the fixes for +c the individual parameters. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'--------------------------------------------------' + write (6,95) 'Individual fixes follow..., fhr= ',ifhours(ifh) + & ,ifclockmins(ifh),' ',storm(ist)%tcv_storm_id,' ' + & ,storm(ist)%tcv_storm_name + write (6,97) gstorm(ist)%gv_gen_date,gstorm(ist)%gv_gen_fhr + & ,gstorm(ist)%gv_gen_lat + & ,gstorm(ist)%gv_gen_latns,gstorm(ist)%gv_gen_lon + & ,gstorm(ist)%gv_gen_lonew,gstorm(ist)%gv_gen_type + print *,'Model name = ',atcfname + print *,'Values of -99.99 indicate that a fix was unable to be' + print *,'made for that paramater. Parameters 4 & 6 are not' + print *,'used. Vorticity data values are scaled by 1e5.' + print *,'Circulation data values are scaled by 1e-6.' + print *,'errdist is the distance that the position estimate is' + print *,'from the guess position for this time. MSLP value ' + print *,'here may differ from that in the atcfunix file since ' + print *,'the one here is that derived from the area-averaged ' + print *,'barnes analysis, while that in the atcfunix file is ' + print *,'from a specific gridpoint.' + write (6,21) geslon,360.-geslon,geslat + write (6,*) ' ' + write (6,23) + write (6,25) + endif + + if (geslat > 0.0) then + charmaxmin(1) = ' Max ' + charmaxmin(2) = ' Max ' + charmaxmin(3) = ' Max ' + charmaxmin(5) = ' Max ' + charmaxmin(10) = ' Max ' + charmaxmin(11) = ' Max ' + else + charmaxmin(1) = ' Min ' + charmaxmin(2) = ' Min ' + charmaxmin(3) = ' Min ' + charmaxmin(5) = ' Min ' + charmaxmin(10) = ' Min ' + charmaxmin(11) = ' Min ' + endif + + do ip=1,maxtp + if (ip == 1 .or. ip == 2 .or. ip == 11) then + ! This IF block allows vorticity values to be + ! written out and scaled up by 1e5 ... + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e5 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + elseif (ip == 3 .or. ip == 5 .or. ip == 10) then + ! This IF block allows circulation values to be + ! written out and scaled down by 1e-6 ... + + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip)*1e-6 + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + + else + if (clon(ist,ifh,ip) < 0.001 .and. + & clon(ist,ifh,ip) > -0.001) then + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip),0.0 + & ,0.0,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + else + + if ( verb .ge. 3 ) then + write (6,27) ip,charparm(ip),charmaxmin(ip) + & ,clon(ist,ifh,ip),360.-clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),xvalues(ip) + & ,calcparm(ip,ist),errdist(ip) + endif + + endif + endif + enddo + + 21 format (' Guess location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 23 format (' parm# parm Max/Min Lon_fix(E) Lon_fix(W)' + & ,' Lat_fix Max/Min_value calcparm errdist(km)') + 25 format (' ----- ---- ------- ---------- ----------' + & ,' ------- ------------- -------- ----------') + 27 format (2x,i2,4x,a8,2x,a8,3x,f7.2,5x,f7.2,4x,f7.2,7x,f9.2 + & ,6x,L2,7x,f7.2) + 95 format (1x,a33,1x,i4,':',i2.2,a2,a4,a1,a9) + 97 format (' Gen ID (if available): ',i10.10,'_F',i3.3,'_',i3.3,a1 + & ,'_',i4.4,a1,'_',a3) + + +c If number of parameter centers close enough (iclose) > 0, then +c calculate the center by taking an average of all the parameter +c center positions that are within distance errmax from the guess +c position (geslon,geslat). Get a first-guess mean position, and +c then re-calculate the position estimate by giving more weight +c to those positions that are closer to the first-guess mean +c position. + + dist_from_mean = 0.0 + + if (iclose > 0) then + +c Get distances from first-guess mean position.... + + do ip=1,maxtp + if (calcparm(ip,ist)) then + call calcdist (clon_fguess,clat_fguess,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + dist_from_mean(ip) = dist + endif + enddo + +c Get the mean distance of each parameter estimate from +c the first-guess mean position + + call avgcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,iaret) + + if (iaret == 0) then + + call stdevcalc (dist_from_mean,maxtp,calcparm(1,ist) + & ,xmn_dist_from_mean,stderr_close,isret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After stdevcalc, xmn_dist_from_mean= ' + & ,xmn_dist_from_mean,' stderr_close= ' + & ,stderr_close,' isret= ',isret + endif + + endif + if (iaret /= 0 .or. isret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER -- Error occurred in either' + print *,'!!! avgcalc or stdevcalc. Storm number = ',ist + print *,'!!! RCC from avgcalc = ',iaret + print *,'!!! RCC from stdevcalc = ',isret + print *,'!!! Center fix will NOT be made, and processing' + print *,'!!! for this storm is ending. The probable cause' + print *,'!!! is that no calcparms were valid for this storm' + print *,'!!! at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + if (calcparm(1,ist) .or. calcparm(2,ist) .or. calcparm(7,ist) + & .or. calcparm(8,ist) .or. calcparm(9,ist) + & .or. calcparm(11,ist) .or. calcparm(3,ist) + & .or. calcparm(10,ist) .or. calcparm(5,ist) + & .or. calcparm(12,ist) .or. calcparm(13,ist) + & .or. calcparm(14,ist)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! In fixcenter, STOPPING PROCESSING for this' + print *,'!!! storm. The reason is that none of the fix' + print *,'!!! locations for parms z850, z700, zeta 850,' + print *,'!!! zeta 700, MSLP, wcirc_850, wcirc_700, ' + print *,'!!! wcirc_sfc, sfc zeta or the various levels ' + print *,'!!! of thicknesses were within a ' + print *,'!!! reasonable distance of the guess location.' + print *,'!!! ist= ',ist,' ifh= ',ifh + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'!!! Forecast hour: ',i4,':',i2.2) + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now re-calculate the mean position by giving more weight +c to those position estimates that are closer to the first +c guess mean position. Note that if stderr_close < 5.0, we +c force it to be 5.0; we do this to avoid getting very +c large numbers for devia values, which could make the +c weights (wtpos) equal to 0. This occurred during testing +c when only 2 parameters were valid, and so, of course, the +c standard deviation from the mean of those 2 parameters +c was close to 0, which gave devia values around 6000, and +c then wtpos values of 0, leading to a divide by 0 crash +c later on in subroutine wtavrg. + + kprm=0 + + if (stderr_close > 0.0) then + if (stderr_close < 5.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Since stderr_close had a value less than' + print *,'5, stderr_close has been forced to be equal' + print *,'to 5 in order to avoid dividing by zero later' + print *,'on in subroutine wtavrg.' + endif + + stderr_close = 5.0 + endif + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + devia(kprm) = dist_from_mean(ip) / stderr_close + wtpos(kprm) = exp(-devia(kprm)/3.) + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + + if ( verb .ge. 3 ) then + write (6,113) ip,kprm,dist_from_mean(ip),devia(kprm) + & ,wtpos(kprm),temp_clon(kprm) + & ,360.-temp_clon(kprm),temp_clat(kprm) + endif + + endif + enddo + 113 format (1x,'ip= ',i2,' kprm= ',i2,' dist_from_mean= ',f7.3 + & ,' devia= ',f7.3,' wtpos= ',f8.5,2x,3(2x,f7.2)) + else +c +c This next if statement is for the case in which only 1 +c parameter is valid, for which the stderr_close will = 0 +c (obviously), but as long as we have 1 valid parameter, +c continue processing, and set the weight for that parm = 1. +c The else portion is for the case in which stderr_close +c = 0 with NO parms being close. +c + if (iclose == 1) then + do ip=1,maxtp + if (calcparm(ip,ist)) then + kprm = kprm + 1 + wtpos(kprm) = 1 + temp_clon(kprm) = clon(ist,ifh,ip) + temp_clat(kprm) = clat(ist,ifh,ip) + endif + enddo + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, stderr_close not > 0' + print *,'!!! stderr_close = ',stderr_close + print *,'!!! The probable cause is that no calcparms were' + print *,'!!! valid for this storm at this forecast hour.' + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + endif +c + if (kprm > 0) then + call wtavrg_lon (temp_clon,wtpos,kprm,fixlon(ist,ifh),iwtret1) + call wtavrg (temp_clat,wtpos,kprm,fixlat(ist,ifh),iwtret2) + if (iwtret1 == 0 .and. iwtret2 == 0) then + if (verb .ge. 3) then + print *,' ' + write (6,173) storm(ist)%tcv_storm_id,ifhours(ifh) + & ,ifclockmins(ifh),fixlon(ist,ifh) + & ,360.-fixlon(ist,ifh),fixlat(ist,ifh) + 173 format ('At end of fixcenter: ',a4,' fhr= ',i4,':',i2.2 + & ,' Fix position= ',f7.2,'E (',f6.2,'W)',2x,f7.2) + print *,' ' + endif + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER in call to wtavrg.' + print *,'!!! Return Codes from wtavrg calls follow: ' + print *,'!!! RCC from wtavrg for long fix: ',iwtret1 + print *,'!!! RCC from wtavrg for lat fix: ',iwtret2 + print *,'!!! This means a divide by zero would have ' + print *,'!!! been attempted, which means that the ' + print *,'!!! weights in wtpos are not > 0. Check in' + print *,'!!! subroutine fixcenter where devia values' + print *,'!!! are calculated to see if something is ' + print *,'!!! wrong there. Values of wtpos array follow:' + print *,'!!! ',wtpos + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + print *,' ' + endif + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR IN FIXCENTER, kprm NOT > 0' + print *,'!!! This means that, for whatever reason, the ' + print *,'!!! calcparm logical flag was set to .FALSE. for' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax,' kprm= ',kprm + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: IN FIXCENTER, No storms are within errmax ' + print *,'!!! OR the calcparm logical flag was set to .FALSE. ' + print *,'!!! all of the parameters. Thus, a center' + print *,'!!! position could not be obtained for this storm' + print *,'!!! ist= ',ist,' ifh= ',ifh,' iclose= ',iclose + print *,'!!! errmax= ',errmax + endif + + fixlon(ist,ifh) = -999.0 + fixlat(ist,ifh) = -999.0 + ifret = 95 + return + endif + +c Now calculate the average error of all the parms that are within +c a radius errpmax (defined in error_parms, ~600km), and the std +c dev of those errors. This standard deviation will be used in +c calculating the maximum allowable error for the next forecast +c time. + + if (itot4next > 0 .and. ifret /= 95) then + trkerr_avg = trkerr_avg / float(itot4next) + call stdevcalc (errdist,maxtp,use4next,trkerr_avg + & ,stderr(ist,ifh),isret) + if (isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in FIXCENTER calculating std deviation ' + print *,'!!! for use in next forecast hours errmax.' + print *,'!!! ist= ',ist,' ifh= ',ifh,' itot4next= ' + & ,itot4next + endif + + ifret = 95 + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine avgcalc (xdat,kmax,valid,xavg,iaret) +c +c ABSTRACT: This subroutine just calculates a straight average of +c the parameters in the input array (xdat). The logical array +c (valid) indicates whether or not to include a particular array +c member or not in the calculation. + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) +c + iaret = 0 +c + xsum = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + xsum = xsum + xdat(i) + ict = ict + 1 + endif + enddo +c + if (ict > 0) then + xavg = xsum / float(ict) + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in avgcalc, ict NOT > 0' + endif + + xavg = xdat(1) + iaret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg (xdat,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xdat) using the input weights +c in the input array (wt). It is used to calculate the center lat +c and lon fix positions. +c + USE verbose_output + + real xdat(kmax),wt(kmax) +c + iwtret = 0 +c + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xdat(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg, wtot NOT > 0' + endif + + iwtret = 95 + endif +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine wtavrg_lon (xlon,wt,kmax,xwtavg,iwtret) +c +c ABSTRACT: This subroutine calculates a weighted average of the +c parameters in the input array (xlon) using the input weights +c in the input array (wt). This subroutine is specifically used +c to find the center lon fix positions. It contains code to +c account for wrapping around the Greenwich Meridian. +c + + USE verbose_output + + real xlon(kmax),wt(kmax) + integer gt345_ct,lt15_ct +c + iwtret = 0 + gt345_ct = 0 + lt15_ct = 0 + +c First check to see if we have lons that are both to the left +c and the right of the greenwich meridian + + do i = 1,kmax + if (xlon(i) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (xlon(i) < 15.) then + lt15_ct = lt15_ct + 1 + endif + enddo + + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some lons that are in the 300's (west of the GM), and + ! some that are in the 0's (east of the GM). We need to + ! standardize these if we want to get a meaningful average. + do i = 1,kmax + if (xlon(i) < 15.) then + xlon(i) = xlon(i) + 360.0 + endif + enddo + endif + + xwtavg = 0.0 + wtot = 0.0 + do i=1,kmax + xwtavg = xwtavg + xlon(i)*wt(i) + wtot = wtot + wt(i) + enddo +c + if (wtot > 0.0) then + xwtavg = xwtavg / wtot + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in wtavrg_lon, wtot NOT > 0' + endif + + iwtret = 95 + endif + + if (xwtavg >= 360.0) then + xwtavg = xwtavg - 360.0 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine stdevcalc (xdat,kmax,valid,xavg,stdx,isret) + + USE verbose_output + + real xdat(kmax) + logical(1) valid(kmax) + + isret = 0 + + stdx = 0.0 + ict = 0 + do i=1,kmax + if (valid(i)) then + stdx = stdx + (xdat(i) - xavg)**2 + ict = ict + 1 + endif + enddo + + if (ict > 0) then + stdx = sqrt(stdx/float(ict)) + if (stdx == 0.0) then +c This can happen if you have just 2 points; The mean position +c will be exactly in the middle of the 2 points and so the +c standard deviation around that mean point will be 0. And +c since the calling routine will quit if the returned standard +c deviation is 0, we must force it to be 1 so the program +c continues running. Theoretically, it could also happen with +c 3 or more points, but the likelihood of the distances working +c out to exactly equidistant for 3 points is not that good. + stdx = 1.0 + endif + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in stdevcalc, ict NOT > 0' + endif + + isret = 95 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_wind_circulation (uvgeslon,uvgeslat,imax,jmax + & ,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,fxval,trkrinfo + & ,cmodel_type,maxmin,igwcret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the wind circulation near the storm center. This center fix is +c done differently than for the other parms. With this fix, +c we limit the area that is searched. This subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the +c original guess position for this lead time and the 5 other parm +c fixes that have already been made for this lead time. That +c modified guess position is passed into this subroutine as uvgeslon +c and uvgeslat, and that's where the searching for the wind +c circulation is centered. +c +c This subroutine works by converting the winds to Vt and Vr at +c each grid point evaluated, relative to each candidate center point +c that is being evaluated at the time in the loop. We then compute +c the circulation at each of 24 azimuths surrounding the storm +c center, where circulation = Vt * (length of a 1/24 arc, in meters) +c This process is repeated for 7 successive radii and the results +c are summed up over all radii, approximating a solid disk +c circulation. The point at which the circulation is maximized +c (NHEM) or minimized (SHEM) is the center of circulation. +c +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c + + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + character(*) cmodel_type,maxmin + integer, parameter :: numdist=7,numazim=24 + integer imax,jmax,ist,level,igwcret,icvpret,idist,iazim + real rdist(numdist),vr(numazim,numdist),vt(numazim,numdist) + real vt_mean(numdist),circul_band(numdist) + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + real rads,ri,uvgeslon,uvgeslat,dx,dy,ctlon,ctlat,fxval + real temp_grid_minlon,temp_guesslon,rlatt,rlont,bear + real targlon,targlat,xintrp_u,xintrp_v,vt_azim_sum,degrees + real circ_diff,circ_diff_sum,hemisphere,wind_mag_ctr,dist + real xmin_circ_diff_mean,xmax_circ_diff_mean,tlon,tlat + real dell,fmax,fmin,grid_buffer,circ_diff_mean + real circumference,arclength + real circul_disk,xmax_circul_disk,xmin_circul_disk + integer ibiret1,ibiret2,igvtret,azimuth_ct,igiret,npts + integer igibret + integer circ_diff_ct,ir,nhalf,bskip1,bskip2,iskip,nlev + integer ilonfix,jlatfix,ibeg,iend,jbeg,jend,i,j,k,iix,jix + logical(1) cflag, valid_pt(imax,jmax) + +c---------------- +c + + print *,' ' + print *,'top of get_wind_circulation, ' + print *,' glatmax= ',glatmax + print *,' glatmin= ',glatmin + print *,' glonmax= ',glonmax + print *,' glonmin= ',glonmin + print *,' trkrinfo%gridtype= ',trkrinfo%gridtype + print *,' cmodel_type= ',cmodel_type + print *,' maxmin= ',maxmin + print *,' imax= ',imax,' jmax= ',jmax + print *,' uvgeslon= ',uvgeslon,' uvgeslat= ',uvgeslat + print *,' dx= ',dx,' dy= ',dy,' ist= ',ist + print *,' cflag= ',cflag + print *,' ctlon= ',ctlon,' ctlat= ',ctlat + print *,' fxval= ',fxval + print *,' igwcret= ',igwcret + + igwcret = 0 + + grid_maxlat = glatmax + grid_minlat = glatmin + grid_maxlon = glonmax + grid_minlon = glonmin + + rads = rads_wind_circ + ri = ri_wind_circ + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of get_wind_circulation, rads= ',rads + & ,' ri= ',ri,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+15; fmin = 1.0e+15 + ctlon = 0.0; ctlat = 0.0 + +c Distances checked and the radial intervals are a function of +c the grid resolution.... + + if (dell > 0.50) then + rdist(1) = 50. + rdist(2) = 85. + rdist(3) = 120. + rdist(4) = 155. + rdist(5) = 190. + rdist(6) = 225. + rdist(7) = 260. + else + rdist(1) = 35. + rdist(2) = 65. + rdist(3) = 95. + rdist(4) = 125. + rdist(5) = 155. + rdist(6) = 185. + rdist(7) = 215. + endif + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + print *,' in get_wind_circulation, nlev= ',nlev + + if (uvgeslat >= 0.0) then + hemisphere = 1.0 + else + hemisphere = -1.0 + endif + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend + & ,igibret) + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (uvgeslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = uvgeslon - 360. + else + temp_guesslon = uvgeslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = uvgeslon + endif + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + +c For the wind circulation analysis, we will want to speed things +c up for finer resolution grids. We can do this by skipping some +c of the points in the wind circulation analysis. + + if (dell > 0.20) then + bskip1 = 1 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 3 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 5 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 8 + bskip2 = 3 + else if (dell <= 0.03) then + bskip1 = 10 + bskip2 = 4 + endif + +c bskip1 = 1 +c bskip2 = 1 + + jix = 0 + +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to first loop, ' + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop1: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = uvgeslat + dell*float(j) + + iix = 0 + + iloop1: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + else + cycle iloop1 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop1 + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT from call in ' + print *,'!!! get_wind_circulation: icvpret= ',icvpret + endif + cycle iloop1 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist,degrees) + if (dist .gt. rads) cycle iloop1 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop1: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop1: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + ! These calls to bilin_int_uneven pass a variable "level" + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop1 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop1 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop1 +cc and radiusloop1). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,' ' +cc print *,'1st run, wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'1st run, ir= ',ir,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +c print *,'1st run, circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'1st run, circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'1st run, xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean +c +c if (uvgeslat > 0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif + + enddo iloop1 + + enddo jloop1 + + if (uvgeslat >= 0.0) then + write (6,61) 360.-ctlon,ctlat,xmax_circul_disk + else + write (6,63) 360.-ctlon,ctlat,xmin_circul_disk + endif + + 61 format (' After first run, Wind Circulation (NHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmax_circul_disk = ',f15.1) + 63 format (' After first run, Wind Circulation (SHEM) ctlon= ',f8.3 + & ,'W ctlat= ',f8.3,' xmin_circul_disk = ',f15.1) + +c If nhalf is specified as 0, then don't go through any more +c iterations of this routine, just exit with the value that we +c already got the first time through the loop, above. + + if (dell > 0.50) then + nhalf = 4 + else if (dell > 0.20 .and. dell <= 0.50) then + nhalf = 3 + else if (dell > 0.10 .and. dell <= 0.20) then + nhalf = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + nhalf = 1 + else if (dell <= 0.05) then +c nhalf = 0 + nhalf = 1 +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'In get_wind_circulation, dell is < 0.05 deg, so ' +c print *,'nhalf is set to 0 and only the first iteration of' +c print *,'the search loop is done.' +c print *,' dell= ',dell,' nhalf= ',nhalf +c endif + endif + + if (nhalf < 1) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif + return + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +c npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only do this once +c for this grid-refinement (even though the grid is redefined +c nhalf times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). Cut the value of +c rads in half (only do this once) so that any points beyond +c rads/2 are not considered as potential centers. + + rads = 0.5 * rads + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igibret) + + if (igibret /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_wind_circulation from call to ' + print *,'!!! get_ij_bounds just before nhalf loop. ' + print *,'!!! Stopping processing for storm number ',ist + endif + igwcret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + kloop: do k = 1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: get_wind_circ kloop, k= ',i2,' ' + & ,i2.2,':',i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat +c xmin_circ_diff_mean = 9999.0 +c xmax_circ_diff_mean = -9999.0 + xmin_circul_disk = 9999.0 + xmax_circul_disk = -9999.0 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'get_wind_circ nhalf loop, k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip,' rads= ',rads + endif + + if (verb .ge. 3) then + print *,' ' + print *,'In get_wind_circulation, prior to loop k= ',k + print *,' npts= ',npts,' dell= ',dell,' rads= ',rads + print *,' ' + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,u(1,1,nlev),maxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + call calcdist(rlont,rlatt,temp_guesslon,uvgeslat,dist + & ,degrees) + if (dist .gt. rads) cycle iloop2 + +c Now go through each radius, starting from inner and working +c to outer, and at each one, go around through all of the 24 +c discrete azimuths, starting at 7.5 and adding 15 degrees +c clockwise each time, all the way up through 352.5. + + vt_mean = 0.0 + vt = 0.0 + vr = 0.0 + + circul_band = 0.0 + circul_disk = 0.0 + + radiusloop2: do idist = 1,numdist + + azimuth_ct = 0 + vt_azim_sum = 0.0 + + ! Compute the length of a 1/numazim arc at this radius, and + ! be sure to multiply by 1000 to convert from km to m for + ! use in computing the circulation.... + + circumference = 2 * pi * rdist(idist) * 1000.0 + arclength = circumference / float(numazim) + + azimloop2: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (rlatt,rlont,rdist(idist) + & ,bear,targlat,targlon) + +ctmwc if ( verb .ge. 3 ) then +ctmwc print *,' ' +ctmwc print '(5(a11,f7.2))',' ctr lat= ',rlatt +ctmwc & ,' ctr lon= ',rlont +ctmwc & ,' rdist= ',rdist(idist),' targlat= ',targlat +ctmwc & ,' targlon= ',targlon +ctmwc print '(19x,a10,f7.2,35x,a9,f7.2)',' ctr lon= ' +ctmwc & ,360.-rlont +ctmwc & ,'targlon= ',360.-targlon +ctmwc endif + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (rlont,rlatt,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim,idist) + & ,vt(iazim,idist),igvtret) + azimuth_ct = azimuth_ct + 1 + circul_band(idist) = circul_band(idist) + & + (vt(iazim,idist) * arclength) +c vt_azim_sum = vt_azim_sum + vt(iazim,idist) + else if (ibiret1 == 85 .or. ibiret2 == 85) then + vr(iazim,idist) = -999.0 + vt(iazim,idist) = -999.0 + else + igwcret = 95 + return + endif + + enddo azimloop2 + + if (azimuth_ct > 0) then + ! Add the value for the circulation in this radial + ! band (circul_band(idist)) to the "solid disk" + ! circulation total. Also, + ! Compute azimuthally-averaged Vt at this distance + circul_disk = circul_disk + circul_band(idist) + vt_mean(idist) = vt_azim_sum / float(azimuth_ct) + else +c vt_mean(idist) = -999.0 + print *,' ' + endif + + enddo radiusloop2 + + if (uvgeslat > 0.0) then + if (circul_disk > xmax_circul_disk) then + xmax_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + else + if (circul_disk < xmin_circul_disk) then + xmin_circul_disk = circul_disk + ctlon = rlont + ctlat = rlatt + endif + endif + +CC--> This section was commented out in Feb 2018 due to finding a +cc bug/flaw in the whole "circulation difference" concept, and +cc it has now been replaced throughout this subroutine with +cc more robust circulation computation logic. +cc +cc Now get the wind magnitude at the candidate center of +cc circulation (i.e., the one that we just used for +cc computing all of the Vr and Vt in the previous azimloop2 +cc and radiusloop2). +c +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) +c call bilin_int_uneven (rlatt,rlont +c & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) +c if (ibiret1 == 0 .and. ibiret2 == 0) then +c wind_mag_ctr = sqrt (xintrp_u**2 + xintrp_v**2) +c else +c if ( verb .ge. 3 ) then +c print *,' ' +c print *,'!!! NOTE: bilint_uneven failed for center' +c print *,'!!! wind mag in get_wind_circulation.' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt +c print *,'!!! ' +c endif +c endif +c +cc print *,'kloop k= ',k,' wind_mag_ctr= ',wind_mag_ctr +c +c circ_diff_ct = 0 +c circ_diff_sum = 0.0 +c do ir = 1,numdist +cc print *,'kloop k= ',k,' vtmean(ir)= ',vt_mean(ir) +c if (vt_mean(ir) > -998.0) then +c circ_diff = vt_mean(ir) - (hemisphere * wind_mag_ctr) +c circ_diff_ct = circ_diff_ct + 1 +c circ_diff_sum = circ_diff_sum + circ_diff +c endif +c enddo +c +cc print *,'kloop k= ',k,' circ_diff_ct= ',circ_diff_ct +c +c if (circ_diff_ct > 0) then +c +c circ_diff_mean = circ_diff_sum / float(circ_diff_ct) +c +cc print *,'kloop k=',k,' circ_diff_sum= ',circ_diff_sum +cc & ,' circ_diff_mean= ',circ_diff_mean +c +c if (uvgeslat > 0.0) then +c if (circ_diff_mean > xmax_circ_diff_mean) then +c xmax_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c else +c if (circ_diff_mean < xmin_circ_diff_mean) then +c xmin_circ_diff_mean = circ_diff_mean +c ctlon = rlont +c ctlat = rlatt +c endif +c endif +c endif +c +cc print *,'kloop k= ',k,' xmax_circ_diff_mean= ' +cc & ,xmax_circ_diff_mean + + enddo iloop2 + + enddo jloop2 + + if ( verb .ge. 3 ) then + if (uvgeslat >= 0.0) then + print *,'---> xmax_circul_disk= ',xmax_circul_disk + write (6,71) k,360.-ctlon,ctlat,xmax_circul_disk + else + print *,'---> xmin_circul_disk= ',xmin_circul_disk + write (6,73) k,360.-ctlon,ctlat,xmin_circul_disk + endif + endif + + enddo kloop + + 71 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (NHEM: Max) = ' + & ,f15.1) + 73 format (' nhalf get_wind_circ, k= ',i2,' ctlon= ',f8.3,'W ' + & ,' ctlat= ',f8.3,' Wind Circulation (SHEM: Min) = ' + & ,f15.1) + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if (uvgeslat > 0.0) then + fxval = xmax_circul_disk + else + fxval = xmin_circul_disk + endif +c + return + end + +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_center (uvgeslon,uvgeslat,imax,jmax,dx,dy + & ,ist,level,valid_pt,cflag + & ,ctlon,ctlat,xval,trkrinfo,igucret) +c +c ABSTRACT: This subroutine calculates the center fix position for +c the minimum in the wind speed near the storm center. This center +c fix is done differently than for the other parms. With this fix, +c we severely limit the area that is searched, because we do not +c want to confuse a wind minimum out on the periphery of a storm +c with the center wind minimum. Therefore, this subroutine is not +c called until center fixes have been made for the 5 other parms +c (z850, z700, zeta850, zeta700, mslp). Once those fixes have been +c made, a modified first guess is made of the average of the guess +c position for this time and the 5 other parm fixes. That modified +c guess position is passed into this subroutine as uvgeslon and +c uvgeslat, and that's where the searching for the wind minimum +c is done. To get the wind minimum, the u and v data are first +c interpolated down to a fine grid (see details below for exact +c figures), and then a single-pass barnes analysis is done on that +c fine grid. The reason that we first interpolate the data (which +c is different from how we do the other parms) is that if we just +c use the original grid resolution, we may not be able to +c accurately pick out a minimum in the wind field at the center. +c + USE radii; USE grid_bounds; USE tracked_parms; USE trig_vals + USE level_parms; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real, allocatable :: uold(:,:),vold(:,:),unew(:,:),vnew(:,:) + real, allocatable :: rlonold(:),rlatold(:),rlonnew(:),rlatnew(:) + real, allocatable :: vmag(:,:) + real :: dx,dy + real :: grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + character*1 :: gotlat + logical(1) cflag, valid_pt(imax,jmax) + logical(1), allocatable :: lbi(:,:) +c + gotlat = 'n' +c +c ----------------------------------------------------------------- +c INTERPOLATE INPUT GRID TO SMALLER GRID +c ----------------------------------------------------------------- +c +c Get beginning and ending j points (on the input grid) for a +c smaller array that surrounds the storm. It is this smaller array +c that we will interpolate to a finer grid. +c +c Calculate number of pts to either side of this j to search +c + npts = ceiling(rads_vmag/(dtk*((dx+dy)/2.))) +c + call get_ij_bounds (npts,0,ritrk_vmag,imax,jmax,dx,dy + & ,glatmax,glatmin,glonmax,glonmin,uvgeslon,uvgeslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if ( verb .ge. 3 ) then + print *,' ' + print *,' After get_ij D, ibeg jbeg = ',ibeg,jbeg + print *,' After get_ij D, iend jend = ',iend,jend + endif + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center from call to ' + print *,'!!! get_ij_bounds, stopping processing for ' + print *,'!!! storm number ',ist + endif + + igucret = 92 + return + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and ' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the ibeg returned from' + print *,'!!! get_ij_bounds is < 1, and our gridtype is NOT' + print *,'!!! global, so we are going to redefine ibeg to 1.' + print *,' ' + endif + + ibeg = 1 + endif + endif + + if (jbeg < 1) jbeg = 1 + + if (ibeg > imax .or. jbeg > jmax .or. jbeg < 1 .or. + & iend < 1 .or. jend < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center calculating ibeg, iend, jbeg' + print *,'or jend. ibeg= ',ibeg,' iend= ',iend,' jbeg= ',jbeg + print *,'jend= ',jend + print *,'uv center will not be calculated for this time.' + endif + + igrret = 99 + return + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, but our gridtype is ' + print *,'!!! global, so we are going to leave it as is and' + print *,'!!! account for the grid wrapping.' + print *,' ' + endif + + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In get_uv_center, the iend returned from' + print *,'!!! get_ij_bounds is > imax, and our gridtype is' + print *,'!!! NOT global, so we will redefine iend to imax.' + print *,' ' + endif + + iend = imax + endif + endif + + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, ibeg= ',ibeg,' iend= ',iend + print *,' jbeg= ',jbeg,' jend= ',jend + print *,' ilonfix= ',ilonfix,' jlatfix= ',jlatfix + endif +c + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + +c This next if statement determines how many times to interpolate +c the input grid to a smaller grid. Here are the grid sizes for +c some of the typical grids that will be used: +c +c Original grid size # of interps Final grid size +c -------------------- ------------ --------------------- +c 1.00 deg (111.19 km) 3 0.125 deg (13.9 km) +c 1.25 deg (138.99 km) 3 0.156 deg (17.4 km) +c 2.50 deg (277.99 km) 4 0.156 deg (17.4 km) + + if ((dx+dy)/2. > 1.2) then + numinterp = 4 + else if ((dx+dy)/2. > 0.50 .and. (dx+dy)/2. <= 1.2) then + numinterp = 3 + else if ((dx+dy)/2. > 0.25 .and. (dx+dy)/2. <= 0.50) then + numinterp = 2 + else if ((dx+dy)/2. > 0.10 .and. (dx+dy)/2. <= 0.25) then + numinterp = 1 + else if ((dx+dy)/2. <= 0.10) then + numinterp = 0 + endif + + dell = (dx+dy)/2. + imxold = iend - ibeg + 1 + jmxold = jend - jbeg + 1 + +c -------------------------------------------------------------- +c Before interpolating, make sure that all the original +c points have valid data. If they don't then exit the +c subroutine. NOTE: This is NOT checking to see if ALL the pts +c on the complete & full input grid have valid data; it only +c checks those points that are within the box returned from +c get_ij_bounds. + + do i=ibeg,iend + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_uv_center, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! PROCESSING WILL STOP. ' + print *,'!!! Subroutine location A....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',i + print *,' ' + endif + + stop 94 + endif + else + ip = i + endif + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ip = i + imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine get_uv_center' + print *,'!!! for a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + do j=jbeg,jend + if (.not. valid_pt(ip,j)) goto 975 + enddo + + enddo + +c ------------------------------------ +c Now begin the interpolation process + + allocate (uold(imxold,jmxold),stat=iuo) + allocate (vold(imxold,jmxold),stat=ivo) + allocate (rlonold(imxold),stat=iloo) + allocate (rlatold(jmxold),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. iloo /= 0 .or. ilao /= 0) goto 970 + + do intnum = 1,numinterp + + if (intnum == 1) then + + do i=ibeg,iend + + ik = i + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + ik = i + imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i < 1' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i + endif + + igucret = 92 + return + endif + endif + + if (i > imax) then + if (trkrinfo%gridtype == 'global') then + ik = i - imax !GM wrapping + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_uv_center, i > imax' + print *,'!!! for a non-global grid at AA.' + print *,'!!! i = ',i,' imax= ',imax + endif + + igucret = 92 + return + endif + endif + + rlonold(i-ibeg+1) = glon(ik) + do j=jbeg,jend + uold(i-ibeg+1,j-jbeg+1) = u(ik,j,nlev) + vold(i-ibeg+1,j-jbeg+1) = v(ik,j,nlev) + if (gotlat == 'n') then + rlatold(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatold once + enddo + + else + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate (rlatold) + allocate (uold(imxnew,jmxnew),stat=iuo) + allocate (vold(imxnew,jmxnew),stat=ivo) + allocate (rlonold(imxnew),stat=iloo) + allocate (rlatold(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 970 + + gotlat = 'n' + do i=1,imxnew + rlonold(i) = rlonnew(i) + do j=1,jmxnew + uold(i,j) = unew(i,j) + vold(i,j) = vnew(i,j) + if (gotlat == 'n') then + rlatold(j) = rlatnew(j) + endif + enddo + gotlat = 'y' + enddo + + imxold = imxnew + jmxold = jmxnew + deallocate (unew); deallocate (vnew) + deallocate (rlonnew); deallocate (rlatnew) + + endif + + dell = 0.5 * dell + imxnew = 2 * imxold - 1 + jmxnew = 2 * jmxold - 1 + + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + + call bilin_int_even (imxold,jmxold,uold + & ,imxnew,jmxnew,unew,ibiret) + call bilin_int_even (imxold,jmxold,vold + & ,imxnew,jmxnew,vnew,ibiret) +c call lin_int (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int_lon (imxold,imxnew,rlonold,rlonnew,iliret) + call lin_int (jmxold,jmxnew,rlatold,rlatnew,iliret) + + chk_lonspc_old = rlonold(imxold) - rlonold(imxold - 1) + chk_latspc_old = rlatold(jmxold) - rlatold(jmxold - 1) + chk_lonspc_new = rlonnew(imxnew) - rlonnew(imxnew - 1) + chk_latspc_new = rlatnew(jmxnew) - rlatnew(jmxnew - 1) + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In get_uv_center, intnum= ',intnum + print *,'imxold= ',imxold,' imxnew= ',imxnew + print *,'jmxold= ',jmxold,' jmxnew= ',jmxnew + print *,'Grid boundaries of modified uv grid: ' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ' + & ,grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ' + & ,grid_minlon + endif + + enddo + +c ------------------ + + deallocate (uold); deallocate (vold) + deallocate (rlonold); deallocate(rlatold) + + if (numinterp == 0) then + + ! No interpolations were done for this fine mesh grid, but we + ! need to fill some of these arrays and define variables for + ! subsequent subroutine calls just below here that require + ! the variables imxnew, jmxnew, and the arrays unew and vnew. + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional ' + print *,'grid; iend should not > imax here !!!' + endif + + igucret = 99 + return + endif + endif + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'ERROR in get_uv_center: Should not have gotten' + print *,'to this point in get_uv_center for a regional' + print *,'grid; ibeg should not < 1 here !!!' + endif + + igucret = 99 + return + endif + endif + + imxnew = iend - ibeg + 1 + jmxnew = jend - jbeg + 1 + allocate (unew(imxnew,jmxnew),stat=iuo) + allocate (vnew(imxnew,jmxnew),stat=ivo) + allocate (rlonnew(imxnew),stat=iloo) + allocate (rlatnew(jmxnew),stat=ilao) + if (iuo /= 0 .or. ivo /= 0 .or. + & iloo /= 0 .or. ilao /= 0) goto 971 + gotlat = 'n' + + do i=ibeg,iend + + ip = i + + if (i > imax) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i - imax ! Wrapping past GM + endif + + if (i < 1) then + ! This HAS to be a global, wrapping grid, or else the if + ! statement a few lines up would have caught this already. + ip = i + imax ! Wrapping past GM + endif + + rlonnew(i-ibeg+1) = glon(ip) + do j=jbeg,jend + unew(i-ibeg+1,j-jbeg+1) = u(i,j,nlev) + vnew(i-ibeg+1,j-jbeg+1) = v(i,j,nlev) + if (gotlat == 'n') then + rlatnew(j-jbeg+1) = glat(j) + endif + enddo + gotlat = 'y' ! Only need to fill rlatnew once + enddo + + endif + + grid_maxlat = rlatnew(1) + grid_minlat = rlatnew(jmxnew) + grid_minlon = rlonnew(1) + grid_maxlon = rlonnew(imxnew) + + if ( verb .ge. 3 ) then + print *,'Grid boundaries of modified uv grid in get_uv_center:' + print *,'grid_maxlat= ',grid_maxlat,' grid_minlat= ',grid_minlat + print *,'grid_maxlon= ',grid_maxlon,' grid_minlon= ',grid_minlon + endif + + allocate (vmag(imxnew,jmxnew),stat=ivm) + allocate (lbi(imxnew,jmxnew),stat=ilb) + if (ivm /= 0 .or. ilb /= 0) goto 972 + call calc_vmag (unew,vnew,imxnew,jmxnew,vmag,icvret) + deallocate (unew); deallocate (vnew) + + lbi = .TRUE. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before call to find_maxmin, imxnew= ',imxnew + & ,'jmxnew= ',jmxnew,' ist= ',ist + write (6,171) dell,uvgeslon,360.-uvgeslon,uvgeslat + 171 format (' dell= ',f7.3,' uvgeslon= ',f8.3,'E (',f8.3,'W)' + & ,' uvgeslat= ',f8.3) + endif + +c Note that in the next call, I pass the 'global' argument to +c find_maxmin. This defines what type of grid it is, so that the +c proper grid_buffer can be chosen. This grid_buffer is designed +c to avoid having a center be chosen too close to the grid +c boundary. However, in the case of vmag here, we are only using +c a small subgrid, and we want to make sure we use *all* points +c in that subgrid for searching, and that will occur if we set that +c calling argument to 'global' as opposed to 'regional'. + + call find_maxmin (imxnew,jmxnew,dell,dell,'vmag' + & ,vmag,'min',ist,uvgeslon,uvgeslat,rlonnew,rlatnew,lbi + & ,trkrinfo,cflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,'global',ifmret) + deallocate (vmag); deallocate (lbi) + deallocate (rlonnew); deallocate (rlatnew) +c + if (ifmret == 0) then + goto 995 + else + igucret = ifmret + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_center in call to find_maxmin' + print *,'!!! storm num = ',ist,' igucret = ',igucret + endif + + goto 998 + endif +c + 970 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either uold, vold,' + print *,'!!! rlonold or rlatold in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 971 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either unew, vnew,' + print *,'!!! rlonnew or rlatnew in get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! intnum= ',intnum + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! imxold= ',imxold,' jmxold= ',jmxold + print *,'!!! iuo= ',iuo,' ivo= ',ivo + print *,'!!! iloo= ',iloo,' ilao= ',ilao + endif + + igucret = 97 + goto 998 + + 972 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR ALLOCATING either vmag or lbi in ' + print *,'!!! subroutine get_uv_center' + print *,'!!! Storm number = ',ist + print *,'!!! imxnew= ',imxnew,' jmxnew= ',jmxnew + print *,'!!! ivm= ',ivm,' ilb= ',ilb + endif + + igucret = 97 + goto 998 + + 975 continue + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Inside get_uv_center, at least one of the points' + print *,'!!! is not a valid data point. This point may be ' + print *,'!!! outside the valid data bounds of a regional grid' + print *,'!!! i= ',i,' j= ',j + print *,'!!! Storm number = ',ist + endif + + igucret = 98 + goto 998 +c + 995 continue + igucret = 0 +c + 998 continue + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_uv_guess (guesslon,guesslat,clon,clat + & ,calcparm,ist,ifh,maxstorm + & ,uvgeslon,uvgeslat,igugret) +c +c ABSTRACT: The purpose of this subroutine is to get a modified +c first guess lat/lon position before searching for the +c minimum in the wind field. The reason for doing this is +c to better refine the guess and avoid picking up a wind +c wind minimum far away from the center. So, use the +c first guess position (and give it strong weighting), and +c then also use the fix positions for the current time +c (give the vorticity centers stronger weighting as well), +c and then take the average of these positions. +c +c INPUT: +c guesslon guess longitude for this forecast time +c guesslat guess latitude for this forecast time +c clon array with center longitude fixes for the various parms +c clat array with center latitude fixes for the various parms +c calcparm logical; tells whether or not a parm has a valid fix +c at this forecast hour +c ist index for current storm +c ifh index for current forecast hour +c maxstorm max # of storms that can be handled +c +c OUTPUT: +c uvgeslon contains modified guess longitude position at which to +c look for the wind minimum +c uvgeslat contains modified guess latitude position at which to +c look for the wind minimum +c igugret return code for this subroutine (0=normal) +c---- +c + USE set_max_parms; USE level_parms; USE error_parms + USE verbose_output + + logical(1) calcparm(maxtp,maxstorm) + real clon(maxstorm,maxtime,maxtp) + real clat(maxstorm,maxtime,maxtp) + real uvgeslon, uvgeslat + real guesslon,guesslat,degrees + integer gt345_ct,lt15_ct + + sumlon = 0.0 + sumlat = 0.0 + ict = 0 + gt345_ct = 0 + lt15_ct = 0 + +c NOTE: We need to be careful in this routine when averaging +c the longitudes together, in case we cross the greenwich +c meridian, because then we may be averaging 345+ lons with +c lons that are less than 15, giving incorrect results. +c Therefore, check for this, and if it occurs, add 360 onto +c any of the <15 lons (add it twice for those lons being +c counted twice (guesslon and the vorticity centers)). + +c Weight the uv guess position by counting the storm's guess +c position twice. + + sumlon = sumlon + 2.*guesslon + sumlat = sumlat + 2.*guesslat + ict = ict + 2 + + if (guesslon > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (guesslon < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct.... + endif + + do ip = 1,maxtp + if ((ip > 2 .and. ip < 7) .or. ip == 10) then + cycle ! because 3-6 are for 850 & 700 u & v and 10 is + ! for surface wind magnitude. + else + if (calcparm(ip,ist)) then + call calcdist (guesslon,guesslat,clon(ist,ifh,ip) + & ,clat(ist,ifh,ip),dist,degrees) + + if (dist < uverrmax) then +c +c Give the vorticity centers 2x weighting as well +c + if (ip == 1 .or. ip == 2 .or. ip == 11) then + sumlon = sumlon + 2.*clon(ist,ifh,ip) + sumlat = sumlat + 2.*clat(ist,ifh,ip) + ict = ict + 2 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 2 ! Yes, 2 is correct... + endif + else + sumlon = sumlon + clon(ist,ifh,ip) + sumlat = sumlat + clat(ist,ifh,ip) + ict = ict + 1 + if (clon(ist,ifh,ip) > 345.) then + gt345_ct = gt345_ct + 1 + endif + if (clon(ist,ifh,ip) < 15.) then + lt15_ct = lt15_ct + 1 ! Only 1 for non-zeta parms + endif + endif + + endif + + endif + endif + enddo +c + if (ict > 0) then + if (gt345_ct > 0 .and. lt15_ct > 0) then + ! We have some parms left of the GM and some to the right, + ! so we will add (360*lt15_ct) to the sum of the lons (sumlon) + uvgeslon = (sumlon + (360.*float(lt15_ct)))/ ict + else + uvgeslon = sumlon / ict + endif + if (uvgeslon >= 360.0) then + uvgeslon = uvgeslon - 360. + endif + uvgeslat = sumlat / ict + igugret = 0 + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in get_uv_guess, ict not > 0, ict= ',ict + print *,'!!! vmag center will not be calculated for this' + print *,'!!! storm -- at least not at this level' + print *,'!!! Storm number = ',ist + endif + + igugret = 91 + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calc_vmag (xu,xv,imx,jmx,wspeed,icvret) +c +c ABSTRACT: This subroutine calculates the magnitude of the wind +c speed for an array of points, given real u and real v arrays. +c + real xu(imx,jmx),xv(imx,jmx),wspeed(imx,jmx) +c + do i=1,imx + do j=1,jmx + wspeed(i,j) = sqrt( xu(i,j)*xu(i,j) + xv(i,j)*xv(i,j) ) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine bilin_int_even (imxold,jmxold,xold + & ,imxnew,jmxnew,xnew,ibiret) +c +c ABSTRACT: This subroutine does a bilinear interpolation on a +c grid of evenly spaced data. Do NOT attempt to use this subroutine +c with data that are not evenly spaced or you will get unpredictable +c results. +c + real xold(imxold,jmxold), xnew(imxnew,jmxnew) +c +c +c --------------------------------------------------------------------- +c Latitude ----> | +c | +c L O e O e O e O e O | O: original point from input array +c o | +c n e 1 2 1 2 1 2 1 e | 1: interpolated, primary inter. pt +c g | +c i O 2 O 2 O 2 O 2 O | e: interpolated edge point +c t | +c u e 1 2 1 2 1 2 1 e | 2: interpolated, secondary inter. pt +c d | +c e O 2 O 2 O 2 O 2 O | Interpolations are done in the order +c | as indicated above; First, the input +c | e 1 2 1 2 1 2 1 e | 'O' pts are placed onto the new, +c | | larger grid. From that, the '1' pts +c | O 2 O 2 O 2 O 2 O | can be interpolated. Next, the edge +c | | (e) pts are interpolated using an +c v e 1 2 1 2 1 2 1 e | interpolation of two 'O' pts and one +c | '1' pt. Finally, the '2' pts are +c O e O e O e O e O | done using the 2 surrounding '0' and +c | '1' pts. Bilinear interpolation is +c | made incredibly easier by the fact +c | that the grid is evenly spaced. +c --------------------------------------------------------------------- +c NOTE: Remember that the arrays that are read in are indexed as +c (lon,lat), so that in the diagram above, pt (1,1) is at the upper +c left and pt (imax,jmax) is at the lower right, and each column is +c a new latitude and each row is a new longitude. +c +c ----------------------------------------------------------------- +c Put original (O) values from input array into new, expanded array +c ----------------------------------------------------------------- +c + do i=1,imxold + do j=1,jmxold + xnew(2*i-1,2*j-1) = xold(i,j) + enddo + enddo +c +c ---------------------------------------------- +c Interpolate to get primary interior (1) points +c ---------------------------------------------- +c + do i=1,imxold-1 + do j=1,jmxold-1 + xnew(2*i,2*j) = 0.25 * (xnew(2*i-1,2*j-1) + xnew(2*i+1,2*j-1) + & + xnew(2*i+1,2*j+1) + xnew(2*i-1,2*j+1)) + enddo + enddo +c +c --------------------------- +c Interpolate edge (e) points +c --------------------------- +c +c ... Northernmost 'e' points ... +c + j=1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,2)) + enddo +c +c ... Southernmost 'e' points ... +c + j = 2*jmxold - 1 + do i=1,imxold-1 + xnew(2*i,j) = 0.3333 * (xnew(2*i-1,j) + xnew(2*i+1,j) + & + xnew(2*i,j-1)) + enddo +c +c ... Westernmost 'e' points ... +c + i=1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(2,2*j)) + enddo +c +c ... Easternmost 'e' points ... +c + i = 2*imxold - 1 + do j=1,jmxold-1 + xnew(i,2*j) = 0.3333 * (xnew(i,2*j-1) + xnew(i,2*j+1) + & + xnew(i-1,2*j)) + enddo +c +c ------------------------------------------------ +c Interpolate to get secondary interior (2) points +c ------------------------------------------------ +c + do j=2,2*jmxold-2 + istep = mod(j+1,2) + do i=istep+2,2*imxold-2,2 + xnew(i,j) = 0.25 * (xnew(i-1,j) + xnew(i,j-1) + xnew(i+1,j) + & + xnew(i,j+1)) + enddo + enddo +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points +c + do i=1,ioldmax-1 + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine lin_int_lon (ioldmax,inewmax,xold,xnew,iliret) +c +c ABSTRACT: This subroutine linearly interpolates evenly spaced +c data from one grid to another. This particular +c routine is specifically used for interpolating +c longitudes, and it factors in the possibility of +c interpolating across the greenwich meridian. +c + real xold(ioldmax), xnew(inewmax) +c +c First just copy points from old grid onto new, larger grid +c + do i=1,ioldmax + xnew(2*i-1) = xold(i) + enddo +c +c Now interpolate to get the in-between points, and make the +c necessary adjustment when interpolating a longitude between, +c for example, 359.5 and 0.0. +c + do i=1,ioldmax-1 + if (xnew(2*i-1) > 350. .and. xnew(2*i+1) < 10.) then + xnew(2*i) = 0.5 * (xnew(2*i-1) + (360. + xnew(2*i+1))) + else + xnew(2*i) = 0.5 * (xnew(2*i-1) + xnew(2*i+1)) + endif + enddo +c + return + end + +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_zeta_values (fixlon,fixlat,imax,jmax,dx,dy + & ,trkrinfo,imeanzeta,igridzeta,readflag + & ,valid_pt,ist,ifh,maxstorm,inp,igzvret) +c +c ABSTRACT: This subroutine finds the maximum and mean zeta values +c at 850 & 700 mb, near a storm center. It is called from +c subroutine tracker, and its purpose is to report these values +c that will then be written out to a special, modified version of +c the atcfunix file. + + USE tracked_parms; USE radii; USE trig_vals; USE set_max_parms + USE trkrparms; USE level_parms; USE grid_bounds; USE inparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + + logical(1) readflag(14),valid_pt(imax,jmax),compflag + character cmaxmin*3,cvort_maxmin*3 + real fixlon(maxstorm,maxtime),fixlat(maxstorm,maxtime) + real gridpoint_maxmin,xmeanzeta,dx,dy,re,ri,parmlon,parmlat + integer igridzeta(nlevgrzeta),imeanzeta(nlevgrzeta) + integer n,ix1,ix2,ilev,npts,imax,jmax,igzvret,ilonfix,jlatfix + integer idum,jdum,ibeg,jbeg,iend,jend,igiret,icount,iuret + integer ifilret,ist,ifh,ifmret,maxstorm + +c First, call get_ij_bounds in order to get the (i,j) coordinates +c of the (fixlon,fixlat) position that we need to search around. +c These (i,j) coordinates are returned as ilonfix and jlatfix. + + npts = imax * jmax + + call get_ij_bounds (npts,0,ridlm,imax,jmax + & ,dx,dy,glatmax,glatmin,glonmax,glonmin + & ,fixlon(ist,ifh),fixlat(ist,ifh),trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (ilonfix > imax) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix - imax ! If wrapping past GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In get_zeta_values, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! eastern ilonfix = ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if (ilonfix < 1) then + if (trkrinfo%gridtype == 'global') then + ilonfix = ilonfix + imax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: ilonfix < 1 in subroutine' + print *,'!!! get_zeta_values for a non-global grid.' + print *,'!!! ilonfix= ',ilonfix + print *,'!!! ' + print *,' ' + endif + + igzvret = 99 + return + endif + endif + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,601) + write (6,603) + write (6,605) + write (6,607) + write (6,609) + write (6,*) ' ' + write (6,613) ist,ifh + write (6,615) fixlon(ist,ifh),360.-fixlon(ist,ifh) + & ,fixlat(ist,ifh) + write (6,617) ilonfix,jlatfix + endif + + 601 format(1x,'#---------------------------------------------------#') + 603 format(1x,'# Entering loop to determine the mean and gridpoint #') + 605 format(1x,'# max zeta values at 850 and 700 mb for the purpose #') + 607 format(1x,'# of reporting them on the modified atcfunix file. #') + 609 format(1x,'#---------------------------------------------------#') + 613 format(1x,'--- In get_zeta_values, ist= ',i3,' ifh= ',i3) + 615 format(1x,' Fix location for this time: ',f7.2,'E (',f6.2,'W)' + & ,2x,f7.2) + 617 format(1x,' ilonfix= ',i4,' jlatfix= ',i4) + + report_zeta_loop: do n=1,2 + + gridpoint_maxmin = -99.0 + xmeanzeta = -99.0 + compflag = .true. + + select case (n) + case (1); ilev=850 ! For 850 mb + case (2); ilev=700 ! For 700 mb + end select + + if (zeta(ilonfix,jlatfix,n) > -9990.0) then + + ! ------------------------------------------- + ! We have valid zeta data for this level, so + ! we first call barnes now to get the mean zeta + ! surrounding our found center position. + ! ------------------------------------------- + + if (fixlat(ist,ifh) > 0.0) then + cvort_maxmin = 'max' + else + cvort_maxmin = 'min' + endif + + call find_maxmin (imax,jmax,dx,dy,'zeta' + & ,zeta(1,1,n),cvort_maxmin,ist,fixlon(ist,ifh) + & ,fixlat(ist,ifh),glon,glat,valid_pt,trkrinfo + & ,compflag,parmlon,parmlat,xmeanzeta + & ,glatmax,glatmin,glonmax,glonmin,inp%modtyp,ifmret) + if (ifmret == 0) then ! Out of regional grid bounds + imeanzeta(n) = int ((xmeanzeta * 1e6) + 0.5) + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,519) + write (6,520) + write (6,521) + endif + + 519 format (1x,' The call to find_maxmin in get_zeta_values') + 520 format (1x,' returned a nonzero return code. The search') + 521 format (1x,' for zeta values will not be done.') + exit report_zeta_loop ! If out of grid bounds at 850, + ! then will also be out at 700... + endif + else + imeanzeta(n) = -99 + igridzeta(n) = -99 + exit report_zeta_loop + endif + + if ( verb .ge. 3 ) then + write (6,621) n,ilev,xmeanzeta,imeanzeta(n) + 621 format (1x,'+++ RPT_MEAN_ZETA: n= ',i2,' lev= ',i4 + & ,' xmeanzeta= ',f9.6,' imeanzeta (*1e6)= ',i8) + write (6,*) ' --- mean zeta raw = ',xmeanzeta + endif + + ! ----------------------------------------------- + ! Call fix_latlon_to_ij to get the nearest actual + ! raw (grid) zeta data value, not the mean value. + ! ----------------------------------------------- + + call fix_latlon_to_ij (imax,jmax,dx,dy + & ,zeta(1,1,n),cvort_maxmin,valid_pt,fixlon(ist,ifh) + & ,fixlat(ist,ifh),xmeanzeta,idum,jdum + & ,gridpoint_maxmin,'tracker' + & ,glatmax,glatmin,glonmax,glonmin + & ,trkrinfo,ifilret) + if (ifilret == 0) then + igridzeta(n) = int ((gridpoint_maxmin * 1e6) + 0.5) + else + igridzeta(n) = -99 + endif + + if ( verb .ge. 3 ) then + write (6,623) n,ilev,gridpoint_maxmin,igridzeta(n),ifilret + 623 format (1x,'+++ RPT_GRID_ZETA: n= ',i2,' lev= ',i4 + & ,' grid zeta= ',f9.6,' igrid zeta (*1e6)= ',i8 + & ,' ifilret= ',i3) + write (6,*) ' --- grid zeta raw= ',gridpoint_maxmin + endif + + enddo report_zeta_loop + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,631) + write (6,633) + write (6,635) + write (6,*) ' ' + endif + + 631 format(1x,'#---------------------------------------------------#') + 633 format(1x,'# End of loop to get 850 & 700 zeta for atcf file. #') + 635 format(1x,'#---------------------------------------------------#') + + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine find_maxmin (imax,jmax,dx,dy,cparm,fxy,maxmin,ist + & ,guesslon,guesslat,rlonv,rlatv,valid_pt,trkrinfo + & ,compflag,ctlon,ctlat,xval,grid_maxlat,grid_minlat + & ,grid_maxlon,grid_minlon,cmodel_type,ifmret) +c +c This routine finds the location (clon,clat) of and value of the +c the max or min of fxy in the vicinity of slon,slat. The value of +c the input flag maxmin determines whether to look for a max or a +c min value. The max/min is determined by finding the point which +c gives the max/min value of a single point barnes analysis of fxy +c with e-folding radius re (km) and influence radius ri (km). The +c initial search is restricted to a radius rads around the point +c (slon,slat) on a grid with lon,lat spacing dx and dy. The location +c is refined by reducing the spacing of the search grid by a factor +c of two, nhalf times. +c +c INPUT: +c imax Num pts in i direction on input grid +c jmax Num pts in j direction on input grid +c dx Grid spacing in i-direction on input grid +c dy Grid spacing in j-direction on input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c maxmin Char string indicating whether to search for a max or min +c ist Number of the storm being processed +c guesslon Guess longitude of the storm +c guesslat Guess latitude of the storm +c rlonv Array containing longitude values of input grid points +c rlatv Array containing latitude values of input grid points +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c trkrinfo derived type detailing user-specified grid info +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c cmodel_type character, 'global' or 'regional' +c +c INPUT/OUTPUT: +c compflag Logical; continue processing this storm or not (would be +c set to FALSE if, for example, the guess position is +c outside the domain of a regional grid) +c +c OUTPUT: +c ctlon Center longitude of storm found for this parameter +c ctlat Center latitude of storm found for this parameter +c xval Max or Min value found at the (ctlon,ctlat) +c ifmret Return code from this subroutine +c +c UPDATE DEC 2009: For the HFIP HRH testing, it was found that +c due to the very limited domain size of some of the models, the +c barnes scheme was allowing points close to the grid boundaries +c to erroneously be selected as the center point. We add in a +c buffer (grid_buffer) here to prevent this from occurring. + + USE radii; USE grid_bounds; USE set_max_parms; USE level_parms + USE trig_vals; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + character(*) maxmin,cparm,cmodel_type + logical(1) compflag, valid_pt(imax,jmax) + real fxy(imax,jmax),rlonv(imax),rlatv(jmax) + real ctlon,ctlat,degrees,dx,dy,guesslon,guesslat,xval + real rads,re,ri,dell,fmax,fmin,rlatt,rlont,dist,ftemp,ritmp + real vmag_latmax,vmag_latmin,vmag_lonmax,vmag_lonmin,retmp + real tlon,tlat,grid_buffer,temp_grid_minlon,temp_guesslon + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + integer imax,jmax,ist,bskip1,bskip2,iskip,ifmret,npts,maxvgrid + integer ibeg,iend,jbeg,jend,ilonfix,jlatfix,igiret,icount,iret + integer ibct,ibarnes_loopct,i,j,k,iix,jix,jvlatfix,ivlonfix + integer nhalf,icvpret + integer date_time(8) + character (len=10) big_ben(3) +c + ifmret = 0 + nhalf = 5 +c +c ----------------------------------------------------------- +c Set initial parms for use in find_maxmin. +c Different radii used for V magnitude than for other parms, +c see discussion in module radii for more details. +c + if (cparm == 'vmag') then + +c NOTE: The maxvgrid variable determines what size grid to send +c to subroutine barnes. e.g., maxvgrid = 8 means send an +c 8x8 grid; maxvgrid = 12 means send a 12x12 grid. For +c ultra-fine mesh grids (finer than 0.04 deg, or 1/25 deg), +c we expand to 12 in order to sample a few more points +c around each grid point. + + if ((dx+dy)/2. > 0.04) then + maxvgrid = 8 + else + maxvgrid = 12 + endif + + rads = rads_vmag; re = retrk_vmag; ri = ritrk_vmag + re = (float(maxvgrid)/4) * ((dx+dy)/2. * dtk) ! Basically, this +c sets re equal to half the distance from the gridpoint +c in question to the farthest point that will be +c sampled when the (maxvgrid x maxvgrid) grid is passed +c on to subroutine barnes. Thus, just ignore the +c parameter retrk_vmag, and use this instead. + else if ((dx+dy)/2. < 1.26 .and. (dx+dy)/2. >= 0.40) then + rads = rads_most; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.40 .and. (dx+dy)/2. >= 0.10) then + rads = rads_fine; re = retrk_most; ri = ritrk_most + else if ((dx+dy)/2. < 0.10) then + rads = rads_hres; re = retrk_hres; ri = ritrk_most + else + rads = rads_coarse; re = retrk_coarse; ri = ritrk_coarse + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'At beg of find_maxmin, rads= ',rads,' re= ',re + & ,' ri= ',ri,' cparm= ',cparm,' dx= ',dx,' dy= ',dy + endif + + dell = (dx+dy)/2. + npts = rads/(dtk*dell) + fmax = -1.0e+12; fmin = 1.0e+12 + ctlon = 0.0; ctlat = 0.0 + + if (npts == 0) npts = 1 + +c For the barnes analysis, we will want to speed things up for +c finer resolution grids. We can do this by skipping some of +c the points in the barnes analysis. + + if (dell > 0.20) then + bskip1 = 2 + bskip2 = 1 + else if (dell > 0.10 .and. dell <= 0.20) then + bskip1 = 4 + bskip2 = 2 + else if (dell > 0.05 .and. dell <= 0.10) then + bskip1 = 6 + bskip2 = 3 + else if (dell > 0.03 .and. dell <= 0.05) then + bskip1 = 10 + bskip2 = 5 + else if (dell <= 0.03) then + bskip1 = 15 + bskip2 = 5 + endif + + if (cparm == 'vmag') then + bskip1 = 1 + bskip2 = 1 + endif + +c If input parm is vmag, we've already done the minimizing by +c interpolating to the fine mesh grid, so we'll simply send the +c bounds that were input to this subroutine to barnes +c as boundaries for the array to search. For all other parms, +c however, no minimizing has been done yet, so we need to call +c get_ij_bounds to set the boundaries for a much smaller grid that +c surrounds the storm (as opposed to having subroutine barnes +c search the entire global grid). + + if (cparm == 'vmag') then + + if ( verb .ge. 3 ) then + print *,'In find_maxmin, jmax= ',jmax,' imax= ',imax + endif + + ibeg=1; jbeg=1; iend=imax; jend=jmax + vmag_latmax = rlatv(1) ! N-most lat of vmag subgrid + vmag_latmin = rlatv(jmax) ! S-most lat of vmag subgrid + vmag_lonmin = rlonv(1) ! W-most lon of vmag subgrid + vmag_lonmax = rlonv(imax) ! E-most lon of vmag subgrid + + if ( verb .ge. 3 ) then + write (6,44) vmag_latmax,vmag_lonmin,360.-vmag_lonmin + & ,imax,jmax + write (6,46) vmag_latmin,vmag_lonmax,360.-vmag_lonmax + endif + + 44 format (' vmag_latmax= ',f8.3,' vmag_lonmin= ',f8.3 + & ,'E (',f8.3,'W) imax= ',i4,' jmax= ',i4) + 46 format (' vmag_latmin= ',f8.3,' vmag_lonmax= ',f8.3 + & ,'E (',f8.3,'W)') + + if (vmag_lonmin > 330. .and. vmag_lonmax < 30.) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: For a case of find_maxmin, our vmag' + print *,'!!! subgrid is straddling the GM. The code should' + print *,'!!! be able to handle this, but if strange errors' + print *,'!!! are occurring, check into the code either here' + print *,'!!! in find_maxmin or get_uv_ctr.' + print *,' ' + endif + endif + + npts = ceiling(rads/(dtk*dell)) + + else + + call get_ij_bounds (npts,0,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,guesslon,guesslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to ' + print *,'!!! get_ij_bounds, stopping processing for' + print *,'!!! storm number ',ist + endif + + ifmret = 92 + return + endif + + endif + +c +c --------------------------------------------------------------- +c + if ( verb .ge. 3 ) then + print *,' ' + write (6,39) guesslon,360.-guesslon,guesslat + 39 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + if (cparm == 'vmag') then + print *,'ilonfix= (unused) jlatfix= (unused)' + & ,' npts= ',npts + print *,'ilonfix and jlatfix are meaningless for computing' + print *,'vmag, so ignore the large values you see for them.' + else + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + endif + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + endif + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + + if ( verb .ge. 3 ) then + write (6,31) date_time(5),date_time(6),date_time(7) + 31 format (1x,'TIMING: find_maxmin 1 ',i2.2,':',i2.2,':',i2.2) + endif + + ibct=0 + ibarnes_loopct = 0 + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + ! Our grid is straddling over the GM. This can happen either + ! with a global grid or with a regional grid. How can it happen + ! for a global grid? Well, for the case in which this routine + ! is called from subroutine get_uv_center, where a smaller + ! subgrid of data is passed in, and that smaller subgrid may + ! straddle the GM. Anyway, we need a workaround. + ! This workaround will put the minimum longitude + ! in terms of a negative number, e.g., as opposed to being say, + ! 354, it will be -6. You can then leave the grid_maxlon as is. + temp_grid_minlon = grid_minlon - 360. + if (guesslon > 330.) then + ! If our grid is straddling the GM and we have adjusted the + ! grid_minlon to be a negative number, then we also need to + ! check on the guesslon and adjust it if it is also to west + ! of the GM. + temp_guesslon = guesslon - 360. + else + temp_guesslon = guesslon + endif + else + temp_grid_minlon = grid_minlon + temp_guesslon = guesslon + endif + + jix = 0 + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + jloop: do j=-npts,npts,bskip1 + + jix = jix + 1 + rlatt = guesslat + dell*float(j) + + iix = 0 + +c vlat(jix) = rlatt + + iloop: do i=-npts,npts,bskip1 + + iix = iix + 1 + rlont = temp_guesslon + dell*float(i) + +c if (cparm == 'vmag') then +c print *,' ' +c print '(a16,i6,a4,i6,2(a8,f8.3),a12,f8.3)' +c & ,'in find_max, i= ',i +c & ,' j= ',j,' rlatt= ',rlatt,' rlont= ',rlont +c & ,' 360-rlont= ',360.-rlont +c endif + +c If any points in the search grid would extend beyond the grid +c boundaries, then check and see if this is global grid. If it +c is, and the extension occurred in the i-direction, then adjust +c the longitude to allow for grid wrapping. If it is a regional +c grid, then just cycle the iloop. In previous versions of the +c tracker, we would exit with an error message, but doing it +c this way allows us to continue tracking some systems that may +c be close to the grid boundary. Also, remember to factor in +c the grid_buffer discussed in the doc block above for this +c subroutine. + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont - 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlont < (temp_grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + if (cparm == 'vmag') then + cycle iloop ! We are off the small vmag subgrid + else + rlont = rlont + 360. ! We just GM-wrapped for the full, + ! regular, global grid + endif + else + cycle iloop + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer)) then + cycle iloop + endif + +c Make sure that the point being investigated here as a +c potential center has valid data at that point. That is, for +c some hires regional grids that have been rotated/converted +c from a non-latlon grid to a latlon grid, there can be +c locations within the (i,j) space that do not have valid data +c at them. It makes no sense to consider a point such as this +c as a potential center. +c There is another simpler case here that we are watching out +c for. This is simply the case, again for model data where we +c only have the innermost nest. Depending on what we choose +c for the variable "rads" above, with the way that "npts" is +c defined for these iloops and jloops that we're in, we may be +c searching over points that are simply well off the grid. +c Therefore, it is critical to run through this +c check_valid_point subroutine to make sure that we're not +c going to inadvertantly be performing an analysis at one of +c these "off-grid" points. So.... if the return code from +c check_valid_point comes back non-zero, simply cycle iloop +c and go to the next point. + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,temp_grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + if ( verb .ge. 1 ) then + print *,'!!! NOT A VALID PT: icvpret= ',icvpret + endif + + cycle iloop + endif + + call calcdist(rlont,rlatt,temp_guesslon,guesslat,dist,degrees) + if (dist .gt. rads) cycle iloop + + if (cparm == 'vmag') then + +c This next bit of code gets the ij coordinates for an 8x8 +c box around the current point under consideration. These ij +c coordinates are sent to barnes so that barnes only loops +c 64 times, as opposed to nearly 10,000 if the whole 97x97 +c array were sent. So, fix rlatt to the grid point just +c northward of rlatt and fix rlont to the grid point just +c eastward of rlont. Note that this makes for a modified +c barnes analysis in that we're sort of specifying ahead of +c time exactly which grid points will be included and we'll +c be excluding some points that would be near the periphery +c of each (rlont,rlatt)'s range, but as long as we're consis- +c tent and do it this way for each point, it's well worth the +c trade-off in cpu time. Parameter maxvgrid determines what +c size array to send to barnes (maxvgrid=8 means 8x8) + + jvlatfix = int((vmag_latmax - rlatt)/dy + 1.) + ivlonfix = int((rlont - temp_grid_minlon)/dx + 2.) +c ivlonfix = int((rlont - vmag_lonmin)/dx + 2.) + + ibeg = ivlonfix - (maxvgrid/2) + iend = ivlonfix + (maxvgrid/2 - 1) + jbeg = jvlatfix - (maxvgrid/2 - 1) + jend = jvlatfix + (maxvgrid/2) + + if (ibeg < 1 .or. jbeg < 1 .or. + & iend > imax .or. jend > jmax) then + + ! DO NOT quit if we find a boundary outside the grid + ! bounds. Rather, just set the J violating bound(s) to + ! the min or max limit, and for I bounds, allow the + ! program to continue down to subsequent code below, + ! provided it's a global grid. + +c print *,'!!! ' +c print *,'!!! Before vmag adjustments, boundaries are: ' +c print *,'!!! rlont= ',rlont,' rlatt= ',rlatt,' dx= ',dx +c print *,'!!! temp_grid_minlon= ',temp_grid_minlon +c print *,'!!! vmag_latmax= ',vmag_latmax +c print *,'!!! ivlonfix = ',ivlonfix,' jvlatfix = ',jvlatfix +c print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax +c print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested western boundary' + print *,'!!! is beyond the western bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' ibeg= ',ibeg + print *,'!!! ' + print *,'!!! Vmag will not be computed for' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! If wrapping past GM, there is code below + ! in this find_maxmin routine that can + ! modify the indices appropriately. So... + ! do nothing here. + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_maxmin, the ' + print *,'!!! user-requested eastern boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! the vmag subgrid for this regional ' + print *,'!!! grid. ' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! ivlonfix = ',ivlonfix,' iend= ',iend + print *,'!!! ' + print *,'!!! Vmag will not be computed for ' + print *,'!!! this time.' + print *,' ' + endif + + ifmret = 99 + return + endif + endif + + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,'!!! ' + print *,'!!! *AFTER* vmag adjustments, boundaries are: ' + print *,'!!! ibeg= ',ibeg,' iend= ',iend,' imax= ',imax + print *,'!!! jbeg= ',jbeg,' jend= ',jend,' jmax= ',jmax + endif + + endif + + endif + + if (cparm == 'vmag') then + ri = re * 3 +c print '(a36,f10.4,a6,f10.4)' +c & ,' + before call to vmag barnes, re= ',re,' ri= ',ri + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,bskip1,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes...' + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop + enddo jloop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'After 1st findmax loop, # calls to barnes = ',ibct + print *,'Total # of barnes loop iterations = ',ibarnes_loopct + endif + +c + 55 format ('i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ',f7.3 + & ,' barnval= ',f11.5) + 56 format ('k= ',i3,' i= ',i3,' j= ',i3,' rln= ',f7.3,' rlt= ' + & ,f7.3,' barnval= ',f11.5) + + if (ctlon < 0.) then + ! We have grid-wrapped to find the ctlon, which was found to be + ! < 0, so for reporting purposes and for the start of the next + ! loop, set ctlon to positive degress east. + ctlon = ctlon + 360. + endif + + if (cparm == 'zeta') then + + if ( verb .ge. 3 ) then + print *,'!!! Zeta check, fmax= ',fmax,' fmin= ',fmin + write (6,61) 360.-ctlon,ctlat,fmax*100000.,fmin*100000. + endif + + else + + if ( verb .ge. 3 ) then + write (6,63) 360.-ctlon,ctlat,fmax,fmin + endif + + endif + 61 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 63 format (' After first run, ctlon= ',f8.3,'W ctlat= ',f8.3 + & ,' fmax = ',e16.3,' fmin = ',e16.3) + 111 format (i2,' rlont= ',f7.2,'W rlatt= ',f7.2,' zeta= ',f13.8) + +c Through interpolation, the grid for vmag has already been +c minimized considerably, we don't need to go through the 2nd part +c of this subroutine, which halves the grid spacing. + + if (nhalf < 1 .or. cparm == 'vmag') then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c If on our first pass through, we were dealing with a regional grid +c that straddled the GM, then it becomes (for now) too much of a +c coding hassle to deal with in the rest of this routine (i.e., in +c all the nhalf iterations), so we will just go with the first run +c through for the center fix and exit the routine. + + if (grid_minlon > 330. .and. grid_maxlon < 30.) then + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif + return + endif + +c ------------------------------------------------------------- +c If the grid spacing is +c fine enough (I've chosen 0.2-deg as a min threshold), there is +c no need to halve the grid more than 3 times, as halving a +c 0.2-deg grid 3 times gives a resolution of 0.025-deg (2.7 km), +c or a max error in the position estimate of 2.7/2 = 1.35 km. + + if ((dx+dy)/2. <= 0.2) then + if ((dx+dy)/2. <= 0.05) then + nhalf = 1 + else + nhalf = 2 + endif + endif + +c --------------------------------------------------------------- +c --------------------------------------------------------------- +c Halve the grid spacing to refine the location and value of the +c max/min value, but restrict the area of the new search grid. + +ctpm npts = 3 + npts = npts/2 + npts = max(npts,1) + +c ------------------------------------------------------------- +c First, recalculate the i and j beginning and ending points to +c be used in the barnes analysis subroutine. Only +c do this once for this grid-refinement (even though the grid is +c redefined 3 times in this subroutine), but make sure to have the +c possible search grid be big enough to allow the possibility of +c the grid shifting way right or way left each time through the +c loop (get_ij_bounds takes care of this). + + call get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,ctlon,ctlat,trkrinfo + & ,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) + + if (igiret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in find_maxmin from call to get_ij_bounds' + print *,'!!! just before nhalf loop. Stopping processing' + print *,'!!! for storm number ',ist + endif + + ifmret = 92 + return + endif + +c -------------------------------------------------------------- +c Now do the actual searching for the max/min value + + + if ( verb .ge. 3 ) then + print *,' ' + endif + + if ((dx+dy)/2. <= 1.25 .and. ri >= 300 .and. re >= 150) then + retmp = re + ritmp = ri + re = re * 0.5 + ri = ri * 0.5 + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re has been reduced' + print *,'from ',retmp,' to ',re,', and ri has been reduced ' + print *,'from ',ritmp,' to ',ri + endif + + else + + if ( verb .ge. 3 ) then + print *,'After first pass through barnes, re and ri have NOT ' + print *,'been changed. re = ',re,' ri = ',ri + endif + + endif + + ibct=0 + ibarnes_loopct = 0 + do k=1,nhalf + + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + if ( verb .ge. 3 ) then + write (6,32) k,date_time(5),date_time(6),date_time(7) + 32 format (1x,'TIMING: find_maxmin kloop, k= ',i2,' ',i2.2,':' + & ,i2.2,':',i2.2) + endif + + dell = 0.5*dell + tlon = ctlon + tlat = ctlat + fmax = -1.0e+15; fmin = 1.0e+15 + + iskip = bskip2 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'find_maxmin nhalf loop, cparm= ',cparm,' k= ',k + write (6,161) tlon,360.-tlon,tlat + print *,'ilonfix= ',ilonfix,' jlatfix= ',jlatfix + & ,' npts= ',npts + print *,'ibeg= ',ibeg,' jbeg= ',jbeg,' imax= ',imax + print *,'iend= ',iend,' jend= ',jend,' jmax= ',jmax + print *,'nhalf= ',nhalf,' iskip= ',iskip + endif + + jloop2: do j=-npts,npts,iskip + + rlatt = tlat + dell*float(j) + + iloop2: do i=-npts,npts,iskip + + rlont = tlon + dell*float(i) + + if (rlont >= (grid_maxlon + dx - grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont - 360. + else + cycle iloop2 + endif + endif + + if (rlont < (grid_minlon + grid_buffer)) then + if (trkrinfo%gridtype == 'global') then + rlont = rlont + 360. + else + cycle iloop2 + endif + endif + + if (rlatt > (grid_maxlat - grid_buffer) .or. + & rlatt < (grid_minlat + grid_buffer) .or. + & rlont >= (grid_maxlon + dx - grid_buffer) .or. + & rlont < (grid_minlon + grid_buffer)) then + cycle iloop2 + endif + +c Again, check and make sure that the lat/lon point in +c question here has valid data (see the explanation further +c up in this subroutine inside iloop). + + call check_valid_point (imax,jmax,dx,dy,fxy,maxmin,valid_pt + & ,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) + + if (icvpret /= 0) then + cycle iloop2 + endif + + ibct = ibct + 1 + call barnes(rlont,rlatt,rlonv,rlatv,imax,jmax,ibeg,jbeg + & ,iend,jend,fxy,valid_pt,iskip,re,ri,ftemp,icount,'tracker' + & ,trkrinfo,iret) + + ibarnes_loopct = ibarnes_loopct + icount + + if (iret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! Non-zero RCC from barnes, k= ',k + print *,'!!! Exiting find_maxmin' + endif + + compflag = .FALSE. + ifmret = iret + return + endif + + if (maxmin == 'max') then + if (ftemp > fmax) then + fmax = ftemp + ctlon = rlont + ctlat = rlatt + endif + else + if (ftemp < fmin) then + fmin = ftemp + ctlon = rlont + ctlat = rlatt + endif + endif + + enddo iloop2 + enddo jloop2 + + if ( verb .ge. 3 ) then + if (cparm == 'zeta') then + write (6,71) k,360.-ctlon,ctlat,fmax*100000.,fmin*100000. + else + write (6,73) k,360.-ctlon,ctlat,fmax,fmin + endif + endif + + enddo + + 71 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax (x10e5) = ',e16.3,' fmin (x10e5) = ',e16.3) + 73 format (' nhalf findmax, k= ',i2,' ctlon= ',f8.3,'W ctlat= ' + & ,f8.3,' fmax = ',e16.3,' fmin = ',e16.3) + + 161 format (' guesslon= ',f8.3,'E (',f8.3,'W) guesslat= ',f8.3) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ppp after 2nd findmax loop, # calls to barnes = ' + & ,ibct + print *,'ppp Total # of barnes loop iterations = ' + & ,ibarnes_loopct + endif + + if (maxmin == 'max') then + xval = fmax + else + xval = fmin + endif +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine barnes(flon,flat,rlon,rlat,iimax,jjmax,iibeg,jjbeg + & ,iiend,jjend,fxy,defined_pt,bskip,re,ri,favg,icount,ctype + & ,trkrinfo,iret) +c +c ABSTRACT: This routine performs a single-pass barnes anaylsis +c of fxy at the point (flon,flat). The e-folding radius (km) +c and influence radius (km) are re and ri, respectively. +c +c NOTE: The input grid that is searched in this subroutine is most +c likely NOT the model's full, original grid. Instead, a smaller +c subgrid of the original grid is searched. The upper left and +c lower right grid point indices are passed into this subroutine +c (iibeg, jjbeg, iiend, jjend) for this subgrid. These indices are +c determined in the subroutine get_ij_bounds, and the purpose of +c doing it this way is to limit the number of points for which the +c subroutine has to calculate distances (for a global 1 deg grid, +c the number of loop iterations is reduced from 65160 to somewhere +c around 600). +c +c NOTE: This subroutine will immediately exit with a non-zero +c return code if it tries to access a grid point that does not have +c valid data. This would happen in the case of a regional grid, if +c you try to access a point near the edge of the grid (remember that +c because of the interpolation for the regional grids, there will be +c areas around the edges that have no valid data). +c +c INPUT: +c flon Lon value for center point about which barnes anl is done +c flat Lat value for center point about which barnes anl is done +c rlon Array of lon values for each grid point +c rlat Array of lat values for each grid point +c iimax Max number of pts in x-direction on input grid +c jjmax Max number of pts in y-direction on input grid +c iibeg i index for grid point to start barnes anlysis (upp left) +c jjbeg j index for grid point to start barnes anlysis (upp left) +c iiend i index for last grid point in barnes anlysis (low right) +c jjend j index for last grid point in barnes anlysis (low right) +c fxy Real array of data on which to perform barnes analysis +c defined_pt Logical; bitmap array used for regional grids +c bskip integer to indicate number of grid points to skip during +c a barnes loop, in order to speed processing +c re input e-folding radius for barnes analysis +c ri input influence radius for searching for min/max +c ctype character that lets subroutine know if this is a search +c for the next position for the purposes of tc vitals or +c for general tracking. In the case of vitals, in +c this barnes subroutine we are more lax and allow the +c routine to keep searching even if we are close to the +c grid boundary. In a general tracking search, if we hit +c the grid boundary even just once, we exit. +c trkrinfo derived type detailing user-specified grid info +c +c OUTPUT: +c favg Average value about the point (flon,flat) +c iret Return code from this subroutine +c + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + real fxy(iimax,jjmax), rlon(iimax), rlat(jjmax) + real degrees + integer bskip + logical(1) defined_pt(iimax,jjmax) + character(*) ctype + +c -------------------------- + + res = re*re + wts = 0.0 + favg = 0.0 + + icount = 0 + + do jix=jjbeg,jjend,bskip + do iix=iibeg,iiend,bskip + + i = iix + j = jix + + if (i < 1) then + if (trkrinfo%gridtype == 'global') then + i = iix + iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i < 1 in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i + print *,' ' + endif + + stop 97 + endif + endif + + if (i > iimax) then + if (trkrinfo%gridtype == 'global') then + i = iix - iimax + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: i > imax in subroutine barnes for' + print *,'!!! a non-global grid. STOPPING....' + print *,'!!! i= ',i,' imax= ',iimax + print *,' ' + endif + + stop 97 + endif + endif + + icount = icount + 1 + + call calcdist(flon,flat,rlon(i),rlat(j),dist,degrees) + + if (dist .gt. ri) cycle + + if (defined_pt(i,j)) then + if (fxy(i,j) >-999.01 .and. fxy(i,j) <-998.99) then + ! This is a patch. Even though this (i,j) is a valid + ! point, its zeta value has been set to -999 because a + ! neighboring point in subroutine rvcal was found + ! to be out of the grid boundaries. + cycle + endif + wt = exp(-1.0*dist*dist/res) + wts = wts + wt + favg = favg + wt*fxy(i,j) + else + if (ctype == 'vitals') then + continue + else +carw print *,' ' +carw print *,'!!! UNDEFINED PT OUTSIDE OF GRID IN BARNES....' +carw print *,'!!! i= ',i,' j= ',j +carw print *,'!!! flon= ',flon,' flat= ',flat +carw print *,'!!! rlon= ',rlon(i),' rlat= ',rlat(j) +carw print *,'!!! re= ',re,' ri= ',ri +carw print *,'!!! EXITING BARNES....' +carw print *,' ' +carw iret = 95 +carw return + endif + endif + + enddo + enddo + + if (wts > 1.0E-5) then + favg = favg/wts + else + favg = 0.0 + endif + iret = 0 +c + return + end +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine get_ij_bounds (npts,nhalf,ri,imax,jmax,dx,dy + & ,rglatmax,rglatmin,rglonmax,rglonmin,geslon,geslat + & ,trkrinfo,ilonfix,jlatfix,ibeg,jbeg,iend,jend,igiret) +c +c ----------------------------------------------------------- +c ABSTRACT: This subroutine figures out, based on ri, dx and dy and +c the guess latitude and longitude positions, the farthest reaching +c grid points that are searchable by an analysis subroutine. The +c purpose is to return indices for a subgrid that is much smaller +c than the original, full grid. This smaller subgrid can then be +c passed to a subsequent analysis or interpolation subroutine, and +c work can be done on this smaller array. This can help save time, +c especially in the barnes analysis subroutine, as work will only +c be done on, say, a 20 x 20 array (400 pts) instead of on a +c 360 x 181 array (65160 pts). It's crucial, however, to make sure +c that the ibeg, jbeg, iend and jend are extended far enough out to +c fully encompass any search that would be done. Below is a +c diagram showing the different grids.... +c +c Full Global or Regional Model Grid (Grid F) -----------> +c ---------------------------------------------------------------- +c | | (ibeg,jbeg) | +c | | x = ij position that the | (Grid R) | +c | | geslat/geslon is fixed to. ._______________. | +c | | | | | +c | | Even though only the points | (Grid B) | | +c | | within Grid B will be checked | . . . . k | | +c v | later on for a max/min (in the | . . . . . | | +c | case of a subsequent call to | . . x . e | | +c | find_maxmin), the barnes anal- | . . . . . | | +c | ysis will include all pts sur- | . . . . . | | +c | rounding these Grid B points | | | +c | that are within a radius of ri. ._______________. | +c | So in the case of pt. k, that ri | +c | radius may extend all the way to the Grid R | | +c | boundary, thus we need to include those (iend,jend) | +c | points within our ibeg-jbeg-iend-jend bounds. | +c | | +c ---------------------------------------------------------------- +c +c Remember that the grids we deal with start north and increase +c south, so the northernmost latitude on the input grid will have +c a j index of 1. +c +c INPUT: +c npts Num pts from x to edge of max/min search grid (Grid B) +c (i.e., You define the size of Grid B by the value of +c npts that you pass into this subroutine). +c nhalf Number of times the grid spacing will be halved +c ri Radius of influence (for use in barnes analysis) +c imax Number of points in x-direction on original grid +c jmax Number of points in y-direction on original grid +c dx Input grid spacing in i-direction on original grid +c dy Input grid spacing in j-direction on original grid +c rglatmax Value of northern-most latitude on original grid +c rglatmin Value of southern-most latitude on original grid +c rglonmax Value of eastern-most longitude on original grid +c rglonmin Value of western-most longitude on original grid +c geslat Value of latitude of guess position of storm +c geslon Value of longitude of guess position of storm +c +c OUTPUT: +c ilonfix i index on full, input grid that storm is fixed to +c jlatfix j index on full, input grid that storm is fixed to +c ibeg i index for top left of sub-array (Grid R) of input grid +c iend i index for bot right of sub-array (Grid R) of input grid +c jbeg j index for top left of sub-array (Grid R) of input grid +c jend j index for bot right of sub-array (Grid R) of input grid +c igiret Return code from this subroutine +c + USE trig_vals; USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + real tmpangle +c + igiret = 0 +c +c -------------------------------------- +c GET BEGINNING AND ENDING J POINTS.... +c +c (1) Calculate number of searchable, max/min pts, that is, the pts +c from x to the edge of Grid B. +c (2) Calculate number of pts beyond the last search point in Grid +c B, but are within the bounds of Grid R and thus can be +c included in the barnes analysis. +c (3) Add (1) and (2) to get the max number of pts to subtract/add +c to x to get jbeg and jend. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Beginning of get_ij_bounds...' + print *,' geslat= ',geslat,' geslon= ',geslon + print *,' ' + endif + + +c If nhalf > 0: This occurs in the case of a call from fmax, when +c the grid spacing is halved nhalf times. In this case, we have to +c do extra work to figure out the maximum possible grid point. For +c this case: +c jhlatpts = # of grid pts to last possible search pt (from x to +c edge of Grid B in above diagram), plus a cushion. +c jripts = # of searchable grid points within radius ri of last +c possible search pt (num pts between edge of Grid B +c and edge of Grid R in above diagram), plus a cushion +c jbmaxlatpts = # of pts (in j direction) from x to the edge of +c Grid R to include in this subgrid. +c +c If nhalf = 0: In this case, the grid spacing will not be reduced, +c so the number of pts in j direction from x to the edge of Grid +c B will be the input parameter npts, and just multiply it by 2, +c and add 2 for a cushion to get jmaxlatpts. Typically, this sub +c is called from find_maxmin, and in that sub, the first time that +c this sub is called, nhalf will = 0. Then, after a first-shot +c center is found, the grid spacing will be cut in order to rerun +c barnes on a smaller grid, and that's when nhalf will be sent +c here as 3. +c + if (nhalf > 0) then + rdeg = 0.0 + do i = 1,nhalf + rdeg = rdeg + float(npts) * (1./(float(i)*2)) * (dx+dy)/2 + enddo + jhlatpts = ceiling(rdeg/dy) + 1 + jripts = ceiling((ri + 1.)/(dtk*dx)) + 1 + jbmaxlatpts = jhlatpts + jripts + else + jbmaxlatpts = npts * 2 + 2 + endif +c +c +c Roughly fix geslat to the grid point just poleward of geslat. +c + + if ( verb .ge. 3 ) then + print *,' ' + print *,' +++ Near top of get_ij_bounds, ' + print *,' +++ geslat= ',geslat,' geslon= ',geslon + print *,' +++ rglatmax= ',rglatmax,' rglatmin= ',rglatmin + print *,' +++ rglonmax= ',rglonmax,' rglonmin= ',rglonmin + print *,' +++ imax= ',imax,' jmax= ',jmax + print *,' +++ dx= ',dx,' dy= ',dy,' nhalf= ',nhalf + print *,' +++ npts= ',npts + if(nhalf>0) then + print *,' +++ jhlatpts= ',jhlatpts,' jripts= ',jripts + else + print *,' +++ nhalf<=0 so jhlatpts and jripts unused' + endif + print *,' +++ jbmaxlatpts= ',jbmaxlatpts + endif + + if (geslat >= 0.0) then + jlatfix = int((rglatmax - geslat)/dy + 1.) + else + jlatfix = ceiling((rglatmax - geslat)/dy + 1.) + endif + + if ( verb .ge. 3 ) then + print *,' +++ jlatfix= ',jlatfix + endif + + jbeg = jlatfix - jbmaxlatpts + jend = jlatfix + jbmaxlatpts + if (jbeg > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jbeg > jmax' + print *,'!!! jbeg = ',jbeg,' jmax= ',jmax + endif + + igiret = igiret + 1 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, jend < 1, jend = ',jend + endif + + igiret = igiret + 1 + return + endif + if (jbeg < 1) jbeg = 1 + if (jend > jmax) jend = jmax + + if ( verb .ge. 3 ) then + print *,' +++ jbeg= ',jbeg,' jend= ',jend + endif + + ! If using a global grid, avoid using the pole points, or else + ! you'll get a cosfac = 0 and then a divide by zero!!! + + if (jend == jmax .and. rglatmin == -90.0) then + jend = jmax - 2 + endif + if (jbeg == 1 .and. rglatmax == 90.0) then + jbeg = 3 + endif + +c ----------------------------------------- +c NOW GET BEGINNING AND ENDING I POINTS.... +c +c Using the map factor (cos lat), figure out, based on ri, the +c max distance beyond the last search point in x-direction (in +c degrees) that could be searched at this guess latitude (geslat) +c (i.e., in the diagram above, the max num pts from pt. e eastward +c to the edge of Grid R). Calculate how many grid points that is, +c add 2 to it for a cushion, & add the number of points (npts) +c within the defined search grid (Grid B) to get ibmaxlonpts. +c +c April, 2007: A min statement was put on the calculation to +c derive dlon, since with that cosine in there, the values of +c of dlon could get pretty ridiculous as you approach the poles. +c Also, the cosine factor (cosfac) used to be computed at the +c most poleward latitude possible given the jend here. For +c similar concerns with cosines near the poles, I've scrapped +c this to instead compute the cosine factor at the input +c guess latitude. - tpm + + cosfac = cos (geslat * dtr) + tmpangle = cosfac * dtk + dlon = min((ri /tmpangle ),20.0) +c dlon = min((ri / (cosfac * dtk)),20.0) +c + if (nhalf > 0) then + ihlonpts = ceiling(rdeg/dx) + 1 + ibmaxlonpts = ihlonpts + ceiling(dlon/dx) + 2 + else + ibmaxlonpts = npts + ceiling(dlon/dx) + 2 + endif + + if ( verb .ge. 3 ) then + if(nhalf>0) then + print *,' +++ rdeg= ',rdeg,' ri= ',ri,' cosfac= ',cosfac + print *,' +++ dtr= ',dtr,' dtk= ',dtk,' dlon= ',dlon + else + print*,' +++ nhalf<=0 so rdeg,ri,cosfac,dtr,dtk,dlon unused' + endif + print *,' +++ ibmaxlonpts= ',ibmaxlonpts,' dx= ',dx,' dy= ',dy + endif + +c Roughly fix geslon to the grid point just EASTward of geslon. + + ilonfix = int((geslon - rglonmin)/dx + 2.) + + ibeg = ilonfix - ibmaxlonpts + iend = ilonfix + ibmaxlonpts + + if ( verb .ge. 3 ) then + print *,' +++ (orig) ilonfix= ',ilonfix + print *,' +++ (orig) ibeg= ',ibeg,' iend= ',iend + print *,' +++ ' + endif + + if (ibeg > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 1 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in get_ij_bounds, ibeg > imax' + print *,'!!! for a non-global grid' + print *,'!!! ibeg = ',ibeg,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend > imax' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + ! For a regional grid, just set iend to be imax + iend = imax + endif + endif + if (ibeg < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, ibeg < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. ibeg = ',ibeg,' imax= ',imax + endif + + else + ! For a regional grid, just set ibeg to be 1 + ibeg = 1 + endif + endif + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + + if ( verb .ge. 3 ) then + print *,'+++ NOTE: in get_ij_bounds, iend < 1' + print *,'+++ for a global grid; GM wrapping expected from' + print *,'+++ calling routine. iend = ',iend,' imax= ',imax + endif + + else + + if ( verb .ge. 3 ) then + print *,'!!! ERROR in get_ij_bounds, iend < 1' + print *,'!!! for a non-global grid' + print *,'!!! iend = ',iend,' imax= ',imax + endif + + igiret = igiret + 1 + return + endif + endif +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine check_bounds (guesslon,guesslat,ist,ifh,trkrinfo + & ,icbret) +c +c ABSTRACT: This subroutine checks to make sure that the requested +c storm is in fact within the model's grid boundaries; +c this is only a concern for the regional models. +c + USE def_vitals; USE grid_bounds; USE set_max_parms + USE trkrparms + USE verbose_output + + type (trackstuff) trkrinfo + + if (trkrinfo%gridtype == 'regional') then + if (guesslon > glonmax .or. guesslon < glonmin .or. + & guesslat > glatmax .or. guesslat < glatmin) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is outside of grid' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + goto 125 + else + icbret = 0 + endif + endif + + ! We have encountered problems with global grids where we + ! continue tracking almost the whole way to the pole. While + ! that's nice to do that, it creates problems for array + ! indices, especially in subroutine getradii. So we will cut + ! our losses and eliminate tracking of storms within + ! 5 degrees of the pole for global grids. + + if ((trkrinfo%type == 'midlat' .or. + & trkrinfo%type == 'tcgen') .and. + & trkrinfo%gridtype == 'global')then + if (guesslat > 85.0 .or. guesslat < -85.0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! IN check_bounds, Storm is too close to the' + print *,'!!! N or S Pole for global tracking.' + print *,'!!! STOPPING TRACKING FOR THIS STORM DUE TO POLE' + print *,'!!! Storm ID = ',storm(ist)%tcv_storm_id + print *,'!!! Storm Name = ',storm(ist)%tcv_storm_name + print *,'!!! ist= ',ist,' ifh= ',ifh + print *,'!!! guess storm lon= ',guesslon + print *,'!!! guess storm lat= ',guesslat + endif + + icbret = 95 + else + icbret = 0 + endif + endif + + 125 continue +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine calcdist(rlonb,rlatb,rlonc,rlatc,xdist,degrees) +c +c ABSTRACT: This subroutine computes the distance between two +c lat/lon points by using spherical coordinates to +c calculate the great circle distance between the points. +c Figure out the angle (a) between pt.B and pt.C, +c N. Pole then figure out how much of a % of a great +c x circle distance that angle represents. +c / \ +c b/ \ cos(a) = (cos b)(cos c) + (sin b)(sin c)(cos A) +c / \ +c pt./<--A-->\c NOTE: The latitude arguments passed to the +c B / \ subr are the actual lat vals, but in +c \ the calculation we use 90-lat. +c a \ +c \pt. NOTE: You may get strange results if you: +c C (1) use positive values for SH lats AND +c you try computing distances across the +c equator, or (2) use lon values of 0 to +c -180 for WH lons AND you try computing +c distances across the 180E meridian. +c +c NOTE: In the diagram above, (a) is the angle between pt. B and +c pt. C (with pt. x as the vertex), and (A) is the difference in +c longitude (in degrees, absolute value) between pt. B and pt. C. +c +c !!! NOTE !!! -- THE PARAMETER ecircum IS DEFINED (AS OF THE +c ORIGINAL WRITING OF THIS SYSTEM) IN KM, NOT M, SO BE AWARE THAT +c THE DISTANCE RETURNED FROM THIS SUBROUTINE IS ALSO IN KM. +c + USE trig_vals + + real degrees +c + if (rlatb < 0.0 .or. rlatc < 0.0) then + pole = -90. + else + pole = 90. + endif +c + distlatb = (pole - rlatb) * dtr + distlatc = (pole - rlatc) * dtr + difflon = abs( (rlonb - rlonc)*dtr ) +c + cosanga = ( cos(distlatb) * cos(distlatc) + + & sin(distlatb) * sin(distlatc) * cos(difflon)) + +c This next check of cosanga is needed since I have had ACOS crash +c when calculating the distance between 2 identical points (should +c = 0), but the input for ACOS was just slightly over 1 +c (e.g., 1.00000000007), due to (I'm guessing) rounding errors. + + if (cosanga > 1.0) then + cosanga = 1.0 + endif +cPENG added bug fixed on 04/28/2016-------------------------- + if (cosanga < -1.0) then + cosanga = -1.0 + endif +cPENG added on 04/28/2016-------------------------- + + degrees = acos(cosanga) / dtr + circ_fract = degrees / 360. + xdist = circ_fract * ecircum +c +c NOTE: whether this subroutine returns the value of the distance +c in km or m depends on the scale of the parameter ecircum. +c At the original writing of this subroutine (7/97), ecircum +c was given in km. +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine subtract_cor (imax,jmax,dy,level) +c +c ABSTRACT: This subroutine subtracts out the coriolis parameter +c from the vorticity values. It is needed because at the original +c writing of this system, all of the forecast centers who included +c vorticity included only absolute vorticity. +c + USE tracked_parms; USE trig_vals; USE grid_bounds + + implicit none + + integer :: i,j,imax,jmax,level + real :: dy,coriolis,rlat +c + do j=1,jmax + rlat = glatmax - ((j-1) * dy) + coriolis = 2. * omega * sin(rlat*dtr) + do i=1,imax + zeta(i,j,level) = zeta(i,j,level) - coriolis + enddo + enddo +c + return + end +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine get_grib_file_name (ifh,gfilename,ifilename) + +c ABSTRACT: This subroutine uses various input regarding the model +c and forecast hour and generates the name of the input grib file +c for this particular forecast hour. Remember that the lead time +c is in minutes and that 5 spaces must be reserved for the lead +c time (e.g., f00360). File name should be something that looks +c like either, e.g., "gfdl.6thdeg.katrina12l.2005082818.f00720", +c or "gfdl.6thdeg.2005082818.f00720" (the part in there with the +c storm name & ID is optional). The grib index file name should +c be exactly the same as the grib data file itself, but with the +c character string ".ix" added onto the end of the name. +c +c NOTE: Array iftotalmins is brought in via module tracked_parms. +c +C INPUT: +c ifh integer array index for current lead time +c +c OUTPUT: +c gfilename GRIB file name +c ifilename GRIB index file name + + USE gfilename_info; USE tracked_parms; USE atcf + USE verbose_output + + implicit none + + character(*) gfilename,ifilename + character cfmin*5,cymdh*10 + integer ifh,nlen1,nlen2,nlen3,nlen4,nlen5 + +c Convert integer minutes to 5-position character, with +c leading zeroes, and convert 10-digit integer date into +c 10-position character. Then trim the various input variables +c and combine all into the file name. + + write (cfmin,'(i5.5)') iftotalmins(ifh) + write (cymdh,'(i10.10)') atcfymdh + + nlen1 = len_trim(gmodname) + gfilename = trim(gmodname(1:nlen1)) + + nlen2 = len_trim(rundescr) + + gfilename = trim(gfilename(1:nlen1))//'.'//trim(rundescr(1:nlen2)) + + nlen3 = len_trim(atcfdescr) + nlen4 = len_trim(gfilename) + +c If an extension to the name with the ATCF or storm name descriptor +c was included, then add it to the name now. Otherwise, just add +c the starting date and the lead time in minutes. + + if (nlen3 > 0) then + gfilename = trim(gfilename(1:nlen4))//'.' + & //trim(atcfdescr(1:nlen3))//'.'//cymdh//'.f'//cfmin + else + gfilename = trim(gfilename(1:nlen4))//'.'//cymdh//'.f'//cfmin + endif + +c Create the name for the grib index file, which is just the name of +c the grib file, with "ix" added to the end of it. + + nlen5 = len_trim(gfilename) + ifilename = trim(gfilename(1:nlen5))//'.ix' + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,72) 'gfilename',gfilename + write (6,72) 'ifilename',ifilename + endif + + 72 format (1x,'In get_grib_file_name, file name for ',a9 + & ,' is ',a120) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_grib (readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons,inp + & ,lugb,lugi,trkrinfo) +c +c ABSTRACT: This subroutine reads the input GRIB file for the +c tracked parameters. It then calls subroutines to convert the +c data from a 1-d array into a 2-d array if the read was successful. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature (I jerry-rigged this by storing +c the data as being at the 401 mb level.) +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +c +c INPUT: +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c inp of a derived type, contains user-input info +c lugb integer unit number of input grib file +c lugi integer unit number of input grib index file +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE verbose_output; USE params; USE grib_mod; USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + type (datecard) inp + type (gribfield) :: gfld,prevfld,holdgfld +c + integer, parameter :: jf=40000000 + integer, parameter :: nreadparms=17 + real, allocatable :: f(:) + real :: dmin,dmax,firstval,lastval + logical(1), allocatable :: lb(:) + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1) file_open + logical :: unpack=.true. + logical :: open_grb=.false. + character*1 :: lbrdflag + character*8 :: chparm(nreadparms) + CHARACTER(len=8) :: pabbrev + character (len=10) big_ben(3) + integer date_time(8) + integer,dimension(200) :: jids,jpdt,jgdt + integer :: listsec1(13), enable_timing + integer, intent(in) :: imax,jmax + integer igparm(nreadparms),iglev(nreadparms) + integer iglevtyp(nreadparms) + integer ig2_parm_cat(nreadparms),ig2_parm_num(nreadparms) + integer ig2_lev_val(nreadparms),ig2_lev_typ(nreadparms) +cPENG 04/18/2018 for CMC Det. and CMC ensemble data + integer ig2_lev_11_cmc(nreadparms),ig2_lev_val_cmc(nreadparms) + integer ig2_lev_11_cmcd(nreadparms),ig2_lev_val_cmcd(nreadparms) + + integer cpsig2_parm_cat(nlevs_cps),cpsig2_parm_num(nlevs_cps) +c integer cpsig2_lev_typ(nlevs_cps),cpsig2_lev_val(nlevs_cps) +cPENG------- + integer cpsig2_lev_10(nlevs_cps) + integer cpsig2_lev_11(nlevs_cps),cpsig2_lev_12(nlevs_cps) + + integer ec_igparm(nreadparms),ec_iglev(nreadparms) + integer ec_iglevtyp(nreadparms) + integer cpsgparm(nlevs_cps),cpsglev(nlevs_cps) + integer cpsglevtyp(nlevs_cps) + integer ec_cpsgparm(nlevs_cps) + integer jpds(200),jgds(200),kpds(200),kgds(200) + integer igvret,ifa,ila,ip,ifh,i,j,k,kj,iret,kf,lugb,lugi + integer jskp,jdisc,np + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer pdt_4p0_vert_level,pdt_4p0_vtime + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 +c + lbrdflag = 'n' + enable_timing=trkrinfo%enable_timing +c The following data statements contain the parameters that will be +c used to search the grib files. The first 9 parameters will all be +c used to locate the storm position. The last 4 parameters (500 mb +c u- and v-components and 10 m u- and v- components) will not be +c used for tracking, but only for helping to estimate the next first +c guess position (500 mb winds) and for estimating the max near- +c surface wind speeds in the vicinity of the storm (10 m winds). +c +c ** NOTE: iglevtyp(12 & 13) and iglev(12 & 13) are initialized to +c 0 just to satisfy the IBM xlf compiler, which barks about +c there being too few initial values in the list when I +c only had 11 values there -- even though the real +c initialization for these variables is done just about +c 10 lines below. +c +c ** NOTE: The new ECMWF hi-res data uses the ECMWF GRIB parameter +c ID table, which has different values than the NCEP +c table. Therefore, we needed to add the variables and +c data values for ec_igparm, ec_iglevtyp and ec_iglev. +c +c July 2007: Read statements added for GP height for cyclone +c phase space (CPS) algorithm. + +c data igparm /41,41,33,34,33,34,7,7,1,33,34,33,34,11,7,7,81/ + data igparm /41,41,33,34,33,34,7,7,2,33,34,33,34,11,7,7,81/ + data iglevtyp /100,100,100,100,100,100,100,100,102,0,0,100,100 + & ,100,100,100,1/ + data iglev /850,700,850,850,700,700,850,700,0,0,0,500,500,401 + & ,500,200,0/ + + data cpsgparm /13*7/ + data ec_cpsgparm /13*156/ + data cpsglevtyp /13*100/ + data cpsglev /900,850,800,750,700,650,600,550,500,450,400 + & ,350,300/ + + data ec_igparm /999,999,131,132,131,132,156,156,151,165,166 + & ,131,132,130,156,156,999/ + data ec_iglevtyp /100,100,100,100,100,100,100,100,1,0,0,100,100 + & ,100,100,100,999/ + data ec_iglev /850,700,850,850,700,700,850,700,0,0,0,500,500 + & ,401,500,200,999/ + + data chparm /'absv','absv','ugrid','vgrid','ugrid','vgrid' + & ,'gphgt','gphgt','mslp','ugrid','vgrid','ugrid' + & ,'vgrid','temp','gphgt','gphgt','lmask'/ + + data ig2_parm_cat /2,2,2,2,2,2,3,3,3,2,2,2,2,0,3,3,2/ + data ig2_parm_num /10,10,2,3,2,3,5,5,1,2,3,2,3,0,5,5,8/ + data ig2_lev_typ /100,100,100,100,100,100,100,100,101,103,103 + & ,100,100,100,100,100,-9999/ + data ig2_lev_val /850,700,850,850,700,700,850,700,0,10,10,500,500 + & ,401,500,200,-9999/ +cPENG 04/18/2018 for CMC Det. and CMC ensemble data + data ig2_lev_11_cmc /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0/ + data ig2_lev_val_cmc/85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999/ + + data ig2_lev_11_cmcd /-3,-4,-3,-3,-4,-4,-3,-4,0,0,0,-4,-4,0 + & ,-4,-4,0/ + data ig2_lev_val_cmcd /85,7,85,85,7,7,85,7,0,10,10,5,5,40100 + & ,5,2,-9999/ + + data cpsig2_parm_cat /13*3/ + data cpsig2_parm_num /13*5/ +c data cpsig2_lev_typ /13*100/ +c data cpsig2_lev_val /900,850,800,750,700,650,600,550,500,450,400 +c & ,350,300/ +cPENG---------------------------------- + data cpsig2_lev_10 /13*100/ + data cpsig2_lev_11 /13*0/ + data cpsig2_lev_12 /90000,85000,80000,75000,70000,65000 + & ,60000,55000,50000,45000,40000 + & ,35000,30000/ + +c Model numbers used: (1) AVN, (2) MRF, (3) UKMET, (4) ECMWF, +c (5) NGM, (6) Early Eta, (7) NAVGEM, (8) GDAS, +c (10) NCEP Ensemble, (11) ECMWF Ensemble, +c (13) SREF Ensemble, +c (14) NCEP Ensemble (from ensstat mean fields), +c (15) CMC, (16) CMC Ensemble, (17) HWRF, +c (18) HWRF Ensemble, (19) HWRF-DAS (HDAS), +c (20) NCEP Ensemble RELOCATION +c (21) UKMET hi-res (from NHC) +c (23) FNMOC Ensemble +c (24) HWRF Basin-scale + + if (trkrinfo%gribver == 2) then + +c For GRIB2, we will check to see if the MSLP being searched for +c is the standard MSLP (MSLP parm ID = 1) or if it is the +c so-called "Eta" or "Membrane" MSLP reduction that is included +c in the output for some models (like GFS and GDAS). Note that +c for 10m winds, with GRIB2, so far with all of the GRIB2 model +c data we've seen to this point, they all have the same IDs for +c 10m winds for all models, so no need to break out by model +c like we do for GRIB v1 in the else portion of this if statement. + + ig2_parm_num(9) = trkrinfo%g2_mslp_parm_id ! 1 = standard MSLP + ! reduction, 192 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB2 read, MSLP ID = ig2_parm_num(9) = ' + & ,ig2_parm_num(9) + + endif + + else + +c For GRIB1, do the same check as done just above in the IF part +c of this IF statement, but note that we need to also check to +c see what the GRIB1 parm IDs are for the sfc wind level type +c and value. Most models list the level type as 105 (which means +c height above the ground) and then a level value of 10. But +c ECMWF and UKMET use a level type of 1 (which means ground or +c water surface) and a level value of 0. + + igparm(9) = trkrinfo%g1_mslp_parm_id ! 102 = standard MSLP + ! reduction, 130 = "Eta" or "Membrane" + ! reduction used in GFS, GDAS and others. + + iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! 105 for most + iglev(10) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + iglev(11) = trkrinfo%g1_sfcwind_lev_val ! 10 for most + + ec_iglevtyp(10) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglevtyp(11) = trkrinfo%g1_sfcwind_lev_typ ! = 1 for ECMWF + ec_iglev(10) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + ec_iglev(11) = trkrinfo%g1_sfcwind_lev_val ! = 0 for ECMWF + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Before GRIB1 read, MSLP ID = igparm(9) = ' + & ,igparm(9) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev type = ' + & ,iglevtyp(10) + print *,'Before GRIB1 read, non-ECMWF sfcwind lev value = ' + & ,iglev(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev type = ' + & ,ec_iglevtyp(10) + print *,'Before GRIB1 read, ECMWF sfcwind lev value = ' + & ,ec_iglev(10) + endif + + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata. A return' + print *,'code (iret) not equal to zero indicates that ' + print *,'subroutine getgb was unable to find the requested ' + print *,'parameter. This could be simply because the parm is ' + print *,'not included in the grib file (this is likely for ' + print *,'ECMWF data, as they limit what they send us), or it ' + print *,'could indicate a problem with the grib index file.' + endif + + + if (allocated(f)) deallocate(f) + if (allocated(lb)) deallocate(lb) + allocate (f(imax*jmax),stat=ifa) + allocate (lb(imax*jmax),stat=ila) + if (ifa /= 0 .or. ila /= 0) then + print *,' ' + print *,'!!! ERROR in getdata allocating f or lb array.' + print *,'!!! ifa = ',ifa,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + if (trkrinfo%gribver == 2) then + + ! Reading from a GRIB v2 file.... + + grib2_standard_parm_read_loop: do ip = 1,nreadparms + + if (ip == 17) then + if (trkrinfo%use_land_mask == 'y' .or. + & trkrinfo%use_land_mask == 'Y') then + continue + else + if (verb .ge. 3) then + print *,' ' + print *,'The use_land_mask flag has not been set to ' + print *,'y or Y, so we will not try to read it in... ' + print *,' ' + cycle grib2_standard_parm_read_loop + endif + endif + endif + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + if (ip == 17) then + jdisc=1 ! hydrological products. At this point, used only + ! for the land-sea mask. + else + jdisc=0 ! meteorological products + endif + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for input parameter by production template 4.0. This +c tave program is used primarily for temperature, but still we +c will leave that as a variable and not-hard wire it in case we +c choose to average something else in the future. + + ! We are looking for Temperature or GP Height here. This + ! block of code, or even the smaller subset block of code that + ! contains the JPDT(1) and JPDT(2) assignments, can of course + ! be modified if this program is to be used for interpolating + ! other variables.... + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = ig2_parm_cat(ip) + JPDT(2) = ig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + + JPDT(10) = ig2_lev_typ(ip) +cPENG 04/18/2018 CMC Det. and CMC ensemble data + if (inp%model == 16 ) then + JPDT(11) = ig2_lev_11_cmc(ip) + JPDT(12) = ig2_lev_val_cmc(ip) + elseif (inp%model == 15 ) then + JPDT(11) = ig2_lev_11_cmcd(ip) + JPDT(12) = ig2_lev_val_cmcd(ip) + else + JPDT(11) = 0 + if (JPDT(10) == 100) then ! isobaric surface + JPDT(12) = ig2_lev_val(ip) * 100 ! GRIB2 levels are in Pa + else + JPDT(12) = ig2_lev_val(ip) ! This is going to be either mslp + & ! or 10m winds. + endif + endif + + if ( verb_g2 .ge. 1 ) then + print *,'before getgb2 call, value of unpack = ',unpack + endif + + inquire (unit=lugb, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugb= ',lugb + & ,' is CLOSED' + endif + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is OPEN' + endif + else + if (verb .ge. 3) then + print *,'TEST b4 getgb2 getdata, unit lugi= ',lugi + & ,' is CLOSED' + endif + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,531) date_time(5),date_time(6),date_time(7) + 531 format (1x,'TIMING: before getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,532) date_time(5),date_time(6),date_time(7) + 532 format (1x,'TIMING: after getgb2-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call, value of unpacked = ' + & ,gfld%unpacked + print *,'after getgb2 call, gfld%ngrdpts = ',gfld%ngrdpts + print *,'after getgb2 call, gfld%ibmap = ',gfld%ibmap + endif + + if ( iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb2 found parm: ',chparm(ip) + print *,'+++ at level = ',ig2_lev_val(ip) + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + select case (chparm(ip)) + case ('absv') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpdt(12) == 85000 .or. jpdt(12) == 85) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpdt(12) == 70000 .or. jpdt(12) == 7) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpdt(12) == 50000 .or. jpdt(12) == 5) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpdt(12) == 20000 .or. jpdt(12) == 2) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb2 could not find parm: ' + & ,chparm(ip) + print *,'!!! at level = ',ig2_lev_val(ip) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif +c J.Peng---07/26/2019 to free the memory in reading GRIB2 data + call gf_free (gfld) + + enddo grib2_standard_parm_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c This is the GRIB2 reading section. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + grib2_cps_parm_lev_loop: do ip = 1,nlevs_cps + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; + ! 1 = ens fcst + jgdtn=0 + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + + jpds = -1 + jgds = -1 + j=0 + + ! Set defaults for JPDT, then override in array + ! assignments below... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + JPDT(1) = cpsig2_parm_cat(ip) + JPDT(2) = cpsig2_parm_num(ip) + + if (inp%lt_units == 'minutes') then + JPDT(8) = 0 + JPDT(9) = iftotalmins(ifh) + else + JPDT(8) = 1 + JPDT(9) = ifhours(ifh) + endif + +c JPDT(10) = cpsig2_lev_typ(ip) + JPDT(10) = cpsig2_lev_10(ip) +cPENG-------------------------------------------- + if (inp%model == 1 .or. inp%model == 10 .or. inp%model == 7 + & .or. inp%model == 22 .or. inp%model == 16 + & .or. inp%model == 15 .or. inp%model == 8) then + JPDT(11) = cpsig2_lev_11(ip) + JPDT(12) = cpsig2_lev_12(ip) + endif + +c if (JPDT(10) == 100) then ! isobaric surface +c JPDT(12) = cpsig2_lev_val(ip) * 100 ! GRIB2 levels +c ! are in Pa +c else +c if (verb .ge. 3) then +c print *,' ' +c print *,'ERROR in getdata: JPDT(10) array value' +c print *,'should only be 100 in this CPS section' +c print *,'for GRIB2 data.' +c endif +c endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,731) date_time(5),date_time(6),date_time(7) + 731 format (1x,'TIMING: before getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn + & ,jgdt,unpack,krec,gfld,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,732) date_time(5),date_time(6),date_time(7) + 732 format (1x,'TIMING: after getgb2-2',i2.2,':',i2.2,':' + & ,i2.2) + endif + + if ( verb .ge. 3 ) then + print *,'iret from getgb2 (PHASE) in getdata = ',iret + endif + + if (verb_g2 .ge. 1) then + print *,'after getgb2 call(PHASE),' + & ,' value of unpacked = ',gfld%unpacked + print *,'after getgb2 (PHASE) call, gfld%ngrdpts = ' + & ,gfld%ngrdpts + print *,'after getgb2 (PHASE) call, gfld%ibmap = ' + & ,gfld%ibmap + endif + + if (verb .ge. 3) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb2 (PHASE) call, j= ',j + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + +c Determine packing information from GRIB2 file. +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if (verb_g2 .ge. 1) then + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial + & ! packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) + & then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ' + & ,gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ' + & ,gfld%igdtlen + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + kf = gfld%ngrdpts ! Number of gridpoints returned + ! from read + + do np = 1,kf + f(np) = gfld%fld(np) + if (gfld%ibmap == 0) then + lb(np) = gfld%bmap(np) + else + lb(np) = .true. + endif + enddo + + call bitmapchk(kf,lb,f,dmin,dmax) + +c Convert logical bitmap to 2-d array (only need to do +c this once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb_g2 .ge. 1) then + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) + & then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.' + & ,gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ' + & ,gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ' + & ,gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.' + & ,gfld%ipdtnum + & ,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + endif + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline + & ,gfld%ipdtmpl(1),gfld%ipdtmpl(2)) + + if (verb .ge. 3) then + print *,' ' + write (6,231) + 231 format (' rec# param level byy bmm bdd ' + & ,'bhh ' + & ,'fhr npts firstval lastval minval ' + & ,' maxval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval,dmin + & ,dmax +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + +c J.Peng---10/07/2019 to free the memory in reading GRIB2 data + call gf_free (gfld) + + enddo grib2_cps_parm_lev_loop + + endif + + endif + + else + + !---------------------------------- + ! Reading from a GRIB v1 file.... + !---------------------------------- + + grib1_read_loop: do ip = 1,nreadparms + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then ! ECMWF hi-res data uses ECMWF table + print *,' ' + print *,'WARNING: From the namelist, inp%model is set to a' + print *,' value of 4, which is for ECMWF, so in routine' + print *,' getdata_grib, the input jpds(5,6,7) parms are' + print *,' going to have values that are specific for' + print *,' ECMWF GRIB1 data.' + print *,' ' + jpds(5) = ec_igparm(ip) + jpds(6) = ec_iglevtyp(ip) + jpds(7) = ec_iglev(ip) + else ! All other models use NCEP-standard GRIB table + jpds(5) = igparm(ip) + jpds(6) = iglevtyp(ip) + jpds(7) = iglev(ip) + endif + + print *,' ' + print *,' --- Before getgb, jpds(5)= ',jpds(5) + print *,' --- , jpds(6)= ',jpds(6) + print *,' --- , jpds(7)= ',jpds(7) + + if (jpds(5) == 999) then + cycle + endif + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,831) date_time(5),date_time(6),date_time(7) + 831 format (1x,'TIMING: before getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + + if (enable_timing /= 0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,832) date_time(5),date_time(6),date_time(7) + 832 format (1x,'TIMING: after getgb-1',i2.2,':',i2.2,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb call, j= ',j,' k= ',k + & ,' iftotalmins= ' + & ,iftotalmins(ifh),' parm # (ip) = ',ip,' iret= ',iret + else + print *,'After getgb call, j= ',j,' k= ',k,' ifhours= ' + & ,ifhours(ifh),' parm # (ip) = ',ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + if ( verb .ge. 3 ) then + print *,'+++ Good Read: getgb found parm: ',chparm(ip) + print *,'+++ at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'+++ Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'+++ Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + readflag(ip) = .TRUE. + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,29) + else + write (6,31) + endif + 29 format (' rec# parm# levt lev byy bmm bdd bhh fmin' + & ,' npts minval maxval') + 31 format (' rec# parm# levt lev byy bmm bdd bhh fhr ' + & ,' npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert logical bitmap to 2-d array (only need to do this +c once since using same model for all variables). + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic (imax,jmax,lb,valid_pt + & ,need_to_flip_lats) + lbrdflag = 'y' + endif + + + select case (chparm(ip)) + case ('absv') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + endif + case ('ugrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + endif + case ('vgrid') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else + ! Near-surface data + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + endif + case ('gphgt') + if (jpds(7) == 850) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (jpds(7) == 700) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (jpds(7) == 500) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (jpds(7) == 200) then + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + endif + case ('mslp') + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + case ('temp') + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + case ('lmask') + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + case default + + if ( verb .ge. 1 ) then + print *,'!!! ERROR: BAD CHPARM IN GETDATA = ',chparm(ip) + endif + + end select + + else + + if ( verb .ge. 3 ) then + print *,'!!! NOTE: getgb could not find parm: ',chparm(ip) + print *,'!!! at level = ',jpds(7) + if (inp%lt_units == 'minutes') then + print *,'!!! Forecast time = ',iftotalmins(ifh) + & ,' minutes' + else + print *,'!!! Forecast time = ',ifhours(ifh) + & ,' hours' + endif + endif + + endif + + enddo grib1_read_loop + +c *------------------------------------------------------------* +c If we are attempting to determine the cyclone structure, +c then read in data now that will allow us to do that. +c *------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + ! Read in GP Height levels for cyclone phase space... + + cps_grib1_lev_loop: do ip = 1,nlevs_cps + + jpds = -1 + jgds = -1 + j=0 + + if (inp%model == 4) then + ! Use different grib parm id for ECMWF GP height + jpds(5) = ec_cpsgparm(ip) + else + jpds(5) = cpsgparm(ip) + endif + jpds(6) = cpsglevtyp(ip) + jpds(7) = cpsglev(ip) + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,841) date_time(5),date_time(6),date_time(7) + 841 format (1x,'TIMING: before getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + call getgb (lugb,lugi,jf,j,jpds,jgds, + & kf,k,kpds,kgds,lb,f,iret) + if(enable_timing/=0) then + call date_and_time (big_ben(1),big_ben(2),big_ben(3) + & ,date_time) + write (6,842) date_time(5),date_time(6),date_time(7) + 842 format (1x,'TIMING: after getgb-2',i2.2,':',i2.2 + & ,':',i2.2) + endif + + if ( verb .ge. 3 ) then + print *,' ' + if (inp%lt_units == 'minutes') then + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifmins= ',iftotalmins(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + else + print *,'After getgb (PHASE) call, j= ',j,' k= ',k + & ,' ifhours= ',ifhours(ifh),' parm # (ip) = ' + & ,ip,' iret= ',iret + endif + endif + + if (iret == 0) then + + call bitmapchk(kf,lb,f,dmin,dmax) + + if ( verb .ge. 3 ) then + if (inp%lt_units == 'minutes') then + write (6,39) + else + write (6,41) + endif + 39 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fmin npts minval maxval') + 41 format (' rec# parm# levt lev byy bmm bdd bhh ' + & ,'fhr npts minval maxval') + print '(i4,2x,8i5,i8,2g12.4)', + & k,(kpds(i),i=5,11),kpds(14),kf,dmin,dmax + endif + +c Convert data to 2-d array + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo cps_grib1_lev_loop + + endif + + endif + + endif +c + deallocate (f) + deallocate (lb) +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine getdata_netcdf (ncfile_id,readflag,valid_pt,imax,jmax + & ,ifh,need_to_flip_lats,need_to_flip_lons + & ,ncfile_tmax,netcdfinfo) +c +c ABSTRACT: This subroutine reads the input NetCDF file for the +c tracked parameters for one lead time. +c +c There are up to 9 fields that are read in that will be used to +c locate the storm position. There are an additional 4 variables +c (500 mb u- and v-components and 10 m u- and v- components) that +c will not be used for tracking, but only for helping to estimate +c the next first guess position (500 mb winds) and for estimating +c the max near-surface wind speeds in the vicinity of the storm +c (10 m winds). +c +c Fields read in are listed here. Numbers indicate positioning in +c the readflag logical array: +c +c 1. 850 mb absolute vorticity +c 2. 700 mb absolute vorticity +c 3. 850 mb u-component +c 4. 850 mb v-component +c 5. 700 mb u-component +c 6. 700 mb v-component +c 7. 850 mb geopotential height +c 8. 700 mb geopotential height +c 9. MSLP +c 10. 10-m u-component +c 11. 10-m v-component +c 12. 500 mb u-component +c 13. 500 mb v-component +c 14. 300-500 mb mean temperature +c 15. 500 mb geopotential height +c 16. 200 mb geopotential height +c 17. Land-Sea mask -- This is for tcgen applications only, and +c even there, it's optional. +c +c If the user has requested to check the cyclone phase space for +c this run (phaseflag set to 'y' and phasescheme set to 'cps'), +c then we need to have gp height data for 900-300 mb at every 50 +c mb. Some of those levels for gp height data were already read +c in with the read of the initial 17 parameters, but we will be +c sure to read in the others, if requested. +c +c INPUT: +c ncfile_id integer ID associated with the NetCDF file +c imax integer number of pts in i-direction on grid +c jmax integer number of pts in j-direction on grid +c ifh integer index for forecast hour +c need_to_flip_lats logical flag read in from getgridinfo that +c indicates if data needs flipped north to south +c need_to_flip_lons logical flag read in from getgridinfo that +c indicates if data needs flipped east to west +c ncfile_tmax integer with max number of time levels in the input +c NetCDF file, as read in from the NetCDF file +c itself in subroutine read_netcdf_fhours. +c +c OUTPUT: +c readflag logical array, indicates if a parm was read in +c valid_pt logical array, indicates for each (i,j) if there is +c valid data at the point (used for regional grids) + + USE tracked_parms; USE level_parms; USE inparms; USE phase + USE netcdf_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo + integer, parameter :: nreadparms=17,nreadparms_cps=13 + real, allocatable :: f(:) + real :: dmin,dmax,xmissing_value,xfill_value + logical(1) valid_pt(imax,jmax),readflag(nreadparms) + logical(1) :: need_to_flip_lats,need_to_flip_lons + character*1 :: lbrdflag,match_check + character*30 :: chparm(nreadparms) + character*30 :: chparm_cps(nreadparms_cps) + integer, intent(in) :: ncfile_id,imax,jmax + integer :: igvret,ifa,ip,ifh,i,j,k,m,n,ncfile_tmax,nf_get_att_real + integer :: nf_get_att_double,nf_inq_attlen,imvlen,ifvlen + integer :: usertime,ncix,missing_val_length,nf_status + integer :: nf_inq_varid,varid +c + lbrdflag = 'n' + +cnc data cpsgparm /13*7/ +cnc data cpsglevtyp /13*100/ +cnc data cpsglev /900,850,800,750,700,650,600,550,500,450,400 +cnc & ,350,300/ + +c data chparm /'vort850','vort700' +c & ,'u850','v850','u700','v700' +c & ,'h850','h700','slp','u_ref','v_ref' +c & ,'u500','v500','tm'/ + +c Load the names of the NetCDF variables for the standard +c variables into the chparm array... + + chparm(1) = netcdfinfo%rv850name + chparm(2) = netcdfinfo%rv700name + chparm(3) = netcdfinfo%u850name + chparm(4) = netcdfinfo%v850name + chparm(5) = netcdfinfo%u700name + chparm(6) = netcdfinfo%v700name + chparm(7) = netcdfinfo%z850name + chparm(8) = netcdfinfo%z700name + chparm(9) = netcdfinfo%mslpname + chparm(10) = netcdfinfo%usfcname + chparm(11) = netcdfinfo%vsfcname + chparm(12) = netcdfinfo%u500name + chparm(13) = netcdfinfo%v500name + chparm(14) = netcdfinfo%tmean_300_500_name + chparm(15) = netcdfinfo%z500name + chparm(16) = netcdfinfo%z200name + chparm(17) = netcdfinfo%lmaskname + + + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: Program is now in subroutine getdata_netcdf.' + endif + + if (allocated(f)) deallocate(f) + allocate (f(imax*jmax),stat=ifa) + if (ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getdata_netcdf allocating f data array.' + print *,'!!! ifa = ',ifa + print *,'!!! STOPPING EXECUTION' + STOP 91 + endif + + !--------------------------------------------------------------- + ! First go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match up + ! the lead times that were read in with the lead times that + ! we read in directly from the NetCDF file. Get the index from + ! the NetCDF file for that lead time and use that in the call to + ! the read routine (get_var3_tlev_double). + !--------------------------------------------------------------- + + usertime = iftotalmins(ifh) + + match_check = 'n' + + find_index_loop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + ncix = m + if (verb .ge. 1) then + print *,'+++ Time match in getdata_netcdf for usertime= ' + & ,usertime,' netcdf file index= ncix= ',ncix + endif + match_check = 'y' + exit find_index_loop + endif + + enddo find_index_loop + + if (match_check == 'n') then + print *,' ' + print *,'!!! ERROR in getdata_netcdf: ' + print *,' For a NetCDF file, the user has ' + print *,' requested to process a lead time, and that lead' + print *,' time does not exist in the NetCDF list of time' + print *,' values. ' + print *,' ifh= ',ifh + print *,' usertime= iftotalmins(ifh)= ',iftotalmins(ifh) + print *,' STOPPING....' + stop 99 + endif + + !--------------------------------------------------------------- + ! Now go through the read loop for the list of parameters + !--------------------------------------------------------------- + + netcdf_standard_parm_read_loop: do ip = 1,nreadparms + + if (chparm(ip) == 'X' .or. chparm(ip) == 'x') then + if (verb .ge. 3) then + print *,' ' + print *,'!!! NetCDF read NOT requested for parm # ',ip + endif + cycle netcdf_standard_parm_read_loop + else + if (verb .ge. 3) then + print *,' ' + print *,'+++ NetCDF read requested for parm # ',ip + & ,' ... parm= ',chparm(ip) + endif + endif + + ! Note that I am sending a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which we + ! want), depending on the model & grid, we may need to flip the + ! grid in the north-south direction. I already have a routine + ! for converting data from a 1-d to a 2-d array, and it has + ! the functionality for flipping a grid, so I programmed it as + ! getting a 1-d array from the netcdf read routine and send that + ! 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm(ip),imax,jmax,ncix + & ,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + + ! Need to get the value of the "missing_value" attribute for + ! this variable from the list of attributes in the NetCDF + ! file. Only do this for the first lead time, since the + ! value of the "missing_value" obviously will not change + ! with lead time. + +c nf_status = nf_inq_attlen (ncfile_id,varid,"missing_value" +c & ,imvlen) +c nf_status = nf_inq_attlen (ncfile_id,varid,"_FillValue" +c & ,ifvlen) + + ! These next two nf function calls retrieve the value of the + ! "missing_value" attribute from the list of attributes for + ! the given variable being read in. This is needed in order + ! to know if a non-valid point is being accessed, as for a + ! regional grid, like the nested fvGFS. In GRIB1/GRIB2 files, + ! such regions would be bitmapped out, but in a NetCDF file, + ! no such bitmap exists, so we have to check for missing + ! values. In case it's a moving grid, we need to do this + ! for every lead time, since the "map of missing values" + ! will shift with lead time. Once we have those missing + ! values, we can loop through them and fill the valid_pt + ! logical array so that, in the end, we will have the same + ! logical bitmap for masking out missing data that we have + ! with GRIB1/GRIB2 data. + + nf_status = nf_inq_varid (ncfile_id,chparm(ip),varid) + + print *,'nf_status from nf_inq_varid call = ',nf_status + + nf_status = nf_get_att_real (ncfile_id,varid,"missing_value" + & ,xmissing_value) + + print *,'nf_status from nf_get_att_real call = ',nf_status + +c nf_status = nf_get_att_real (ncfile_id,varid,"_FillValue" +c & ,xfill_value) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"missing_value",len=imvlen) +c nf_status = nf90_inquire_attribute (ncfile_id,chparm(ip) +c & ,"_FillValue",len=ifvlen) +c +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf90_get_att (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + if (verb .ge. 3) then + write (6,31) + 31 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,33) ifhours(ifh),ifclockmins(ifh),ip,chparm(ip) + & ,dmin,dmax + 33 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,35) chparm(ip),xmissing_value + 35 format (' --- ',a30,' missing value = ',g12.4) + endif + + ! This call to conv1d2d_logic_netcdf creates + ! a logical bitmap, so that in case we have + ! regional (non-global) data and an irregular grid (e.g., + ! the FV3 nested grid), we can mask out grid points that + ! have missing values as their data values. There is not + ! actually a native logical bitmap in NetCDF, so we will + ! create one by examining the real data values and masking + ! out grid points that have missing values. + + if (lbrdflag .eq. 'n') then + call conv1d2d_logic_netcdf (imax,jmax,f,valid_pt + & ,xmissing_value,need_to_flip_lats) + lbrdflag = 'y' + endif + + if (ip == 1) then ! 850 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,1) + & ,need_to_flip_lats) + else if (ip == 2) then ! 700 mb absolute vorticity + call conv1d2d_real (imax,jmax,f,zeta(1,1,2) + & ,need_to_flip_lats) + else if (ip == 3) then ! 850 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,1) + & ,need_to_flip_lats) + else if (ip == 4) then ! 850 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,1) + & ,need_to_flip_lats) + else if (ip == 5) then ! 700 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,2) + & ,need_to_flip_lats) + else if (ip == 6) then ! 700 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,2) + & ,need_to_flip_lats) + else if (ip == 7) then ! 850 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,1) + & ,need_to_flip_lats) + else if (ip == 8) then ! 700 mb gp height + call conv1d2d_real (imax,jmax,f,hgt(1,1,2) + & ,need_to_flip_lats) + else if (ip == 9) then ! MSLP + call conv1d2d_real (imax,jmax,f,slp + & ,need_to_flip_lats) + else if (ip == 10) then ! Near-sfc (10m) u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,4) + & ,need_to_flip_lats) + else if (ip == 11) then ! Near-sfc (10m) v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,4) + & ,need_to_flip_lats) + else if (ip == 12) then ! 500 mb u-comp + call conv1d2d_real (imax,jmax,f,u(1,1,3) + & ,need_to_flip_lats) + else if (ip == 13) then ! 500 mb v-comp + call conv1d2d_real (imax,jmax,f,v(1,1,3) + & ,need_to_flip_lats) + else if (ip == 14) then ! 300-500 mb mean Temp + call conv1d2d_real (imax,jmax,f,tmean + & ,need_to_flip_lats) + else if (ip == 15) then ! 500 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,3) + & ,need_to_flip_lats) + else if (ip == 16) then ! 200 mb height + call conv1d2d_real (imax,jmax,f,hgt(1,1,4) + & ,need_to_flip_lats) + else if (ip == 17) then ! Land-sea mask + call conv1d2d_real (imax,jmax,f,lsmask + & ,need_to_flip_lats) + else + + print *,'!!! NOTE: Parm not recognized. ' + print *,'!!! ip is > 17.... ip= ',ip + print *,'!!! Forecast time level = ',ifh + + endif + + endif + + enddo netcdf_standard_parm_read_loop + +c *--------------------------------------------------------------* +c If we are attempting to determine the cyclone structure using +c Hart's cyclone phase space, then read in data now that will +c allow us to do that. If we are instead just using the +c mid-level (300-500 mb) mean temperature to do that with a +c simple warm-core check, then that mean temperature field was +c already read in above in the read loop for the standard +c variables. The variables needed here for CPS are pretty +c straightforward: gp height every 50 mb from 300 to 900 mb. +c keep in mind that we have already read in a few of these +c gp height records for selected levels above. +c *--------------------------------------------------------------* + + if (phaseflag == 'y') then + + if (phasescheme == 'cps' .or. phasescheme == 'both') then + + chparm_cps(1) = netcdfinfo%z900name + chparm_cps(2) = netcdfinfo%z850name + chparm_cps(3) = netcdfinfo%z800name + chparm_cps(4) = netcdfinfo%z750name + chparm_cps(5) = netcdfinfo%z700name + chparm_cps(6) = netcdfinfo%z650name + chparm_cps(7) = netcdfinfo%z600name + chparm_cps(8) = netcdfinfo%z550name + chparm_cps(9) = netcdfinfo%z500name + chparm_cps(10) = netcdfinfo%z450name + chparm_cps(11) = netcdfinfo%z400name + chparm_cps(12) = netcdfinfo%z350name + chparm_cps(13) = netcdfinfo%z300name + + ! Read in GP Height levels for cyclone phase space... + + if (verb .ge. 3) then + print *,' ' + print *,'--- Reads for CPS parms follow...' + print *,' ' + endif + + netcdf_cps_parm_read_loop: do ip = 1,nreadparms_cps + + if (chparm_cps(ip) == 'X' .or. chparm_cps(ip) == 'x') then + if (verb .ge. 3) then + print *,'!!! ERROR: NetCDF read NOT requested for' + print *,'!!! CPS parm # ',ip + print *,'!!! You must have an error in your namelist.' + print *,'!!! You have requested to do cyclone phase' + print *,'!!! checking, so you need to include the ' + print *,'!!! NetCDF names for ALL requested gp height' + print *,'!!! variables from 900 to 300 mb, every 50 ' + print *,'!!! mb,in the namelist.' + print *,'!!! phaseflag is being set to NO (n), and ' + print *,'!!! phase-checking will NOT take place.' + print *,'!!! If you want to run again and just do ' + print *,'!!! phase-checking with a simple warm-core' + print *,'!!! check, then in the namelist set phaseflag' + print *,'!!! to y and set phasescheme to vtt.' + phaseflag = 'n' + endif + exit netcdf_cps_parm_read_loop + else + if (verb .ge. 3) then + print *,'+++ NetCDF read requested for cps parm # ',ip + & ,' ... parm= ',chparm_cps(ip) + endif + endif + + ! As above, we send a 1-d array, "f", to the netcdf read + ! routine. While that routine returns a 2-d array (which + ! we want), depending on the model & grid, we may need to + ! flip the grid in the north-south direction. I already + ! have a routine for converting data from a 1-d to a 2-d + ! array, and it has the functionality for flipping a grid, + ! so I programmed it as getting a 1-d array from the netcdf + ! read routine and send that 1-d array to conv1d2d_real. + + call get_var3_tlev_double (ncfile_id,chparm_cps(ip),imax + & ,jmax,ncix,f,igvret) + + if (verb .ge. 3) then + print *,' ' + print *,'After read, parm= ',chparm_cps(ip),' ifh= ',ifh + & ,' lead time index= ',ltix(ifh),' parm# (ip) = ',ip + & ,' ncix= ',ncix,' igvret= ',igvret + endif + + if (igvret == 0) then + +c call bitmapchk(kf,lb,f,dmin,dmax) + + readflag(ip) = .TRUE. + dmin = minval(f) + dmax = maxval(f) + +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"missing_value",xmissing_value) +c nf_status = nf_get_att_double (ncfile_id,chparm(ip) +c & ,"_FillValue",xfill_value) + + nf_status = nf_inq_varid (ncfile_id,chparm_cps(ip),varid) + nf_status = nf_get_att_real (ncfile_id,varid + & ,"missing_value",xmissing_value) + + if (verb .ge. 3) then + write (6,231) + 231 format ('parmread lead time parm# parm_id ' + & ,23x,'minval maxval') + + write (6,233) ifhours(ifh),ifclockmins(ifh),ip + & ,chparm_cps(ip),dmin,dmax + 233 format (' ',i3,':',i2.2,14x,i3,10x,a30,1x,2g12.4) + write (6,235) chparm_cps(ip),xmissing_value + 235 format (' --- ',a30,' missing value = ',g12.4) + endif + + call conv1d2d_real (imax,jmax,f,cpshgt(1,1,ip) + & ,need_to_flip_lats) + + endif + + enddo netcdf_cps_parm_read_loop + + endif + + endif + + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_ncdim1 (ncid,var1_name,nmax) +c +c ABSTRACT: This routine queries a netcdf file to get the +c value of a requested file dimension (e.g., imax, jmax) +c + implicit none + + include "netcdf.inc" + + integer, intent(in) :: ncid + character*(*), intent(in) :: var1_name + integer, intent(out) :: nmax + integer :: status, var1id + + status = nf_inq_dimid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + status = nf_inq_dimlen (ncid,var1id,nmax) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_ncdim1 +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine get_var1_double (ncid,var1_name,nmax,var1) +c +c ABSTRACT: This routine reads a netcdf file in order to return +c a 1-dimensional array of data. + + implicit none + + include "netcdf.inc" + + integer, intent(in):: ncid + character*(*), intent(in):: var1_name + integer, intent(in):: nmax + real, intent(out):: var1(nmax) + + integer :: status, var1id + + status = nf_inq_varid (ncid,var1_name,var1id) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) +! write(*,*) 'Got var1id', var1id + + status = nf_get_var_real (ncid,var1id,var1) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var1_double +c +c--------------------------------------------------------- +c +c--------------------------------------------------------- + subroutine get_var3_tlev_double (ncid,var3_name,imax,jmax,ncix + & ,var3,igvret) +c +c ABSTRACT: This routine reads a netcdf file and returns a +c 2-dimensional synoptic variable at a particular lead time. +c The lead time is specified by the ltix array, which is +c included in module tracked_parms and defined in subroutine +c read_fhours. +c +c PARAMETERS +c +c INPUT: +c ncid integer that contains the NetCDF file ID +c var3_name character name of NetCDF input file +c imax integer x-dimension of input data +c jmax integer y-dimension of input data +c ncix integer index of time level for where this time level +c actually is inside the NetCDF data. Do NOT confuse this +c with the index of where this forecast hour is in the +c user's list of input forecast hours, as they may be +c different. For example, the user may request times that +c are every 6 hours, but the NetCDF file might have times +c that are every hour, so the indices for those two arrays +c will be different. Be sure to use the one (ncix) that +c indicates where the data actually starts in the +c NetCDF file. +c +c OUTPUT: +c var3 real array with real values returned from NetCDF read +c igvret integer return code from this routine + + USE tracked_parms; USE verbose_output; USE netcdf_parms + + implicit none + + include "netcdf.inc" +c + integer, intent(in) :: ncid,ncix + character*(*), intent(in) :: var3_name + integer, intent(in) :: imax,jmax + real, intent(out) :: var3(imax,jmax) + integer :: istart(3),ilength(3) + integer :: status,var3id,igvret + + if (verb .ge. 3) then + print *,' ' + print *,'In get_var3_tlev_double, ncix= ',ncix + print *,' nctotalmins(ncix)= ',nctotalmins(ncix) + endif + + istart(1) = 1 + istart(2) = 1 + istart(3) = ncix + + ilength(1) = imax + ilength(2) = jmax + ilength(3) = 1 + + igvret = 0 + + status = nf_inq_varid (ncid,var3_name,var3id) + + if (status /= NF_NOERR) then + print *,' ' + print *,'NOTE: Could not find variable ',var3_name,' at time' + & ,' index ncix= ',ncix + & ,' nctotalmins(ncix)= ',nctotalmins(ncix) + + igvret = 92 + return + endif + + status = nf_get_vara_real (ncid,var3id,istart,ilength,var3) + if (status .ne. NF_NOERR) call handle_netcdf_err(status) + + end subroutine get_var3_tlev_double +c +c---------------------------------------------------------------------- +c +c---------------------------------------------------------------------- + subroutine handle_netcdf_err (status) +c +c ABSTRACT: This subroutine is an error handling routine for NetCDF- +c related functions. + + implicit none + + include "netcdf.inc" + + integer status +c + if (status /= nf_noerr) then + print *,' ' + print *,'Tracker NetCDF error: ' + print *, nf_strerror(status) + stop 'Stopped' + endif + + end subroutine handle_netcdf_err +c +c------------------------------------------------------------------- +c +c------------------------------------------------------------------- + subroutine bitmapchk (n,ld,d,dmin,dmax) +c +c This subroutine checks the bitmap for non-existent data values. +c Since the data from the regional models have been interpolated +c from either a polar stereographic or lambert conformal grid +c onto a lat/lon grid, there will be some gridpoints around the +c edges of this lat/lon grid that have no data; these grid +c points have been bitmapped out by Mark Iredell's interpolater. +c To provide another means of checking for invalid data points +c later in the program, set these bitmapped data values to a +c value of -999.0. The min and max of this array are also +c returned if a user wants to check for reasonable values. +c + logical(1) ld + dimension ld(n),d(n) +c + dmin=1.E15 + dmax=-1.E15 +c + do i=1,n + if (ld(i)) then + dmin=min(dmin,d(i)) + dmax=max(dmax,d(i)) + else + d(i) = -999.0 + endif + enddo +c + return + end +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic (imax,jmax,lb1d,lb2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of logical data (lb1d) into a 2-dimensional output +c array (dimension imax,jmax) of logical data (lb2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c lb1d 1-d array containing logical bitmap values +c iscanflag This is kgds(11), an integer value in the GDS, +c which holds the scanning mode for the data values +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + logical(1) lb1d(imax*jmax),lb2d(imax,jmax) + logical(1) :: need_to_flip_lats + integer :: ilat,ilatix,ilon,imax,jmax +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + lb2d(ilon,ilatix) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + lb2d(ilon,ilat) = lb1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end + +c +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + subroutine conv1d2d_logic_netcdf (imax,jmax,dat1d,lb2d + & ,xmissing_val,need_to_flip_lats) +c +c ABSTRACT: The purpose of this routine is to create a 2-d logical +c bitmap to be used for masking out regions with missing data, +c such as for a regional grid with irregular boundaries (such as +c we've seen for the regional / nested FV3). This bitmap will +c have the same functionality as a GRIB1/GRIB2 bitmap. The trick +c is that NetCDF does not have a logical bitmap within its +c definition, so we need to make one. We do this by reading in +c the "missing_value" attribute for any variable, then here we +c scan through all the data values retrieved from the NetCDF read, +c and then for all grid points with missing values we set the +c valid_pt flag to .false. +c +c Note the use of the need_to_flip_lats flag. This is in order to +c handle grids that are flipped. Most grids -- NCEP, UKMET, ECMWF +c -- have point (1,1) as the uppermost left point on the grid, and +c the data goes from north to south. Some grids -- GFDL and the +c new NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the need_to_flip_lats flag was set to TRUE in getgridinfo, meaning +c that we have northward scanning data, we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. +c +c PARAMETERS: +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d array containing floating point data values +c xmissing_val real value of missing value for the given variable +c that was read in for the calling routine +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c lb2d 2-d array containing logical bitmap values +c + USE verbose_output + + implicit none + + logical(1) lb2d(imax,jmax) + logical(1) need_to_flip_lats + integer ilat,ilatix,ilon,imax,jmax,tct,fct,mct + real dat1d(imax*jmax) + real xmissing_val +c + tct = 0 + fct = 0 + mct = 0 + + if (verb >= 3) then + print *,' ' + print *,'TOP of conv1d2d_logic_netcdf, xmissing_val= ' + & ,xmissing_val + print *,' ' + endif +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then + lb2d(ilon,ilatix) = .false. +c print *,'LBSF FLIP: ilon= ',ilon,' ilatix= ',ilatix +c fct = fct + 1 + else + lb2d(ilon,ilatix) = .true. +c tct = tct + 1 + endif + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + if (dat1d(ilon+(ilat-1)*imax) == xmissing_val) then +c print *,'LBSF no-flip: ilon= ',ilon,' ilat= ',ilat + lb2d(ilon,ilat) = .false. +c fct = fct + 1 + else + lb2d(ilon,ilat) = .true. +c tct = tct + 1 + endif + enddo + enddo + + endif + +c print *,' ' +c print *,' LB STATS: tct= ',tct,' fct= ',fct,' mct= ',mct +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine conv1d2d_real (imax,jmax,dat1d,dat2d,need_to_flip_lats) +c +c ABSTRACT: This subroutine converts a 1-dimensional input +c array of real data (dat1d) into a 2-dimensional output +c array (dimension imax,jmax) of real data (dat2d). +c +c This subroutine was updated in 6/2000 to add the scanning mode +c flag (iscanflag) as an input. This is in order to handle grids +c that are flipped. Most grids -- NCEP, UKMET, ECMWF -- have +c point (1,1) as the uppermost left point on the grid, and the +c data goes from north to south. Some grids -- GFDL and the new +c NAVGEM grid -- are flipped; their point (1,1) is the lowermost +c left point, and their data goes from south to north. So if +c the scanning mode flag indicates northward scanning data +c (bit 2 in the flag is turned on), we catch it in this +c subroutine and flip the data ourselves for our own arrays, +c since this whole program is structured around the data going +c from north to south. As of the writing of this, only the +c first 3 bits of the scanning flag are used, which is why we +c can use the mod statement in the code below. +c +c UPDATE 8/2009: I removed the scanning mode flag, since that is +c GRIB-specific. The north-south determination is now handled with +c the logical flag need_to_flip_lats. +c +c INPUT: +c imax Number of gridpoints in i direction in input box +c jmax Number of gridpoints in j direction in input box +c dat1d 1-d real array of data +c need_to_flip_lats logical flag, set in getgridinfo, that +c indicates if data is correctly N-to-S, or if it is +c S-to-N and needs to be flipped. +c +c OUTPUT: +c dat2d 2-d real array of data +c + logical(1) :: need_to_flip_lats + real dat1d(imax*jmax),dat2d(imax,jmax) +c + if (need_to_flip_lats) then + + ! Input data is south to north; flip the data while + ! converting to 2-d grid.... + + do ilat=1,jmax + ilatix = jmax - ilat + 1 + do ilon=1,imax + dat2d(ilon,ilatix) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + else + + ! Input data is north to south. Just convert the + ! data onto a 2-d grid, do not flip it.... + + do ilat=1,jmax + do ilon=1,imax + dat2d(ilon,ilat) = dat1d(ilon+(ilat-1)*imax) + enddo + enddo + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_nlists (inp,trkrinfo,netcdfinfo) +c +c ABSTRACT: This subroutine simply reads in the namelists that are +c created in the shell script. Namelist datein contains the +c starting date information, plus the model identifier. Namelist +c stswitch contains the flags for processing for each storm. +c + USE inparms; USE set_max_parms; USE atcf; USE trkrparms; USE phase + USE structure; USE gfilename_info + USE verbose_output; USE waitfor_parms; USE netcdf_parms + USE tracking_parm_prefs + + implicit none + + integer ifh + type (datecard) inp + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo +c + namelist/datein/inp + namelist/atcfinfo/atcfnum,atcfname,atcfymdh,atcffreq + namelist/trackerinfo/trkrinfo + namelist/phaseinfo/phaseflag,phasescheme,wcore_depth + namelist/structinfo/structflag,ikeflag + namelist/fnameinfo/gmodname,rundescr,atcfdescr + namelist/verbose/verb,verb_g2 + namelist/waitinfo/use_waitfor,wait_min_age,wait_min_size + & ,wait_max_wait,wait_sleeptime + & ,use_per_fcst_command,per_fcst_command + namelist/netcdflist/netcdfinfo + namelist/parmpreflist/user_wants_to_track_zeta850 + & ,user_wants_to_track_zeta700,user_wants_to_track_wcirc850 + & ,user_wants_to_track_wcirc700,user_wants_to_track_gph850 + & ,user_wants_to_track_gph700,user_wants_to_track_mslp + & ,user_wants_to_track_wcircsfc,user_wants_to_track_zetasfc + & ,user_wants_to_track_thick500850 + & ,user_wants_to_track_thick200500 + & ,user_wants_to_track_thick200850 + +c Set namelist default values: + use_per_fcst_command='t' + per_fcst_command=' ' + atcffreq=600 + trkrinfo%enable_timing=1 + trkrinfo%want_oci=.false. + trkrinfo%gribver=1 ! Set to GRIB1 as default, can be set to + ! something else in the namelist input. + + read (5,NML=datein,END=801) + 801 continue + read (5,NML=atcfinfo,END=807) + 807 continue + print *,'just before trackerinfo read namelist' + read (5,NML=trackerinfo,END=809) + 809 continue + print *,'just after trackerinfo read namelist' + read (5,NML=phaseinfo,END=811) + 811 continue + read (5,NML=structinfo,END=815) + 815 continue + read (5,NML=fnameinfo,END=817) + 817 continue + read (5,NML=waitinfo,END=821) + 821 continue + read (5,NML=netcdflist,END=823) + 823 continue + read (5,NML=parmpreflist,END=825) + 825 continue + read (5,NML=verbose,END=819,ERR=833) + 819 continue + goto 837 + 833 continue + verb = 1 + 837 continue + + print *,'in read_nlists, verb= ',verb + + if ( verb .ge. 0 ) then + print *,' ' + print *,'After datein namelist in trak.f, namelist ' + & ,'parms follow:' + print *,'Forecast initial year = byy = ',inp%byy + print *,'Forecast initial month = bmm = ',inp%bmm + print *,'Forecast initial day = bdd = ',inp%bdd + print *,'Forecast initial hour = bhh = ',inp%bhh + print *,'Forecast model identifier = model= ',inp%model + print *,'Forecast model type = modtyp= ',inp%modtyp + print *,'Forecast model data lead time units= lt_units= ' + & ,inp%lt_units + print *,'Forecast model data sequencing setup= file_seq= ' + & ,inp%file_seq + print *,'Forecast model nest type = ',inp%nesttyp +c + print *,' ' + print *,'Values read in from atcfinfo namelist: ' + write (6,89) atcfnum,atcfname + write (6,90) atcfymdh + write (6,92) atcffreq + 89 format ('ATCF ID = ',i2,' ATCF Name = ',a4) + 90 format ('ATCF date (initial date on output atcf records) = ' + & ,i10) + 92 format ('ATCF output frequency (in hours*100) = atcffreq = ',i6) +c + print *,' ' + print *,'Values read in from trackerinfo namelist follow: ' + write (6,101) ' western boundary = westbd = ',trkrinfo%westbd + write (6,101) ' eastern boundary = eastbd = ',trkrinfo%eastbd + write (6,101) ' northern boundary = northbd = ',trkrinfo%northbd + write (6,101) ' southern boundary = southbd = ',trkrinfo%southbd + write (6,102) ' tracker type = ',trkrinfo%type + write (6,103) ' mslp threshold = mslpthresh = ' + & ,trkrinfo%mslpthresh + write (6,120) ' Flag for using backup mslp gradient check= ' + & ,'use_backup_mslp_grad_check = ' + & ,trkrinfo%use_backup_mslp_grad_check + write (6,103) ' v850 threshold = v850thresh = ' + & ,trkrinfo%v850thresh + write (6,122) ' Flag for using backup 850 mb Vt check= ' + & ,'use_backup_850_vt_check = ' + & ,trkrinfo%use_backup_850_vt_check + write (6,123) ' Max allowable distance between the ' + & ,'tracker-found fixes for mslp and 850 zeta = ' + & ,trkrinfo%max_mslp_850 + write (6,104) ' model grid type = ',trkrinfo%gridtype + write (6,101) ' Contour interval to be used = ',trkrinfo%contint + write (6,106) ' Flag for whether or not roci will be computed' + & ,' and written out for tracker-type case = ' + & ,trkrinfo%want_oci + write (6,105) ' Flag for whether or not vitals will be written ' + & ,'out = ',trkrinfo%out_vit + write (6,109) ' Flag for whether or not a land mask will be ' + & ,'used for tcgen candidate low filtering = ' + & ,trkrinfo%use_land_mask + write (6,110) ' Flag for input data type (grib or netcdf) = ' + & ,trkrinfo%inp_data_type + write (6,107) ' Flag for which GRIB version (1 or 2) the input' + & ,' data will be in = ',trkrinfo%gribver + write (6,108) ' Flag for input GRIB2 JPDTN (0 or 1) = ' + & ,trkrinfo%g2_jpdtn + write (6,112) ' Flag for input GRIB2 MSLP ID (1 or 192) = ' + & ,trkrinfo%g2_mslp_parm_id + write (6,114) ' Flag for input GRIB1 MSLP ID (102 or 130) = ' + & ,trkrinfo%g1_mslp_parm_id + write (6,116) ' Flag for input GRIB1 sfcwind level type ' + & ,'(PDS Octet 10... should be 1 or 105) = ' + & ,trkrinfo%g1_sfcwind_lev_typ + write (6,118) ' Flag for input GRIB1 sfcwind level value ' + & ,'(PDS Octets 11 & 12... usually 0 or 10) = ' + & ,trkrinfo%g1_sfcwind_lev_val + + 101 format (a31,f7.2) + 102 format (a16,a7) + 103 format (a31,f7.4) + 104 format (a19,a8) + 106 format (a46,a41,L1) + 105 format (a48,a6,a1) + 109 format (a45,a41,a1) + 110 format (a45,a6) + 107 format (a47,a19,i1) + 108 format (a39,i2) + 112 format (a43,i4) + 114 format (a45,i4) + 116 format (a41,a39,i4) + 118 format (a42,a43,i4) + 120 format (a44,a29,a1) + 122 format (a40,a26,a1) + 123 format (a36,a44,f7.1) + + print *,' ' + print *,' ' + print *,'Values read in from netcdflist namelist: ' + print *,' ' + write (6,300) netcdfinfo%num_netcdf_vars ! Total *possible* + ! number of input NetCDF variables, + ! including those that are included + ! in the input file and those that + ! are not. + write (6,370) netcdfinfo%netcdf_filename ! full path filename + write (6,301) + write (6,302) netcdfinfo%rv850name ! 850 mb rel vort + write (6,304) netcdfinfo%rv700name ! 700 mb rel vort + write (6,306) netcdfinfo%u850name ! 850 mb u-comp + write (6,308) netcdfinfo%v850name ! 850 mb v-comp + write (6,310) netcdfinfo%u700name ! 700 mb u-comp + write (6,312) netcdfinfo%v700name ! 700 mb v-comp + write (6,314) netcdfinfo%z850name ! 850 mb gp height + write (6,316) netcdfinfo%z700name ! 700 mb gp height + write (6,318) netcdfinfo%mslpname ! mslp + write (6,320) netcdfinfo%usfcname ! near-sfc u-comp + write (6,322) netcdfinfo%vsfcname ! near-sfc v-comp + write (6,324) netcdfinfo%u500name ! 500 mb u-comp + write (6,326) netcdfinfo%v500name ! 500 mb v-comp + write (6,328) netcdfinfo%tmean_300_500_name !Mean T @ 300-500 mb + write (6,330) netcdfinfo%z500name ! 500 mb gp height + write (6,332) netcdfinfo%z200name ! 200 mb gp height + write (6,334) netcdfinfo%lmaskname ! Land mask + write (6,336) netcdfinfo%z900name ! 900 mb gp height + write (6,338) netcdfinfo%z800name ! 800 mb gp height + write (6,340) netcdfinfo%z750name ! 750 mb gp height + write (6,342) netcdfinfo%z650name ! 650 mb gp height + write (6,344) netcdfinfo%z600name ! 600 mb gp height + write (6,346) netcdfinfo%z550name ! 550 mb gp height + write (6,348) netcdfinfo%z450name ! 450 mb gp height + write (6,350) netcdfinfo%z400name ! 400 mb gp height + write (6,352) netcdfinfo%z350name ! 350 mb gp height + write (6,354) netcdfinfo%z300name ! 300 mb gp height + write (6,355) netcdfinfo%time_name ! Name of time variable + ! (usually it is "time") + write (6,356) netcdfinfo%lon_name ! longitudes + write (6,358) netcdfinfo%lat_name ! latitudes + write (6,359) netcdfinfo%time_units ! This will be either "days" + ! or "hours". If it's "hours", + ! then all the time data values + ! are for hours since the initial + ! time. Same thing for "days", + ! however if it is "days", then + ! know that a value of 0.25 will + ! be the same as a 6-hour lead + ! time. + + 300 format ('Total *possible* number of input NetCDF variables,' + & ,/,' including those that are included in the input' + & ,/,' NetCDF file and those that are not = ',i4) + 370 format ('Input NetCDF filename = ',a180) + 301 format (' ',/ + & ,'List of NetCDF variables follows. A value of X ',/ + & ,'indicates the variable is not included in the ',/ + & ,'input file and no attempt will be made to read in ',/ + & ,'that variable: ',/,' ') + 302 format ('NetCDF variable name for 850 mb vort = ',a30) + 304 format ('NetCDF variable name for 700 mb vort = ',a30) + 306 format ('NetCDF variable name for 850 mb u-comp = ',a30) + 308 format ('NetCDF variable name for 850 mb v-comp = ',a30) + 310 format ('NetCDF variable name for 700 mb u-comp = ',a30) + 312 format ('NetCDF variable name for 700 mb v-comp = ',a30) + 314 format ('NetCDF variable name for 850 mb gp height = ',a30) + 316 format ('NetCDF variable name for 700 mb gp height = ',a30) + 318 format ('NetCDF variable name for MSLP = ',a30) + 320 format ('NetCDF variable name for near-sfc u-comp = ',a30) + 322 format ('NetCDF variable name for near-sfc v-comp = ',a30) + 324 format ('NetCDF variable name for 500 mb u-comp = ',a30) + 326 format ('NetCDF variable name for 500 mb v-comp = ',a30) + 328 format ('NetCDF variable name for 300-500 mb Mean T = ',a30) + 330 format ('NetCDF variable name for 500 mb gp height = ',a30) + 332 format ('NetCDF variable name for 200 mb gp height = ',a30) + 334 format ('NetCDF variable name for land-sea mask = ',a30) + 336 format ('NetCDF variable name for 900 mb gp height = ',a30) + 338 format ('NetCDF variable name for 800 mb gp height = ',a30) + 340 format ('NetCDF variable name for 750 mb gp height = ',a30) + 342 format ('NetCDF variable name for 650 mb gp height = ',a30) + 344 format ('NetCDF variable name for 600 mb gp height = ',a30) + 346 format ('NetCDF variable name for 550 mb gp height = ',a30) + 348 format ('NetCDF variable name for 450 mb gp height = ',a30) + 350 format ('NetCDF variable name for 400 mb gp height = ',a30) + 352 format ('NetCDF variable name for 350 mb gp height = ',a30) + 354 format ('NetCDF variable name for 300 mb gp height = ',a30) + 355 format ('NetCDF variable name for time = ',a30) + 356 format ('NetCDF variable name for longitudes = ',a30) + 358 format ('NetCDF variable name for latitudes = ',a30) + 359 format ('NetCDF time value (hours|days) = ',a30) + + print *,' ' + print *,' ' + print *,'Values read in from parmpreflist namelist: ' + print *,' ' + write (6,402) user_wants_to_track_zeta850 + write (6,404) user_wants_to_track_zeta700 + write (6,406) user_wants_to_track_wcirc850 + write (6,408) user_wants_to_track_wcirc700 + write (6,410) user_wants_to_track_gph850 + write (6,412) user_wants_to_track_gph700 + write (6,414) user_wants_to_track_mslp + write (6,416) user_wants_to_track_wcircsfc + write (6,418) user_wants_to_track_zetasfc + write (6,420) user_wants_to_track_thick500850 + write (6,422) user_wants_to_track_thick200500 + write (6,424) user_wants_to_track_thick200850 + + 402 format ('user_wants_to_track_zeta850= ',a2) + 404 format ('user_wants_to_track_zeta700= ',a2) + 406 format ('user_wants_to_track_wcirc850= ',a2) + 408 format ('user_wants_to_track_wcirc700= ',a2) + 410 format ('user_wants_to_track_gph850= ',a2) + 412 format ('user_wants_to_track_gph700= ',a2) + 414 format ('user_wants_to_track_mslp= ',a2) + 416 format ('user_wants_to_track_wcircsfc= ',a2) + 418 format ('user_wants_to_track_zetasfc= ',a2) + 420 format ('user_wants_to_track_thick500850= ',a2) + 422 format ('user_wants_to_track_thick200500= ',a2) + 424 format ('user_wants_to_track_thick200850= ',a2) + + print *,' ' + print *,'Values read in from phaseinfo namelist: ' + write (6,211) phaseflag,phasescheme + write (6,212) wcore_depth + 211 format ('Storm phase flag = ',a1,' Phase scheme = ',a4) + 212 format ('Storm phase, warm core depth (wcore_depth) = ',f7.2) + + print *,' ' + print *,'Values read in from structinfo namelist: ' + write (6,93) structflag + write (6,95) ikeflag + 93 format ('Structure flag = ',a1) + 95 format ('IKE flag = ',a1) + + print *,' ' + print *,'Values read in for grib file name from fnameinfo' + & ,' namelist: ' + write (6,131) gmodname + write (6,133) rundescr + write (6,135) atcfdescr + 131 format ('Model name description = gmodname = ',a4) + 133 format ('Forecast run description = rundescr = ',a40) + 135 format ('Optional ATCF / Storm name description = atcfdescr = ' + & ,a40) + + print *,' ' + print *,'Value read in for verbose output for most output:' + write (6,141) verb + 141 format ('Value read in for verbose flag = verb = ',i2) + + print *,' ' + print *,'Value read in for verbose output for grib2 output:' + write (6,142) verb_g2 + 142 format ('Value read in for GRIB2 verbose flag = verb_g2 = ',i2) + + print *,' ' + print *,'Values read in from waitinfo namelist:' + write (6,151) use_waitfor + write (6,152) wait_min_age + write (6,153) wait_min_size + write (6,154) wait_max_wait + write (6,155) wait_sleeptime + if(len_trim(per_fcst_command)>0) then + write (6,156) trim(per_fcst_command) + else +c No command specified, so disable the feature + use_per_fcst_command='n' + endif + 151 format ('Flag for input file waiting = use_waitfor = ',a1) + 152 format ('min age (time in seconds since last mod) = ' + & ,'wait_min_age = ',i8) + 153 format ('min file size in bytes = wait_min_size = ',i12) + 154 format ('max number of seconds to wait for each file = ' + & ,'wait_max_wait = ',i6) + 155 format ('number of seconds to sleep between checks = ' + & ,'wait_sleeptime = ',i6) + 156 format ('command to run after every forecast time = "',A,'"') +c + if (use_waitfor == 'y') then + if (inp%file_seq == 'multi') then + continue + else + print *,' ' + print *,'!!! ERROR: The use_waitfor flag is set to "y".' + print *,' This requires that the inp%file_seq flag be' + print *,' set to "multi", but you have specified ' + print *,' something else. ' + print *,' inp%file_seq = ',inp%file_seq + print *,' STOPPING....' + print *,' ' + STOP 95 + endif + endif +c + endif + return + end +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_fhours (ifhmax) +c +c ABSTRACT: This subroutine reads in a text file that contains the +c forecast times that will be read in. The format of the file is +c in "MMMMM", i.e., minutes, for example, for a forecast going out +c to 120h, the file would look like this: +c +c For reference, here +c are the times that +c match up with the +c minutes on the left: +c +c 1 0 0:00 +c 2 240 4:00 +c 3 270 4:30 +c 4 300 5:00 +c 5 330 5:30 +c 6 360 6:00 +c 7 600 10:00 +c 8 630 10:30 +c 9 660 11:00 +c 10 690 11:30 +c 11 720 12:00 +c 12 960 16:00 +c 13 990 16:30 +c . . . +c . . . +c . . . +c 87 7200 120:00 +c +c Note that we are now allowing for sub-hourly time intervals. +c + USE tracked_parms + USE verbose_output + + implicit none +c + integer, parameter :: iunit_fh=15 + integer itmphrs(750),itmpmins(750),input_mins(750),itmpltix(750) + integer ifhmax,inphr,inpmin,ict,i,ifa,ifma,icma,ira,inpltix,ila + real xminfract + + itmphrs = -99 + itmpmins = -99 + + if (allocated(ifhours)) deallocate (ifhours) + if (allocated(iftotalmins)) deallocate (iftotalmins) + if (allocated(ifclockmins)) deallocate (ifclockmins) + if (allocated(fhreal)) deallocate (fhreal) + if (allocated(ltix)) deallocate (ltix) + + ict = 0 + do while (.true.) + + if ( verb .ge. 3 ) then + print *,'Top of while loop in read_fhours' + endif + + read (iunit_fh,85,end=130) inpltix,inpmin + write (6,85) inpltix,inpmin + + if (inpmin >= 0 .and. inpmin < 150000) then + ict = ict + 1 + itmpltix(ict) = inpltix + itmphrs(ict) = inpmin / 60 + itmpmins(ict) = mod(inpmin,60) + input_mins(ict) = inpmin + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Input minutes not between 0 and 150000' + print *,'!!! inpmin= ',inpmin + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + if ( verb .ge. 3 ) then + print *,'readloop, ict= ',ict,' inpmin= ',inpmin + endif + + enddo + + 130 continue + + ifhmax = ict + + 85 format (i4,1x,i5) + + if ( verb .ge. 3 ) then + print *,' ' + endif + + allocate (ifhours(ifhmax),stat=ifa) + allocate (iftotalmins(ifhmax),stat=ifma) + allocate (ifclockmins(ifhmax),stat=icma) + allocate (fhreal(ifhmax),stat=ira) + allocate (ltix(ifhmax),stat=ila) + if (ifa /= 0 .or. ifma /= 0 .or. icma /= 0 .or. ira /= 0 .or. + & ila /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_fhours allocating either ifhours,' + print *,'!!! iftotalmins, ifclockmins or fhreal.' + print *,'!!! ifa = ',ifa,' ifma= ',ifma,' ira= ',ira + print *,'!!! icma= ',icma,' ila= ',ila + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + + do i = 1,ifhmax + + ltix(i) = itmpltix(i) + xminfract = float(itmpmins(i)) / 60. + fhreal(i) = float(itmphrs(i)) + xminfract + ifhours(i) = itmphrs(i) + ifclockmins(i) = itmpmins(i) + iftotalmins(i) = input_mins(i) + + if (i > 1) then + if (fhreal(i) > fhreal(i-1)) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! ERROR: In read_fhours, the time read in ' + print *,'!!! is not greater than the previous time.' + print *,'!!! i= ',i + print *,'!!! fhreal(i)= ',fhreal(i) + print *,'!!! fhreal(i-1)= ',fhreal(i-1) + print *,'!!! STOPPING EXECUTION' + endif + + STOP 91 + endif + endif + + if ( verb .ge. 3 ) then + write (6,87) i,ltix(i),iftotalmins(i),fhreal(i),ifhours(i) + & ,ifclockmins(i) + endif + + enddo + + 87 format (1x,'i= ',i3,' input lead time index= ',i4,' minutes= ' + & ,i5,' real_lead_time= ',f6.2,' clock_lead_time= ',i3,':' + & ,i2) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_tcv_card1 (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + logical(1) :: vit_file_exists + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + + ! Check to see if the TC Vitals file exists. If so, then open it + ! using the unit specified in lucard. + + inquire (file="tcvit_rsmc_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for existing, RSMC-numbered' + & ,' storms exists and will be opened with ' + & ,' unit= lucard= ',lucard + endif + + open (unit=lucard,file="tcvit_rsmc_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_rsmc_storms.txt has ' + print *,' been opened with unit= lucard= ',lucard + endif + + else + + if (trkrinfo%type == 'tracker') then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card. The fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. This TC Vitals file is needed for' + print *,'!!! a tracker case. Check to see that the ' + print *,'!!! TC Vitals file exists in this directory and' + print *,'!!! is named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING....' + print *,'!!! ' + iret=99 + return + endif + + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! NOTE: In read_tcv_card, the fortran inquire' + print *,'!!! statement indicates that the tcvitals file for' + print *,'!!! already-existing, RSMC-numbered storms does ' + print *,'!!! NOT exist. While this TC Vitals file is ' + print *,'!!! needed for tracker cases, you are running' + print *,'!!! either a midlat or tcgen case here, and so ' + print *,'!!! that file is not needed... although you can ' + print *,'!!! run with using tc vitals for those genesis' + print *,'!!! cases if you want to. You may want to check' + print *,'!!! and make sure this is what you intend. If ' + print *,'!!! you do want to use it, the TC Vitals file ' + print *,'!!! should be in this directory and it should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! ' + endif + + endif + + endif + + ii=1 + + if (vit_file_exists) then + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + 801 continue + endif + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + + maxstorm = numtcv + + if (maxstorm > 0) then + continue + else + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! STOPPING...' + print *,'!!! ' + iret=99 + return + endif + endif + + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that the TC Vitals file' + print *,'!!! has been created and exists in the working' + print *,'!!! directory. That TC vitals file should be' + print *,'!!! named tcvit_rsmc_storms.txt' + print *,'!!! Stopping....' + print *,' ' + endif + + iret = 99 + return + + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card opening rsmc TC vitals' + print *,'!!! file named tcvit_rsmc_storms.txt. A file with' + print *,'!!! that name needs to be in your working directory.' + print *,'!!! It should contain the input TC vitals for ' + print *,'!!! already-existing storms that have rsmc-issued' + print *,'!!! storm IDs.' + endif + + iret = 97 + return + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c +c----------------------------------------------------------------------- +c +c----------------------------------------------------------------------- + subroutine read_gen_vitals1(lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + logical(1) :: vit_file_exists + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + + ! Check to see if the genesis TC Vitals file exists. If so, then + ! open it using the unit specified in lgvcard. + + inquire (file="tcvit_genesis_storms.txt",exist=vit_file_exists) + + if (vit_file_exists) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC Vitals file for genesis' + & ,' storms exists and will be opened with ' + & ,' unit= lgvcard= ',lgvcard + endif + + open (unit=lgvcard,file="tcvit_genesis_storms.txt",status='old' + & ,err=887) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ TC vitals file tcvit_genesis_storms.txt has ' + print *,' been opened with unit= lgvcard= ',lgvcard + endif + + endif + + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + + ii = numtcv + 1 + + if (vit_file_exists) then + + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1 + & ,1x,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + endif + + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + + 887 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals opening genesis vitals' + print *,'!!! file named tcvit_genesis_storms.txt' + endif + + iret = 97 + return + + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_grib (imax,jmax,ifh,dx,dy,lugb,lugi + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just read the +c grib file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE tracked_parms; USE inparms + USE verbose_output; USE params; USE grib_mod + + implicit none + + type (trackstuff) trkrinfo + type (datecard) inp + + type(gribfield) :: gfld,prevfld,holdgfld + logical(1) :: need_to_flip_lats,need_to_flip_lons + logical(1), allocatable :: lb(:) + logical :: unpack=.true. + logical :: open_grb=.false. + CHARACTER(len=8) :: pabbrev + integer,dimension(200) :: jids,jpdt,jgdt + integer, parameter :: jf=40000000 + integer :: listsec1(13) + integer pdt_4p0_vert_level,pdt_4p0_vtime + real xhold,xlondiff,xlatdiff,temp,firstval,lastval + real, allocatable :: f(:) + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer jpds(200),jgds(200),igetpds(200),igetgds(200) + integer, intent(in) :: ifh + integer, intent(out) :: imax,jmax + integer iia,ija,ila,midi,midj,i,j,iix,jix,ifa,iret + integer iscanflag,iggret,kf,k,lugb,lugi,jskp,jdisc + integer jpdtn,jgdtn,npoints,icount,ipack,krec + integer :: listsec0(2)=(/0,2/) + integer :: igds(5)=(/0,0,0,0,0/),previgds(5) + integer :: idrstmpl(200) + integer :: currlen=1000000 + + iggret = 0 + + allocate (lb(jf),stat=ila); allocate (f(jf),stat=ifa) + if (ila /= 0 .or. ifa /= 0) then + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating either lb or f' + print *,'!!! ila = ',ila,' ifa= ',ifa + endif + iggret = 97 + return + endif + + if (trkrinfo%gribver == 2) then + + ! Search for a record from a GRIB2 file + + ! + ! --- Initialize Variables --- + ! + + gfld%idsect => NULL() + gfld%local => NULL() + gfld%list_opt => NULL() + gfld%igdtmpl => NULL() + gfld%ipdtmpl => NULL() + gfld%coord_list => NULL() + gfld%idrtmpl => NULL() + gfld%bmap => NULL() + gfld%fld => NULL() + + jdisc=0 ! meteorological products + jids=-9999 + jpdtn=trkrinfo%g2_jpdtn ! 0 = analysis or forecast; 1 = ens fcst + jgdtn=0 ! lat/lon grid + jgdt=-9999 + jpdt=-9999 + + npoints=0 + icount=0 + jskp=0 + +c Search for Temperature or GP Height by production template.... + + JPDT(1:15)=(/-9999,-9999,-9999,-9999,-9999,-9999,-9999 + & ,-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999/) + + ! Request a record on a lat/lon grid. + + jgdtn = 0 + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpdt(8) = 0 + jpdt(9) = iftotalmins(ifh) + else + jpdt(8) = 1 + jpdt(9) = ifhours(ifh) + endif + + if (verb >= 3) then + print *,'before getgb2 call, lugb= ',lugb,' lugi= ',lugi + endif + + call getgb2(lugb,lugi,jskp,jdisc,jids,jpdtn,jpdt,jgdtn,jgdt + & ,unpack,krec,gfld,iret) + if ( iret.ne.0) then + print *,' ' + print *,' ERROR: getgb2 error in getgridinfo = ',iret + print *,' FATAL ERROR: cannot proceed without info ' + print *,' from getgridinfo. STOPPING....' + stop 95 + endif + +c Determine packing information from GRIB2 file +c The default packing is 40 JPEG 2000 + + ipack = 40 + + if ( verb_g2 .ge. 1 ) then + print *,' ' + print *,' -- BEGIN getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' gfld%idrtnum = ', gfld%idrtnum + endif + + ! Set DRT info ( packing info ) + if ( gfld%idrtnum.eq.0 ) then ! Simple packing + ipack = 0 + elseif ( gfld%idrtnum.eq.2 ) then ! Complex packing + ipack = 2 + elseif ( gfld%idrtnum.eq.3 ) then ! Complex & spatial packing + ipack = 31 + elseif ( gfld%idrtnum.eq.40.or.gfld%idrtnum.eq.15 ) then + ! JPEG 2000 packing + ipack = 40 + elseif ( gfld%idrtnum.eq.41 ) then ! PNG packing + ipack = 41 + endif + + if ( verb_g2 .ge. 1 ) then + print *,'After check of idrtnum, ipack= ',ipack + print *,'Number of gridpts= gfld%ngrdpts= ',gfld%ngrdpts + print *,'Number of elements= gfld%igdtlen= ',gfld%igdtlen + print *,'PDT num= gfld%ipdtnum= ',gfld%ipdtnum + print *,'GDT num= gfld%igdtnum= ',gfld%igdtnum + endif + + imax = gfld%igdtmpl(8) + jmax = gfld%igdtmpl(9) + dx = float(gfld%igdtmpl(17))/1.e6 + dy = float(gfld%igdtmpl(17))/1.e6 + kf = gfld%ngrdpts + + holdgfld = gfld + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' SECTION 0: discipl= ',gfld%discipline + & ,' gribver= ',gfld%version + + print *,' ' + print *,' SECTION 1: ' + + do j = 1,gfld%idsectlen + print *,' sect1, j= ',j,' gfld%idsect(j)= ' + & ,gfld%idsect(j) + enddo + + if ( associated(gfld%local).AND.gfld%locallen.gt.0) then + print *,' ' + print *,' SECTION 2: ',gfld%locallen,' bytes' + else + print *,' ' + print *,' SECTION 2 DOES NOT EXIST IN THIS RECORD' + endif + + print *,' ' + print *,' SECTION 3: griddef= ',gfld%griddef + print *,' ngrdpts= ',gfld%ngrdpts + print *,' numoct_opt= ',gfld%numoct_opt + print *,' interp_opt= ',gfld%interp_opt + print *,' igdtnum= ',gfld%igdtnum + print *,' igdtlen= ',gfld%igdtlen + + print *,' ' + print '(a17,i3,a2)',' GRID TEMPLATE 3.',gfld%igdtnum,': ' + do j=1,gfld%igdtlen + print *,' j= ',j,' gfld%igdtmpl(j)= ',gfld%igdtmpl(j) + enddo + +c Get parameter abbrev for record that was retrieved + print *,' ' + print *,' PDT num (gfld%ipdtnum) = ',gfld%ipdtnum + print *,' ' + print '(a20,i3,a2)',' PRODUCT TEMPLATE 4.',gfld%ipdtnum,': ' + do j=1,gfld%ipdtlen + print *,' sect 4 j= ',j,' gfld%ipdtmpl(j)= ' + & ,gfld%ipdtmpl(j) + enddo + + endif + + + pdt_4p0_vtime = gfld%ipdtmpl(9) + pdt_4p0_vert_level = gfld%ipdtmpl(12) + + pabbrev=param_get_abbrev(gfld%discipline,gfld%ipdtmpl(1) + & ,gfld%ipdtmpl(2)) + + firstval=gfld%fld(1) + lastval=gfld%fld(kf) + + if (verb .ge. 3) then + print *,' ' + write (6,131) + 131 format (' rec# param level byy bmm bdd bhh ' + & ,'fhr npts firstval lastval') + print '(i5,3x,a8,2x,6i5,2x,i8,4g12.4)' + & ,krec,pabbrev,pdt_4p0_vert_level,gfld%idsect(6) + & ,gfld%idsect(7),gfld%idsect(8),gfld%idsect(9) + & ,pdt_4p0_vtime,gfld%ngrdpts,firstval,lastval +cPENG & ,krec,pabbrev,pdt_4p0_vert_level/100,gfld%idsect(6) + endif + + if (verb_g2 .ge. 1) then + + print *,' ' + print *,' -- END getgridinfo diagnostics for GRIB2 file ---' + print *,' -- at ifh= ',ifh + print *,' ' + print *,' ' + print *,' ' + + endif + + need_to_flip_lons = .false. + + iscanflag = gfld%igdtmpl(19) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(gfld%igdtmpl(12))/1.e6 + glatmax = float(gfld%igdtmpl(15))/1.e6 + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(gfld%igdtmpl(15))/1.e6 + glatmax = float(gfld%igdtmpl(12))/1.e6 + need_to_flip_lats = .false. + endif + + glonmin = float(gfld%igdtmpl(13))/1.e6 + glonmax = float(gfld%igdtmpl(16))/1.e6 + + if (verb .ge. 3) then + print *,'In getgridinfo: glatmin= ',glatmin + print *,' glatmax= ',glatmax + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + +c J.Peng---10/07/2019 to free the memory in reading GRIB2 data + call gf_free (gfld) + + else + + !------------------------------------------ + ! Search for a record from a GRIB1 file + !------------------------------------------ + + jpds = -1 + jgds = -1 + + jgds(1) = 0 ! Request a record that's on a lat/lon grid + + if ( verb .ge. 3 ) then + print *,'before getgb in getgridinfo, ifh= ',ifh + write (6,402) ifhours(ifh),ifclockmins(ifh) + 402 format (1x,'* Forecast hour: ',i4,':',i2.2) + print *,' ifhours(ifh)= ',ifhours(ifh) + print *,' iftotalmins(ifh)= ',iftotalmins(ifh) + endif + + ! Request a record at the current forecast lead time. + + if (inp%lt_units == 'minutes') then + jpds(14) = iftotalmins(ifh) + else + jpds(14) = ifhours(ifh) + endif + + j=0 + +c jpds(14) = 0 ! test +c + write(*,980) jpds(1),jpds(2) + write(*,981) jpds(3),jpds(4) + write(*,982) jpds(5),jpds(6) + write(*,983) jpds(7),jpds(8) + write(*,984) jpds(9),jpds(10) + write(*,985) jpds(11),jpds(12) + write(*,986) jpds(13),jpds(14) + write(*,987) jpds(15),jpds(16) + write(*,988) jpds(17),jpds(18) + write(*,989) jpds(19),jpds(20) + write(*,990) jpds(21),jpds(22) + write(*,991) jpds(23),jpds(24) + write(*,992) jpds(25) + write(*,880) jgds(1),jgds(2) + write(*,881) jgds(3),jgds(4) + write(*,882) jgds(5),jgds(6) + write(*,883) jgds(7),jgds(8) + write(*,884) jgds(9),jgds(10) + write(*,885) jgds(11),jgds(12) + write(*,886) jgds(13),jgds(14) + write(*,887) jgds(15),jgds(16) + write(*,888) jgds(17),jgds(18) + write(*,889) jgds(19),jgds(20) + write(*,890) jgds(21),jgds(22) + + 980 format(' jpds(1) = ',i7,' jpds(2) = ',i7) + 981 format(' jpds(3) = ',i7,' jpds(4) = ',i7) + 982 format(' jpds(5) = ',i7,' jpds(6) = ',i7) + 983 format(' jpds(7) = ',i7,' jpds(8) = ',i7) + 984 format(' jpds(9) = ',i7,' jpds(10) = ',i7) + 985 format(' jpds(11) = ',i7,' jpds(12) = ',i7) + 986 format(' jpds(13) = ',i7,' jpds(14) = ',i7) + 987 format(' jpds(15) = ',i7,' jpds(16) = ',i7) + 988 format(' jpds(17) = ',i7,' jpds(18) = ',i7) + 989 format(' jpds(19) = ',i7,' jpds(20) = ',i7) + 990 format(' jpds(21) = ',i7,' jpds(22) = ',i7) + 991 format(' jpds(23) = ',i7,' jpds(24) = ',i7) + 992 format(' jpds(25) = ',i7) + 880 format(' jgds(1) = ',i7,' jgds(2) = ',i7) + 881 format(' jgds(3) = ',i7,' jgds(4) = ',i7) + 882 format(' jgds(5) = ',i7,' jgds(6) = ',i7) + 883 format(' jgds(7) = ',i7,' jgds(8) = ',i7) + 884 format(' jgds(9) = ',i7,' jgds(10) = ',i7) + 885 format(' jgds(11) = ',i7,' jgds(12) = ',i7) + 886 format(' jgds(13) = ',i7,' jgds(14) = ',i7) + 887 format(' jgds(15) = ',i7,' jgds(16) = ',i7) + 888 format(' jgds(17) = ',i7,' jgds(18) = ',i7) + 889 format(' jgds(19) = ',i7,' jgds(20) = ',i7) + 890 format(' jgds(20) = ',i7,' jgds(22) = ',i7) + + print *,'lugb= ',lugb,' lugi= ',lugi + print *,'before ggi getgb jpds(14) = ',jpds(14) + print *,'before ggi getgb jgds(1) = ',jgds(1) + + call getgb(lugb,lugi,jf,j,jpds,jgds, + & kf,k,igetpds,igetgds,lb,f,iret) + + if (iret.ne.0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo calling getgb' + print *,'!!! Return code from getgb = iret = ',iret + endif + + iggret = iret + else + iggret=0 + imax = igetgds(2) + jmax = igetgds(3) + dx = float(igetgds(9))/1000. + dy = float(igetgds(10))/1000. + endif + +c write(*,780) igetpds(1),igetpds(2) +c write(*,781) igetpds(3),igetpds(4) +c write(*,782) igetpds(5),igetpds(6) +c write(*,783) igetpds(7),igetpds(8) +c write(*,784) igetpds(9),igetpds(10) +c write(*,785) igetpds(11),igetpds(12) +c write(*,786) igetpds(13),igetpds(14) +c write(*,787) igetpds(15),igetpds(16) +c write(*,788) igetpds(17),igetpds(18) +c write(*,789) igetpds(19),igetpds(20) +c write(*,790) igetpds(21),igetpds(22) +c write(*,791) igetpds(23),igetpds(24) +c write(*,792) igetpds(25) +c write(*,680) igetgds(1),igetgds(2) +c write(*,681) igetgds(3),igetgds(4) +c write(*,682) igetgds(5),igetgds(6) +c write(*,683) igetgds(7),igetgds(8) +c write(*,684) igetgds(9),igetgds(10) +c write(*,685) igetgds(11),igetgds(12) +c write(*,686) igetgds(13),igetgds(14) +c write(*,687) igetgds(15),igetgds(16) +c write(*,688) igetgds(17),igetgds(18) +c write(*,689) igetgds(19),igetgds(20) +c write(*,690) igetgds(21),igetgds(22) +c +c 780 format(' kpds(1) = ',i7,' kpds(2) = ',i7) +c 781 format(' kpds(3) = ',i7,' kpds(4) = ',i7) +c 782 format(' kpds(5) = ',i7,' kpds(6) = ',i7) +c 783 format(' kpds(7) = ',i7,' kpds(8) = ',i7) +c 784 format(' kpds(9) = ',i7,' kpds(10) = ',i7) +c 785 format(' kpds(11) = ',i7,' kpds(12) = ',i7) +c 786 format(' kpds(13) = ',i7,' kpds(14) = ',i7) +c 787 format(' kpds(15) = ',i7,' kpds(16) = ',i7) +c 788 format(' kpds(17) = ',i7,' kpds(18) = ',i7) +c 789 format(' kpds(19) = ',i7,' kpds(20) = ',i7) +c 790 format(' kpds(21) = ',i7,' kpds(22) = ',i7) +c 791 format(' kpds(23) = ',i7,' kpds(24) = ',i7) +c 792 format(' kpds(25) = ',i7) +c 680 format(' kgds(1) = ',i7,' kgds(2) = ',i7) +c 681 format(' kgds(3) = ',i7,' kgds(4) = ',i7) +c 682 format(' kgds(5) = ',i7,' kgds(6) = ',i7) +c 683 format(' kgds(7) = ',i7,' kgds(8) = ',i7) +c 684 format(' kgds(9) = ',i7,' kgds(10) = ',i7) +c 685 format(' kgds(11) = ',i7,' kgds(12) = ',i7) +c 686 format(' kgds(13) = ',i7,' kgds(14) = ',i7) +c 687 format(' kgds(15) = ',i7,' kgds(16) = ',i7) +c 688 format(' kgds(17) = ',i7,' kgds(18) = ',i7) +c 689 format(' kgds(19) = ',i7,' kgds(20) = ',i7) +c 690 format(' kgds(20) = ',i7,' kgds(22) = ',i7) + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,' dx= ',dx,' dy= ',dy + endif + + +c ---------------------------------------------------------------- +c Get boundaries of the data grid. NOTE: gds(4) is referred to in +c GRIB documenatation as the "Latitude of origin", which might +c imply "minimum Latitude". However, for the grids that we'll be +c using in this program, the "Latitude of origin" will be listed +c under gds(4) as the northernmost point (eg., in MRF, +c gds(4) = 90), so for this program, use gds(4) as your max lat, +c and gds(7) as your min lat. However, in case NCEP, UKMET or +c ECMWF change their convention and begin flipping their grids, a +c check is made to make sure that the max lat is not less than the +c min lat. +c +c BUGFIX (August, 2001): It is possible to have an input grid +c which goes from south to north (such as NAVGEM). In this case, +c we flip the data in subroutine conv1d2d_real. However, the max +c and min latitudes listed in the GRIB GDS will be confused, so we +c need to check the value of the GRIB scanning mode flag here. + + need_to_flip_lons = .false. + + iscanflag = igetgds(11) + if (mod(iscanflag,128) >= 64) then + ! Input data is south to north... + glatmin = float(igetgds(4))/1000. + glatmax = float(igetgds(7))/1000. + need_to_flip_lats = .true. + else + ! Input data is north to south... + glatmin = float(igetgds(7))/1000. + glatmax = float(igetgds(4))/1000. + need_to_flip_lats = .false. + endif + + glonmin = float(igetgds(5))/1000. + glonmax = float(igetgds(8))/1000. + + endif + +c After this point in this subroutine, nothing is GRIB1 / GRIB2 +c specific, so it does not need to be within the if/then +c statement above that differentiated between GRIB / GRIB2. + +c17Jul2014 if (glonmin < 0.0) glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax < 0.0) glonmax = 360. - abs(glonmax) + + if (glonmin >= 0.0 .and. glonmax >= 0.0) then + if (glonmin > glonmax) then + if (verb .ge. 3) then + print *,' ' + print *,'ERROR: Badly notated longitude boundaries in ' + print *,' GRIB PDS, because the min longitude ' + print *,' (glonmin) is greater than the max ' + print *,' longitude (glonmax) where both longitudes' + print *,' are greater than 0.' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + print *,' !!! STOPPING....' + stop 98 + endif + endif + elseif (glonmin < 0.0 .and. glonmax >= 0.0) then + ! An example of this is the MPAS data, which starts and ends + ! at the dateline and is specified as glonmin=-179.875, + ! glonmax=179.875. Convert to be positive and go from + ! 180.125 to 539.875. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0, glonmax > 0, so glonmin' + print *,' will be converted to be > 0 and 360 will' + print *,' be added to glonmax.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. + abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin < 0.0 .and. glonmax < 0.0) then + ! Examples of this are GFDL and HWRF. In this case, make + ! both glonmin and glonmax positive. + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is < 0 and glonmax < 0, so both' + print *,' will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmin = 360. - abs(glonmin) + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + elseif (glonmin >= 0.0 .and. glonmax < 0.0) then + ! An example of this is the GFS data, which goes from + ! glonmin=0.0 to glonmax=-0.5. Convert it here to go + ! from glonmin=0.0 to glonmax=359.5 + if (verb .ge. 3) then + print *,' ' + print *,'NOTE: glonmin is >= 0 and glonmax < 0, so' + print *,' glonmax will be converted to be > 0.' + print *,' BEFORE CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + glonmax = 360. - abs(glonmax) + if (verb .ge. 3) then + print *,' AFTER CONVERSION: ' + print *,' glonmin= ',glonmin + print *,' glonmax= ',glonmax + endif + endif + +c17Jul2014 if (glonmin < 0.0) then +c17Jul2014 glonmin = 360. - abs(glonmin) +c17Jul2014 if (glonmax <= 0.0) then +c17Jul2014 glonmax = 360. - abs(glonmax) +c17Jul2014 else +c17Jul2014 glonmax = 360 + abs(glonmax) +c17Jul2014 endif +c17Jul2014 endif + + if (glatmax < glatmin) then + temp = glatmax + glatmax = glatmin + glatmin = temp + endif + + if (glonmin > 200.0 .and. glonmin <= 360.) then + if (glonmax < 50.) then + ! Likely GM-wrapping in current record + glonmax = glonmax + 360. + endif + endif +c + if ( verb .ge. 3 ) then + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + print *,' ' + print *,'NOTE: For regional grids, valid data points might' + print *,'NOT extend all the way to the gds-defined grid ' + print *,'boundary, due to the fact that data have been ' + print *,'interpolated from a NPS or Lamb-Conf grid onto a ' + print *,'lat/lon grid. This program checks the logical ' + print *,'bitmap for valid data points, but just keep this in' + print *,'mind if trying to debug errors that occur near the' + print *,'grid boundaries for regional models.' + endif + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glat)) deallocate(glat) + if (allocated(glon)) deallocate(glon) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + endif + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + + iggret = 96 + return + endif + + do j=1,jmax + glat(j) = glatmax - (j-1)*dy + enddo + do i=1,imax + glon(i) = glonmin + (i-1)*dx + enddo + + if (allocated(lb)) deallocate (lb,stat=ila) + if (allocated(f)) deallocate (f,stat=ifa) + + if (ila /= 0 .or. ifa /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo deallocating arrays.' + print *,'!!! ila= ',ila,' ifa= ',ifa + print *,'!!! EXITING....' + stop 98 + endif + +c -------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have +c forgotten to change the input grid bounds from a global grid +c run). Modify the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + + if ( verb .ge. 3 ) then + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine getgridinfo_netcdf (ncfile_id,imax,jmax,dx,dy + & ,trkrinfo,need_to_flip_lats,need_to_flip_lons + & ,inp,netcdfinfo,iggret) +c +c ABSTRACT: The purpose of this subroutine is just to get the max +c values of i and j and the dx and dy grid spacing intervals for the +c grid to be used in the rest of the program. So just query the +c netcdf file to get the lon and lat data. Also, get the info for +c the data grid's boundaries. This boundary information will be +c used later in the tracking algorithm, and is accessed via Module +c grid_bounds. +c + USE grid_bounds; USE trkrparms; USE inparms + USE verbose_output; USE netcdf_parms + + implicit none +c + type (trackstuff) trkrinfo + type (netcdfstuff) netcdfinfo + type (datecard) inp + + logical(1) :: need_to_flip_lats,need_to_flip_lons + real xhold,xlondiff,xlatdiff + real, allocatable :: tmplon(:),tmplat(:) + real, intent(out) :: dx,dy + integer iscanflag,iggret + integer, intent(in) :: ncfile_id + integer, intent(out) :: imax,jmax + integer :: iia,ija,midi,midj,i,j,iix,jix +c + + iggret = 0 + + call get_ncdim1(ncfile_id,netcdfinfo%lon_name,imax) + call get_ncdim1(ncfile_id,netcdfinfo%lat_name,jmax) + + if (allocated(tmplon)) deallocate (tmplon) + if (allocated(tmplat)) deallocate (tmplat) + allocate (tmplon(imax),stat=iia) + allocate (tmplat(jmax),stat=ija) + if (iia /= 0 .or. ija /= 0) then + print *,' ' + print *,'!!! ERROR in sub getgridinfo_netcdf allocating arrays.' + print *,'!!! iia = ',iia,' ija= ',ija + iggret = 94 + return + endif + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + endif + + call get_var1_double (ncfile_id,netcdfinfo%lon_name,imax,tmplon) + call get_var1_double (ncfile_id,netcdfinfo%lat_name,jmax,tmplat) + +c Compute the dx and dy by picking values out of the middle of +c the lat and lon arrays.... + + midi = imax/2 + midj = jmax/2 + + dx = abs(tmplon(midi) - tmplon(midi-1)) + dy = abs(tmplat(midj) - tmplat(midj-1)) + + if (verb .ge. 1) then + print *,' ' + print *,'In getgridinfo, grid dimensions follow:' + print *,'imax= ',imax,' jmax= ',jmax + print *,'dx= ',dx,' dy= ',dy + print *,' ' + write (6,112) midi,dx + write (6,113) midj,dy + + 112 format(1x,' DX: midi= ',i4,' dx= ',f8.4) + 113 format(1x,' DY: midj= ',i4,' dy= ',f8.4) + endif + + +c ------------------------------------------------------------------ +c Get boundaries of the data grid. Note that it is possible to have +c an input grid which goes from south to north (in fact, it appears +c that many NetCDF files are constructed this way). Keep in mind, +c however, that the tracker has been written such that point (1,1) +c should be the upper-leftmost point on the grid, while point +c (imax,jmax) should be the lower-rightmost point. If we check and +c find that we're dealing with data that instead starts from the +c south and increases northward, we flip the data in subroutine +c conv1d2d_real. Similarly here, we make sure to test so that when +c we are done in this routine, glatmax refers to the northernmost +c latitude and glatmin the southernmost latitude. + + if (tmplon(imax) > tmplon(1)) then + glonmin = tmplon(1) + glonmax = tmplon(imax) + else + glonmin = tmplon(imax) + glonmax = tmplon(1) + endif + + if (tmplat(1) > tmplon(jmax)) then + glatmax = tmplat(1) + glatmin = tmplat(jmax) + else + glatmax = tmplat(jmax) + glatmin = tmplat(1) + endif + + print *,' ' + print *,'Data Grid Lat/Lon boundaries follow:' + write (6,81) glatmin,glonmin + 81 format (' Min Lat: ',f8.3,' Min Lon: ',f8.3) + write (6,83) glatmax,glonmax + 83 format (' Max Lat: ',f8.3,' Max Lon: ',f8.3) + +c ---------------------------------------------------------------- +c Fill glat and glon with the lat & lon values for the grid. This +c info will be used in subroutine barnes + + if (allocated(glon)) deallocate (glon) + if (allocated(glat)) deallocate (glat) + + allocate (glat(jmax),stat=ija) + allocate (glon(imax),stat=iia) + if (ija /= 0 .or. iia /= 0) then + print *,' ' + print *,'!!! ERROR in getgridinfo allocating glon or glat' + print *,'!!! ija = ',ija,' iia= ',iia + iggret = 96 + return + endif + + ! If the lat or lon grids are flipped (i.e., the lats increase + ! from south to north, or the lons increase westward), then we + ! will need to flip both the data arrays as well as the arrays + ! that are holding the values of the lats and lons.... + + need_to_flip_lats = .false. + need_to_flip_lons = .false. + + if (tmplat(1) > tmplon(jmax)) then + do j=1,jmax + glat(j) = tmplat(j) + enddo + else + do j=1,jmax + jix = jmax - j + 1 + glat(jix) = tmplat(j) + enddo + need_to_flip_lats = .true. + endif + + if (tmplon(imax) > tmplon(1)) then + do i=1,imax + glon(i) = tmplon(i) + enddo + else + do i=1,imax + iix = imax - i + 1 + glon(iix) = tmplon(i) + enddo + need_to_flip_lons = .true. + endif + +c do i = 1,imax +c print *,'i= ',i,' glon(i)= ',glon(i) +c enddo +c do j = 1,jmax +c print *,'j= ',j,' glat(j)= ',glat(j) +c enddo + +c --------------------------------------------------------------- +c Finally, check to see if the requested boundary limits that +c the user input are contained within this grid (for example, +c someone running this tracker on a regional grid may have forgotten +c to change the input grid bounds from a global grid run). Modify +c the user-input bounds as needed. +c +c NOTE: Only check these bounds for a genesis run on a regional +c grid, whether that be a 'midlat' or a 'tcgen' run. + + if (trkrinfo%gridtype == 'regional' .and. + & trkrinfo%type /= 'tracker') then + + if (trkrinfo%eastbd > glonmax) then + xhold = trkrinfo%eastbd + trkrinfo%eastbd = glonmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'EASTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%eastbd + write (6,91) + endif + + if (trkrinfo%westbd < glonmin) then + xhold = trkrinfo%westbd + trkrinfo%westbd = glonmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'WESTERN LONGITUDE' + write (6,98) xhold + write (6,99) trkrinfo%westbd + write (6,91) + endif + + if (trkrinfo%northbd > glatmax) then + xhold = trkrinfo%northbd + trkrinfo%northbd = glatmax - 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'NORTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%northbd + write (6,91) + endif + + if (trkrinfo%southbd < glatmin) then + xhold = trkrinfo%southbd + trkrinfo%southbd = glatmin + 5.0 + write (6,90) + write (6,91) + write (6,92) + write (6,93) + write (6,94) + write (6,95) + write (6,96) + write (6,97) 'SOUTHERN LATITUDE' + write (6,98) xhold + write (6,99) trkrinfo%southbd + write (6,91) + endif + + endif + + 90 format (///) + 91 format (' *********************************************') + 92 format (' WARNING: A USER-REQUESTED BOUNDARY IS BEYOND') + 93 format (' THE BOUNDARY OF THE DATA, AS DEFINED IN THE ') + 94 format (' GRIB FILE. THE USER BOUNDARY WILL BE MODIFIED') + 95 format (' TO MATCH THE BOUNDARY OF THE DATA FILE.') + 96 format (' ') + 97 format (' USER-INPUT BOUNDARY AT FAULT: ',A20) + 98 format (' USER-INPUT BOUNDARY VALUE: ',f8.2) + 99 format (' NEW BOUNDARY VALUE: ',f8.2) + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine read_netcdf_hours (ncfile,ncfile_id,ncfile_tmax,ifhmax + & ,ncfile_has_hour0,netcdfinfo,irnhret) +c +c ABSTRACT: The purpose of this subroutine is to read the "time" +c dimension and "time data" from the NetCDF file so that we know +c how many time levels there are and what those time levels are. +c One reason for doing this is that some models, like the GFDL +c FV3, do not output hour 0 data, so we need to check this first +c before running through the tracking processing for the various +c hours. We will take the list of hours read in here directly from +c the NetCDF file and compare that against the *requested* list of +c forecast hours that the user has entered. The user might not be +c aware that there is no hour 0 data for a given model. We compare +c these two lists of forecast hours and then write a message if +c there is a lead time that is not in the NetCDF file. +c +c INPUT: +c ncfile character name of NetCDF file +c ncfile_id integer id associated with NetCDF file after open +c ifhmax integer max number of lead times that the user has +c requested on the input lead times data file. This +c value was set in subroutine read_fhours. +c netcdfinfo variable of user-defined type netcdfstuff (from +c module netcdf_parms). +c +c OUTPUT: +c ncfile_tmax integer max number of lead times that are in the +c NetCDF file, as read in from this subroutine +c ncfile_has_hour0 character flag (y|n) that tells whether or not +c the input NetCDF data file actually has an hour0 +c record in it or not. +c + USE netcdf_parms; USE tracked_parms; USE verbose_output + + implicit none +c + type (netcdfstuff) netcdfinfo + + character :: ncfile*180,ncfile_has_hour0*1,match_check*1 + integer, intent(in) :: ncfile_id + integer, intent(out) :: ncfile_tmax + integer :: infta,k,m,n,ifhmax,irnhret,usertime +c + + irnhret = 0 + ncfile_has_hour0 = 'n' + + !----------------------------------------------------------- + ! First read the NetCDF file to get the number of time levels, + ! which will be returned in "ncfile_tmax".... + !----------------------------------------------------------- + + print *,' ' + print *,'in read_netcdf_hours...' + print *,'ncfile_id= ',ncfile_id + print *,'netcdfinfo%time_name= ',netcdfinfo%time_name + print *,'ncfile_tmax= ',ncfile_tmax + + call get_ncdim1(ncfile_id,netcdfinfo%time_name,ncfile_tmax) + + if (verb .ge. 1) then + print *,'in getgridinfo_netcdf, ncfile_id= ',ncfile_id + print *,'Num netcdf time levs= ncfile_tmax= ',ncfile_tmax + endif + + if (allocated(netcdf_file_time_values)) then + deallocate (netcdf_file_time_values) + endif + + allocate (netcdf_file_time_values(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating' + print *,'!!! netcdf_file_time_values array. infta = ',infta + irnhret = 94 + return + endif + + + !----------------------------------------------------------- + ! Now read in the actual time values that are stored in the + ! NetCDF file.... + !----------------------------------------------------------- + + call get_var1_double (ncfile_id,netcdfinfo%time_name,ncfile_tmax + & ,netcdf_file_time_values) + + if (verb .ge. 1) then + do k = 1,ncfile_tmax + print *,'k= ',k,' netcdf_file_time_values(k)= ' + & ,netcdf_file_time_values(k) + enddo + endif + + !------------------------------------------------------------ + ! Now convert the NetCDF time values into minutes in order to + ! be able to compare with the user-requested list of lead + ! times. Remember that the NetCDF lead times will be listed + ! either as hours or as fractions of days. + !------------------------------------------------------------ + + if (allocated(nctotalmins)) then + deallocate (nctotalmins) + endif + + allocate (nctotalmins(ncfile_tmax),stat=infta) + if (infta /= 0) then + print *,' ' + print *,'!!! ERROR in sub read_netcdf_hours allocating ' + print *,'!!! nctotalmins array. infta = ',infta + irnhret = 94 + return + endif + + do k = 1,ncfile_tmax + + if (netcdfinfo%time_units == 'hours') then + nctotalmins(k) = int(netcdf_file_time_values(k)) * 60 + elseif (netcdfinfo%time_units == 'days') then + nctotalmins(k) = int(netcdf_file_time_values(k) * 60. * 24.) + else + print *,' ' + print *,'!!! ERROR: In read_netcdf_hours, the value of' + print *,' netcdfinfo%time_units is neither hours nor days.' + print *,' netcdfinfo%time_units= ',netcdfinfo%time_units + print *,' STOPPING....' + print *,' ' + stop 99 + endif + + if (verb .ge. 1) then + write (6,71) k,netcdf_file_time_values(k),nctotalmins(k) + endif + + enddo + + 71 format (1x,i5,' netcdf_file_time_values(k)= ',f8.4 + & ,' nctotalmins(k)= ',i10) + + !------------------------------------------------------------ + ! Now go through the list of user-requested lead times that + ! were read in from subroutine read_fhours and try to match + ! the two lists up. The big one to watch out for is whether + ! or not the NetCDF file actually has an hour 0 lead time. + !------------------------------------------------------------ + + userloop: do n = 1,ifhmax + + usertime = iftotalmins(n) + + match_check = 'n' + + netcdfloop: do m = 1,ncfile_tmax + + if (usertime == nctotalmins(m)) then + if (verb .ge. 1) then + print *,'+++ Time match for usertime= ',usertime + endif + match_check = 'y' + endif + + enddo netcdfloop + + if (match_check == 'n') then + + if (usertime == 0) then + print *,' ' + print *,'Warning: For a NetCDF file, the user has requested' + print *,'to read in an hour 0 file, however a scan of the' + print *,'time data values in the NetCDF file indicates' + print *,'that there is no hour 0 data in this file. ' + print *,'We will substitute either missing values or ' + print *,'the values from the TC Vitals data in the ' + print *,'hour 0 record and then start searching at the ' + print *,'next lead time.' + ncfile_has_hour0 = 'n' + else + print *,' ' + print *,'!!! ERROR: For a NetCDF file, the user has' + print *,' requested to process a particular lead time that' + print *,' does not exist in the NetCDF list of time ' + print *,' values.' + print *,' n= ',n + print *,' usertime= iftotalmins(n)= ',iftotalmins(n) + print *,' STOPPING....' + stop 99 + endif + + elseif (match_check == 'y') then + + if (usertime == 0) then + if (verb .ge. 1) then + print *,' ' + print *,'+++ For the input NetCDF file, an hour0 data ' + print *,' record exists in the data file.' + endif + ncfile_has_hour0 = 'y' + endif + + endif + + enddo userloop +c + return + end +c +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_valid_point (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt,grid_maxlat,grid_minlat,grid_maxlon + & ,grid_minlon,trkrinfo,icvpret) +c +c ABSTRACT: This subroutine checks to see if the input lat/lon +c point is associated with four surrounding (i,j) locations that +c have valid data. The writing of this routine was prompted by the +c HFIP project in February, 2009. Some of their high resolution +c data for their inner nests contained grids that had been rotated +c from native map projections to regular lat/lon grids, but that +c rotation left "empty" spots on the lat/lon grid where there is +c no data. Then when searching in find_maxmin, we were running +c barnes iterations from these lat/lon locations where there was +c no data, which would give artificially low values at those +c lat/lon locations (because the barnes scheme would only include +c points that were relatively far away where there was valid data). +c So in this routine, we call subroutine fix_latlon_to_ij in order +c to get the nearest (i,j) coordinates, and then we check all of +c these points to make sure that valid data exist. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing in i-direction +c dy grid spacing in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c rlatt,rlont input lat/lon about which we will check the +c surrounding (i,j) locations for valid data. +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c icvpret return code from this routine. A value of 0 means that +c all is okay and the input point is surrounded by valid +c data. + + USE trkrparms + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,ifix,jfix + integer ifilret,icvpret + character(*) cmaxmin + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real rlont,rlatt,xdum,gridpoint_maxmin + real dx,dy,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon +c + call fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,rlont,rlatt + & ,xdum,ifix,jfix,gridpoint_maxmin,'checker' + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) + + if (ifilret /= 0) then + icvpret = 99 + return + endif + + if (valid_pt(ifix,jfix)) then + icvpret = 0 + else + icvpret = 99 + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine fix_latlon_to_ij (imax,jmax,dx,dy,fxy,cmaxmin + & ,valid_pt,parmlon,parmlat,xdataval + & ,ifix,jfix,gridpoint_maxmin,ccall + & ,grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + & ,trkrinfo,ifilret) +c +c ABSTRACT: This subroutine takes an input lat/lon position and +c assigns it to a nearby (i,j) gridpoint. If this is being used +c before the call to check_closed_contour after the barnes analysis +c to see if we have a storm or not, then the lat/lon position that +c is input into this subroutine is one which was obtained from a +c barnes analysis, so it is essentially an area-weighted average +c of nearby points. What we need to do in this subroutine is find +c the actual nearby gridpoint which does have the actual raw max or +c min value. Then we return the (i,j) coordinates of that point as +c well as that raw data value at that point. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c dx grid spacing of the data grid in i-direction +c dy grid spacing of the data grid in j-direction +c fxy real array of input data values +c cmaxmin character that tells if searching for max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c parmlon lon at which input parameter center was found, or the lon +c for the mean storm center fix (check calling routine) +c parmlat lat at which input parameter center was found, or the lat +c for the mean storm center fix (check calling routine) +c xdataval barnes-obtained value of parameter at (parmlon,parmlat) +c ccall character that tells if this call is part of a tracker +c fix routine or just from the check_valid_point routine +c ('tracker' or 'checker') +c grid_maxlat northernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlat southernmost latitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_maxlon easternmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c grid_minlon westernmost longitude on the input grid being sent to +c this routine. This grid may be a subset of the original +c full grid from the original dataset, or it may be the +c original grid itself. +c trkrinfo derived type containing grid info on user boundaries +c +c OUTPUT: +c ifix i-index for gridpoint to which the max or min is assigned +c jfix j-index for gridpoint to which the max or min is assigned +c gridpoint_maxmin value of fxy at (ifix,jfix). This will be +c different from the input value xdataval, which came from +c the barnes averaging. This is the raw value at the +c gridpoint. + + USE grid_bounds; USE trkrparms + USE verbose_output + + implicit none +c + type (trackstuff) trkrinfo + + integer imax,jmax,istart,iend,jstart,jend,ifix,jfix + integer ipfix,jpfix,i,j,ifilret,iix,jix,grfact + character(*) cmaxmin,ccall + logical(1) valid_pt(imax,jmax) + real fxy(imax,jmax) + real parmlon,parmlat,xdataval,gridpoint_maxmin + real xplon,yplat,dmin,dmax,dx,dy,grdspc + real grid_maxlat,grid_minlat,grid_maxlon,grid_minlon + + ifilret = 0 + +c print *,' ' +c print *,'-------------------------------------------- ' +c print *,'Top of fix_latlon_to_ij, call type = ',ccall +c print *,'parmlon= ',parmlon,' parmlat= ',parmlat +c print *,'max lon = ',grid_maxlon,' max lat = ',grid_maxlat +c print *,'min lon = ',grid_minlon,' min lat = ',grid_minlat + +c Fix parmlat to the *nearest* j-point (i.e., round it....) + + if (parmlat >= 0.0) then ! N. Hemisphere + jpfix = int((grid_maxlat - parmlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jpfix = ceiling((grid_maxlat - parmlat)/dy + 1.0 - 0.5) + endif + +c Fix parmlon to the *nearest* i-point (i.e., round it....) + + ipfix = int((parmlon - grid_minlon)/dx + 1.0 + 0.5) + +c Calculate the longitude and latitude of these ipfix and +c jpfix points.... + + xplon = grid_minlon + (ipfix-1)*dx + yplat = grid_maxlat - (jpfix-1)*dy + +c We want to do a simple search in the very few points around +c this (ipfix,jpfix) point to find the raw max or min data +c value. First we need to set up a 4x4 box to search: +c +c o o o o +c +c +c o a b o +c + +c +c o c d o +c +c +c o o o o +c +c In the above diagram, if "+" is the lat/lon location of our +c barnes-found point (i.e., the input (parmlon,parmlat)), and +c a-b-c-d is the square of points surrounding "+", we only want +c to look out 1 layer of points further. So first we need to +c know, for each case we're looking at, if "+" got assigned to +c a or b or c or d. By the way, if the parmlon falls directly +c on a gridpoint in either the i or j direction, we will only +c look at the 2 gridpoints on either side of that point, as +c opposed to having 4 points set up as in the box above. +c +c UPDATE (4-Feb-2011): For fine resolution grids, it is +c possible to have the gridpoint max/min be more than 1 or 2 grid +c points away from the barnes-averaged max. So allow for this +c here, with a check of grdspc ((dx+dy)/2) below and the +c addition of the "grfact" multiplier for fine resolution grids. + +c print *,'ipfix= ',ipfix,' xplon= ',xplon +c print *,'jpfix= ',jpfix,' yplat= ',yplat + + grdspc = (dx+dy)*0.5 + if (grdspc <= 0.025) then + grfact = 20 + else if (grdspc > 0.025 .and. grdspc <= 0.05) then + grfact = 12 + else if (grdspc > 0.05 .and. grdspc <= 0.10) then + grfact = 6 + else if (grdspc > 0.10 .and. grdspc <= 0.20) then + grfact = 3 + else if (grdspc > 0.20 .and. grdspc <= 0.30) then + grfact = 2 + else + grfact = 1 + endif + + if (xplon < parmlon) then !(ipfix is at either a or c) + istart = ipfix - (1*grfact) + iend = ipfix + (2*grfact) + else if (xplon > parmlon) then !(ipfix is at either b or d) + istart = ipfix - (2*grfact) + iend = ipfix + (1*grfact) + else if (xplon == parmlon) then !(parmlon is exactly ipfix) + istart = ipfix - (1*grfact) + iend = ipfix + (1*grfact) + endif + + if (yplat < parmlat) then !(jpfix is at either c or d) + jstart = jpfix - (2*grfact) + jend = jpfix + (1*grfact) + else if (yplat > parmlat) then !(jpfix is at either a or b) + jstart = jpfix - (1*grfact) + jend = jpfix + (2*grfact) + else if (yplat == parmlat) then !(parmlat is exactly jpfix) + jstart = jpfix - (1*grfact) + jend = jpfix + (1*grfact) + endif + +c print *,'istart= ',istart,' iend= ',iend +c print *,'jstart= ',jstart,' jend= ',jend +c print *,' ' + +c Make sure the edges of our box are within the grid bounds... + + if (jstart > jmax ) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, jstart > jmax' + print *,'!!! ',ccall,' jstart = ',jstart,' jmax= ',jmax + endif + + + ifilret = 99 + return + endif + if (jend < 1) then + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, ',ccall + & ,' jend < 1, jend = ',jend + endif + + ifilret = 99 + return + endif + if (jstart < 1) jstart = 1 + if (jend > jmax) jend = jmax + + if (istart > imax ) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, istart > imax' + print *,'!!! istart = ',istart,' imax= ',imax + endif + + ifilret = 99 + return + endif + endif + + if (iend < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in fix_latlon_to_ij, iend < 1, iend = ' + & ,iend,' call type = ',ccall + endif + + ifilret = 99 + return + endif + endif + + if (iend > imax) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + iend = imax ! For a regional grid, just cut it off + endif + endif + + if (istart < 1) then + if (trkrinfo%gridtype == 'global') then + continue ! GM wrapping will be handled in loop below... + else + istart = 1 ! For a regional grid, just cut it off + endif + endif + +c Now look for the max or min value.... + + dmax = -9.99e12 + dmin = 9.99e12 + ifix = ipfix + jfix = jpfix + + do iix = istart,iend + do jix = jstart,jend + + i = iix + j = jix + + if (i < 1) then + i = iix + imax !GM wrapping + endif + if (i > imax) then + i = iix - imax !GM wrapping + endif + + if (valid_pt(i,j)) then + continue + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In fix_latlon_to_ij, we tried to ' + print *,'!!! access an invalid data point.' + print *,'!!! ',ccall,' i= ',i,' j= ',j + print *,'!!! ipfix= ',ipfix,' jpfix= ',jpfix + print *,'!!! parmlon= ',parmlon,' parmlat= ',parmlat + print *,' ' + endif + + ifilret = 98 + return + endif + + if (cmaxmin == 'min') then + if (fxy(i,j) < dmin) then + dmin = fxy(i,j) + ifix = i + jfix = j + endif + else + if (fxy(i,j) > dmax) then + dmax = fxy(i,j) + ifix = i + jfix = j + endif + endif + + enddo + enddo + + if (cmaxmin == 'min') then + gridpoint_maxmin = dmin + else + gridpoint_maxmin = dmax + endif + +c print *,' End of fix_latlon_to_ij, gridpoint_maxmin = ' +c & ,gridpoint_maxmin + +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine rvcal (imax,jmax,dlon,dlat,z,vp) +c +c ABSTRACT: This routine calculates the relative vorticity (zeta) +c from u,v on an evenly-spaced lat/lon grid. Centered finite +c differences are used on the interior points and one-sided +c differences are used on the boundaries. +c +c NOTE: There are 3 critical arrays in this subroutine, the first +c being zeta and the 2nd and 3rd being u and v. There is a +c critical difference in the array indexing for the levels. For +c zeta, the array is dimensioned with levels from 1 to 3, with +c 1 = 850, 2 = 700, 3 = sfc. However, there is an extra level +c for the winds, such that the level dimension goes 1 = 850, +c 2 = 700, 3 = 500, 4 = sfc. So we need to adjust for that in +c this routine. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE trig_vals; USE grid_bounds + USE verbose_output + + implicit none + + dimension cosfac(jmax),tanfac(jmax) + real tmpzeta(imax,jmax) + real xlondiff,xlatdiff,dlon,dlat,dfix + real dlat_edge,dlat_inter,dlon_edge,dlon_inter + real rlat(jmax),cosfac,tanfac + integer z,iscanflag,nlat,nlon,i,j,imax,jmax,w + integer ii,jj + logical(1) vp(imax,jmax) + +c -------------------------- + +c Figure out what level of data we have and what the array +c indices should be. + + if (z == 1) then + ! z = 1 for 850 mb zeta, w = 1 for 850 mb winds + w = 1 + else if (z == 2) then + ! z = 2 for 700 mb zeta, w = 2 for 700 mb winds + w = 2 + else if (z == 3) then + ! z = 3 for sfc zeta, w = 4 for sfc (10m) winds + w = 4 + endif + +c Calculate grid increments for interior and edge points. + +c IMPORTANT: If dtk is defined in module trig_vals in km, then +c we need to multiply by 1000 here to get meters. If it's defined +c as meters, just let it be. Since the wind values are given in +c meters, that's why we need the dlon values to be in meters. + + if (dtk < 750.) then ! chances are, dtk was defined as km + dfix = 1000.0 + else ! dtk was already defined as meters + dfix = 1.0 + endif + + dlon_edge = dtk * dfix * dlon ! Di dist over 1 grid pt + dlat_edge = dtk * dfix * dlat ! Dj dist over 1 grid pt + dlon_inter = dtk * dfix * 2.0 * dlon ! Di dist over 2 grid pts + dlat_inter = dtk * dfix * 2.0 * dlat ! Dj dist over 2 grid pts + + +c Calculate required trig functions. These are functions of +c latitude. Remember that the grid must go from north to south. +c This north-to-south requirement has +c already been checked in subroutine getgridinfo. If necessary, +c any flipping of the latitudes was done there, and flipping of +c the data, again if necessary, was done in subroutine getdata. + + do j=2,jmax-1 + rlat(j) = glatmax - ((j-1) * dlat) + cosfac(j) = cos(dtr*rlat(j)) + tanfac(j) = (tan(dtr*rlat(j)))/erad + enddo + +c Set trig factors at end points to closest interior point +c to avoid a singularity if the domain includes the poles, +c which it will for the global grids (MRF, GDAS, GFS, UKMET,NCE) + + cosfac(1) = cosfac(2) + tanfac(1) = tanfac(2) + cosfac(jmax) = cosfac(jmax-1) + tanfac(jmax) = tanfac(jmax-1) + +c NOTE: These next bits of vorticity calculation code assume that +c the input grid is oriented so that point (1,1) is the upper +c left-most (NW) and point (imax,jmax) is the lower right- +c most point. Any other grids will probably crash the +c program due to array out of bounds errors. +c NOTE: Before each calculation is done, the logical array is +c checked to make sure that all the data points in this +c calculation have valid data (ie., that the points are not +c outside a regional model's boundaries). +c +c !!! IMPORTANT NOTE: While testing this, I uncovered a bug, which was +c that I had the "j+1" and "j-1" reversed. Just from a physical +c understanding, the du/dy term at a point is calculated by taking +c the u value north of the point minus the u value south of the +c point. Intuitively, this is u(j+1) - u(j-1). However, we have +c designed this program to have the northernmost point as +c the beginning of the grid (i.e., for the global grids, j=1 at 90N, +c and j increases southward). Thus, if you would do u(j+1) - +c u(j-1), you would actually be taking the u value south of the +c point minus the u value north of the point, EXACTLY THE OPPOSITE +c OF WHAT YOU WANT. Therefore, the vorticity calculations have +c been changed so that we now have u(j-1) - u(j+1). +c +c UPDATE FEB 2009: With limited domain grids that have missing +c data on them (such as you would have for a grid that has been +c converted from a non-lat/lon grid to a lat/lon grid), we were +c running into problems below with the setting of zeta values to +c a missing value of -999. In place of this, the easiest thing to +c do is to simply assign a value of the background coriolis value +c to that point. No, this is not correct, but it is the easiest +c workaround for this right now. Setting it to zero would be too +c far off. Setting it to the coriolis component has a net effect +c of not having much impact on the barnes scheme result. +c +c --------------- +c Interior points +c --------------- + + if ( verb .ge. 3 ) then + print *,'Just before inter rvcalc, dlon_inter = ',dlon_inter + & ,' dlat_inter = ',dlat_inter + endif + + do j=2,jmax-1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1) .and. vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo + enddo +c +c ----------------------------- +c Bottom (Southernmost) points +c ----------------------------- +c + j=jmax + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j-1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j-1,w) - u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------- +c Top (Northernmost) points +c -------------------------- +c + j=1 + do i=2,imax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i-1,j) .and. + & vp(i,j+1)) then +c + zeta(i,j,z)= (v(i+1,j,w) - v(i-1,j,w))/(dlon_inter * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c ------------------------------- +c Left edge (Westernmost) points +c ------------------------------- +c + i=1 + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c -------------------------------- +c Right edge (Easternmost) points +c -------------------------------- +c + i=imax + do j=2,jmax-1 +c + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) .and. + & vp(i,j-1)) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w) - u(i,j+1,w))/(dlat_inter) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + enddo +c +c --------- +c SW corner +c --------- + i=1 + j=jmax + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i+1,j,w)-v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NW corner +c --------- + i=1 + j=1 + if (vp(i,j) .and. vp(i+1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i+1,j,w) - v(i,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c NE corner +c --------- + i=imax + j=1 + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j+1) ) then +c + zeta(i,j,z) = (v(i,j,w) - v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j,w) - u(i,j+1,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c +c --------- +c SE corner +c --------- + i=imax + j=jmax + if (vp(i,j) .and. vp(i-1,j) .and. vp(i,j-1) ) then +c + zeta(i,j,z) = (v(i,j,w)-v(i-1,j,w))/(dlon_edge * cosfac(j)) + & - (u(i,j-1,w)-u(i,j,w))/(dlat_edge) + & + tanfac(j)*u(i,j,w) + else +c zeta(i,j,z)= -999. + zeta(i,j,z) = 2. * omega * sin(rlat(j)*dtr) + endif +c + do ii=1,imax + do jj=1,jmax + tmpzeta(ii,jj) = zeta(ii,jj,z) * 1.e5 + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine thickness_calc (imax,jmax,vp) +c +c ABSTRACT: This routine calculates the thicknesses for three +c different layers: 200-500, 500-850 and 200-850 mb. +c +c LOCAL VARIABLES: +c + USE tracked_parms; USE verbose_output + + implicit none + + integer i,j,layer,upper,lower,imax,jmax + logical(1) vp(imax,jmax) + +c -------------------------- + +c The array indices for the 3 different thickness layers are +c as follows: +c 1: 500-850 +c 2: 200-500 +c 3: 200-850 +c +c The array indices for the levels for the 4 different GP height +c arrays (as assigned in subroutine getdata) are as follows: +c 1: 850 mb +c 2: 700 mb +c 3: 500 mb +c 4: 200 mb + + + do layer = 1,3 + + select case (layer) + case (1); upper=3; lower=1; + case (2); upper=4; lower=3; + case (3); upper=4; lower=1; + end select + + do j = 1,jmax + do i = 1,imax + + if (vp(i,j)) then + thick(i,j,layer) = hgt(i,j,upper) - hgt(i,j,lower) + else + thick(i,j,layer) = -999.0 + endif + + enddo + enddo + + enddo +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine first_ges_center (imax,jmax,dx,dy,cparm,fxy + & ,cmaxmin,trkrinfo,ifh,valid_pt,maxstorm,masked_out + & ,stormct,contour_info,maxmini,maxminj,ifgcret) +c +c ABSTRACT: This subroutine scans an array and picks out areas of +c max or min, then loads those center positions into the first- +c guess lat & lon arrays to be used by subroutine tracker for +c locating the very specific low center positions. +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c dx Grid spacing in i-direction for the input grid +c dy Grid spacing in j-direction for the input grid +c cparm Char string indicating what parm is being passed in +c fxy Real array of data values +c finf Logical. Field of influence. Dimension same as fxy +c cmaxmin Char string to indicate if search is for a max or a min +c trkrinfo Derived type that holds/describes various tracker parms, +c including the contour interval to be used +c ifh Index for the forecast hour +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c contour_info Type cint_stuff from module contours. Contains +c contour information +c +c OUTPUT: +c maxmini Integer array containing i-indeces of max/min locations +c maxminj Integer array containing j-indeces of max/min locations +c ifgcret return code from this subroutine +c +c OTHER: +c storm Contains the tcvitals for the storms (module def_vitals) + + USE trkrparms; USE grid_bounds; USE set_max_parms; USE def_vitals + USE contours; USE tracked_parms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,n,isstart,ifamret,ibeg,jbeg,iend,jend + integer ifh,maxstorm,imax,jmax,itemp,ifgcret + integer stormct,oldstormct,mm + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + character(*) cparm,cmaxmin + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real dmax,dmin,dx,dy,dbuffer,tmp + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-------------------------------------------------*' + print *,'* At top of first_ges_center *' + write (6,102) ifhours(ifh),ifclockmins(ifh) + 102 format (1x,'* Searching for new lows at hour ',i4,':',i2.2) + print *,'*-------------------------------------------------*' + endif + + +c First check the user-supplied grid boundaries to see if we will +c scan the entire array or just a portion of it. + + if (trkrinfo%northbd < -998.0 .or. trkrinfo%southbd < -998.0 .or. + & trkrinfo%westbd < -998.0 .or. trkrinfo%eastbd < -998.0) then + ! User did not specify a subgrid, so scan the whole domain + ibeg = 1 + iend = imax + jbeg = 1 + jend = jmax + else + +c if (trkrinfo%westbd > 360.0 .or. trkrinfo%eastbd < 0.0 .or. +c & trkrinfo%westbd < 0.0 .or. + + if (trkrinfo%westbd > 360.0 .or. + & trkrinfo%northbd > 90.0 .or. trkrinfo%northbd <-90.0 .or. + & trkrinfo%southbd > 90.0 .or. trkrinfo%southbd <-90.0 .or. + & trkrinfo%westbd >= trkrinfo%eastbd .or. + & trkrinfo%southbd >= trkrinfo%northbd) then + + if (trkrinfo%westbd > trkrinfo%eastbd) then + + if (trkrinfo%westbd < 360.0 .and. + & trkrinfo%eastbd >= 0.0)then + + ! In this special case, the user has specified that the + ! western boundary be to the west of the Greenwich + ! meridian and the eastern boundary be to the east of it. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'++ NOTE: The user supplied grid lon boundaries' + print *,'++ span across the Greenwich meridian.' + print *,'++ ' + print *,'++ Western boundary: ',trkrinfo%westbd + print *,'++ Eastern boundary: ',trkrinfo%eastbd + print *,'++ Northern boundary: ',trkrinfo%northbd + print *,'++ Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ! Calculate the beginning and ending i and j points for + ! this case of spanning the Greenwich meridian. The + ! beginning and ending j points are, obviously, the same + ! as for the regular case below in the else. The + ! i-beginning point will also be the same as for the + ! regular case. However, the i-ending point will be + ! modified for the meridian wrap; it will be > imax. + + jbeg = int(((glatmax + dy - trkrinfo%northbd) + & / dy) + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) + & / dy) + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) + & / dx) + 0.5) +c iend = int(((trkrinfo%eastbd - glonmin + dx) +c & / dx) + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) + & / dx) + 0.5) + imax + + goto 377 + + endif + endif + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. There is a' + print *,'!!! problem with the user-supplied grid ' + print *,'!!! boundaries. Please check them and ' + print *,'!!! resubmit the program.' + print *,'!!!' + print *,'!!! Western boundary: ',trkrinfo%westbd + print *,'!!! Eastern boundary: ',trkrinfo%eastbd + print *,'!!! Northern boundary: ',trkrinfo%northbd + print *,'!!! Southern boundary: ',trkrinfo%southbd + print *,' ' + endif + + ifgcret = 91 + return + + 377 continue + + else + ! Calculate the beginning and ending i and j points.... + jbeg = int(((glatmax + dy - trkrinfo%northbd) / dy) + & + 0.5) + jend = int(((glatmax + dy - trkrinfo%southbd) / dy) + & + 0.5) + ibeg = int(((trkrinfo%westbd - glonmin + dx) / dx) + & + 0.5) + iend = int(((trkrinfo%eastbd - glonmin + dx) / dx) + & + 0.5) + endif + endif + +c Scan the requested portion of the grid and pick out the max and +c min data values, figure out what the max and min contour levels +c will be, and fill an array with the values of the various +c intermediate, incremental contour levels. + + if (trkrinfo%contint <= 0) then + + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in first_ges_center. For a midlat' + print *,'!!! or tcgen run of the tracker, the contour' + print *,'!!! interval supplied by the user is not ' + print *,'!!! greater than 0.' + print *,'!!! ' + print *,'!!! User-supplied contint = ',trkrinfo%contint + print *,' ' + endif + + ifgcret = 91 + return + endif + + dmin = 9.99e20 + dmax = -9.99e20 + + do j = jbeg,jend + do i = ibeg,iend + if (i > imax) then + itemp = i - imax ! If wrapping past GM + else + itemp = i + endif + if (valid_pt(itemp,j)) then + if (fxy(itemp,j) < dmin) dmin = fxy(itemp,j) + if (fxy(itemp,j) > dmax) dmax = fxy(itemp,j) + endif + enddo + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*--------------------------------------------*' + print *,'In first_ges_center, dmin= ',dmin,' dmax= ',dmax + endif + + +c We want to allow for storms moving out of the sub-region, +c in which case we might hit slightly lower or higher +c contours than were found in the sub-region, so allow for +c an extra buffer and modify dmin and dmax.... + + dbuffer = (dmax - dmin) / 2.0 + dmax = dmax + dbuffer + dmin = dmin - dbuffer + + if ( verb .ge. 3 ) then + print *,'after adjustment, dmin= ',dmin,' dmax= ',dmax + endif + +c Next 2 lines changed for compiler compatibility on +c other platforms.... +c contour_info%xmaxcont = dmax - amod(dmax,trkrinfo%contint) +c contour_info%xmincont = dmin - amod(dmin,trkrinfo%contint) + + tmp = trkrinfo%contint + contour_info%xmaxcont = dmax - mod(dmax,tmp) + contour_info%xmincont = dmin - mod(dmin,tmp) + + if ( verb .ge. 3 ) then + print *,'A1 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A1 contour_info%xmincont= ',contour_info%xmincont + endif + + if (contour_info%xmincont > contour_info%xmaxcont) then + contour_info%xmincont = contour_info%xmaxcont + endif + +c if (dmin > contour_info%xmincont) then +c contour_info%xmincont=contour_info%xmincont + trkrinfo%contint +c endif +c if (dmax < contour_info%xmaxcont) then +c contour_info%xmaxcont=contour_info%xmaxcont - trkrinfo%contint +c endif + + if ( verb .ge. 3 ) then + print *,'A2 contour_info%xmaxcont= ',contour_info%xmaxcont + print *,'A2 contour_info%xmincont= ',contour_info%xmincont + print *,'maxconts= ',maxconts + endif + +c NOTE: In the loop below, the contour_info%contvals array is now +c (5/2003) no longer used in subsequent subroutines. But we still +c need to figure out the value of the contvals as we iterate the +c loop so we can know when we've surpassed dmax and can stop +c incrementing contour_info%numcont, which we do need in subsequent +c subroutines. + + contour_info%numcont = 0 + do n = 1,maxconts + contour_info%numcont = contour_info%numcont + 1 + contour_info%contvals(n) = contour_info%xmincont + + & float(n-1)*trkrinfo%contint +c print *,'n= ',n,' contour_info%contvals(n)= ' +c & ,contour_info%contvals(n) + if (contour_info%contvals(n) >= dmax) exit + enddo + + oldstormct = stormct + call find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) + + if (stormct > 0) then + continue + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' ' + print *,'!!! ************************************************' + print *,'!!! ' + print *,'!!! NOTE: In first_ges_center, the value of stormct' + print *,'!!! returned from find_all_maxmins is not greater' + print *,'!!! than 0. This means there are no new centers' + print *,'!!! to track, which is not likely. Perhaps you are' + print *,'!!! searching over too small of an area??' + print *,'!!! ' + print *,'!!! ************************************************' + print *,' ' + endif + + endif + + print *,'ifh= ',ifh,' oldstormct= ',oldstormct + print *, ' stormct= ',stormct + + do mm = 1,300 + print *,'mm= ',mm,' maxmini(mm)= ',maxmini(mm) + & ,' maxminj(mm)= ',maxminj(mm) + enddo + + if (stormct > oldstormct .and. stormct > 0) then + isstart = oldstormct + 1 + + if ( verb .ge. 3 ) then + write (6,*) ' ' + write (6,*) 'New search: ' + write (6,*) 'Possible new max/min locations at ifh= ',ifh + write (6,*) '--------------------------------------------' + endif + + do n = isstart,stormct + if (trkrinfo%type == 'midlat') then + storm(n)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(n)%tcv_center = 'TCG ' + endif + slonfg(n,ifh) = glonmin + (maxmini(n)-1)*dx + slatfg(n,ifh) = glatmax - (maxminj(n)-1)*dy + storm(n)%tcv_stspd = -99 + storm(n)%tcv_stdir = -99 + write (storm(n)%tcv_storm_id,'(i4.4)') n + write (storm(n)%tcv_storm_name,'(i4.4)') n + stormswitch(n) = 1 + if (cparm == 'mslp') then + + if ( verb .ge. 3 ) then + write (6,71) maxmini(n),maxminj(n),slonfg(n,ifh) + & ,360.-slonfg(n,ifh),slatfg(n,ifh) + & ,slp(maxmini(n),maxminj(n))/100.0 + endif + + endif + enddo + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,' New search: ' + print *,'!!! NOTE: No new storms found in find_all_maxmins' + print *,'!!! at ifh = ',ifh,' stormct= ',stormct + print *,'!!! oldstormct= ',oldstormct + print *,' ' + endif + + endif + + 71 format (1x,'i= ',i4,' j= ',i4,' lon: ',f7.2,'E (',f6.2,'W)' + & ,2x,' lat: ',f6.2,' mslp: ',f6.1,' mb') +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine find_all_maxmins (imax,jmax,ibeg,iend,jbeg,jend,fxy + & ,valid_pt,masked_out,contour_info,dx,dy + & ,trkrinfo,cmaxmin,maxstorm,stormct,maxmini + & ,maxminj,ifamret) +c +c ABSTRACT: This subroutine will search an area delineated by +c input i and j indeces in order to find all local maxes or mins +c in that area. The (i,j) locations of the maxes/mins are returned +c in the maxmini and maxminj arrays. The input 3-character string +c cmaxmin will tell the subroutine to look for a "max" or a "min". +c +c INPUT: +c imax Number of gridpoints in i direction in input grid +c jmax Number of gridpoints in j direction in input grid +c ibeg i-index for upper left location of grid to search +c iend i-index for lower right location of grid to search +c jbeg j-index for upper left location of grid to search +c jend j-index for lower right location of grid to search +c fxy Real array of data values +c valid_pt Logical bitmap masking non-valid grid points. This is a +c concern for the regional models, which are interpolated +c from Lam-Conf or NPS grids onto lat/lon grids, leaving +c grid points around the edges which have no valid data. +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c contour_info Type cint_stuff from module contours containing the +c the following 4 variables: +c 1. xmincont Real value for min contour level in the fxy data array +c 2. xmaxcont Real value for max contour level in the fxy data array +c 3. contvals Real array holding values of cont levels at this time +c 4. numcont Number of contour intervals found at this time +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c trkrinfo derived type containing various user-input tracker parms +c cmaxmin String that declares if "min" or "max" is being searched +c maxstorm max # of storms that can be handled in this run +c +c INPUT/OUTPUT: +c stormct Integer: keeps and increments a running tab of the number +c of storms that have been tracked at any time across all +c forecast hours +c +c OUTPUT: +c maxmini integer array containing i-indeces of the max/min points +c maxminj integer array containing j-indeces of the max/min points +c ifamret return code from this subroutine + + USE trkrparms; USE set_max_parms; USE contours + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + integer stormct,i,j,ibeg,iend,jbeg,jend,ix,jx,ixp1,ixm1 + integer ip,jp,maxstorm,jxp1,jxm1,ifamret,isret,iaret,iclmret + integer isoiret,icccret,igicwret,imax,jmax + character ccflag*1,get_last_isobar_flag*1,point_is_over_water*1 + character(*) cmaxmin + logical(1) still_finding_valid_maxmins,rough_gradient_check_okay + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + integer maxmini(maxstorm),maxminj(maxstorm) + real fxy(imax,jmax) + real xavg,stdv,search_cutoff,dmin,dmax,sphere_cutoff + real plastbar,rlastbar,fract_land,dx,dy + +c----- + still_finding_valid_maxmins = .true. + + +c print *,'ctm beg of find_all_maxmins, maxstorm= ',maxstorm + + +c First, we want to get the mean and standard deviation of the input +c field to be searched. We can use the standard deviation info as +c part of our guideline for when to stop searching for maxes & mins. +c We will set the search cut-off threshold at 1/2 standard deviation +c above the mean for min searches. So, for the example of mslp, if +c the mean pressure over the whole domain is 1010 mb and the +c standard deviation is 12 mb, then when we are searching, if the +c lowest available (i.e., hasn't been found in a previous iteration +c of this loop) pressure is 1016, then it's time to stop searching. + + call avgcalc (fxy,imax*jmax,valid_pt,xavg,iaret) + call stdevcalc (fxy,imax*jmax,valid_pt,xavg,stdv,isret) + if (iaret /= 0 .or. isret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: In find_all_maxmins, the calls to avgcalc' + print *,'!!! and/or stdevcalc returned an error.' + print *,'!!! iaret= ',iaret,' isret= ',iaret + print *,' ' + endif + + ifamret = 98 + return + endif + + if (cmaxmin == 'min') then + search_cutoff = xavg + stdv*0.5 + else + search_cutoff = xavg - stdv*0.5 + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In find_all_maxmins, search_cutoff= ',search_cutoff + print *,' ' + endif + +c Now begin to search the domain. We do a simple gridpoint scan, +c and once we find the max/min value, we pass the (i,j) coordinates +c at that point to a routine to check for a closed contour. Then +c we mask out those points in the contour (or, if there is not a +c closed contour, just the 8 points immediately surrounding the low +c center) and we do another iteration of search_loop to look for +c more lows. We mask out points we've found so that on subsequent +c iterations of search_loop, we don't find the same old center +c again and again and again..... + + search_loop: do while (still_finding_valid_maxmins) + + dmin = 9.99e20 + dmax = -9.99e20 + + jloop: do j = jbeg,jend + iloop: do i = ibeg,iend + + ip = i + jp = j + + if (ip > imax) then + if (trkrinfo%gridtype == 'global') then + ip = i - imax ! If wrapping past GM + else + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! WARNING: In find_all_maxmins, the ' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is beyond the eastern bounds of ' + print *,'!!! this regional grid. The search' + print *,'!!! will not extend to the user-requested' + print *,'!!! grid boundary.' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',ip + print *,' ' + endif + + exit iloop + + endif + endif + + if (valid_pt(ip,jp) .and..not. masked_out(ip,jp)) then + if (cmaxmin == 'min') then + if (fxy(ip,jp) < dmin) then + dmin = fxy(ip,jp) + ix = ip + jx = jp + endif + else + if (fxy(ip,jp) > dmax) then + dmax = fxy(ip,jp) + ix = ip + jx = jp + endif + endif + endif + + enddo iloop + enddo jloop + + if (cmaxmin == 'min') then + if (dmin < search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + else + if (dmax > search_cutoff) then + continue + else + still_finding_valid_maxmins = .false. + exit search_loop + endif + endif + +c As a rough first check, see if the neighboring points on all +c 4 sides have a gradient sloping down into the found min point, +c or at least that there is a flat field not having a gradient +c sloping away from the center point. + + call get_ijplus1_check_wrap (imax,jmax,ix,jx,ixp1,jxp1,ixm1 + & ,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In find_all_maxmins, the center we found' + print *,'!!! is too close to the grid boundary and will' + print *,'!!! NOT be checked for a closed contour.' + print *,'!!! ix= ',ix,' jx= ',jx,' fxy= ',fxy(ix,jx) + print *,'!!! ' + print *,' ' + endif + + masked_out(ix,jx) = .true. + cycle search_loop + endif + + if (cmaxmin == 'min') then + if (fxy(ix,jx) <= fxy(ixp1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxm1) .and. + & fxy(ix,jx) <= fxy(ixm1,jx) .and. + & fxy(ix,jx) <= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + else + if (fxy(ix,jx) >= fxy(ixp1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxm1) .and. + & fxy(ix,jx) >= fxy(ixm1,jx) .and. + & fxy(ix,jx) >= fxy(ix,jxp1)) then + rough_gradient_check_okay = .true. + else + rough_gradient_check_okay = .false. + endif + endif + + if (rough_gradient_check_okay) then + + if ( verb .ge. 3 ) then + print *,'Found a possible max/min at ix= ',ix,' jx= ',jx + endif + + +c From this rough check, we appear to have a gradient sloping +c in towards the center point. Now call the subroutine to +c check whether or not there is in fact a closed contour +c surrounding this local maximum or minimum. + + get_last_isobar_flag = 'n' + ccflag = 'n' + call check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,ccflag,cmaxmin,trkrinfo + & ,1,contour_info,get_last_isobar_flag,plastbar + & ,rlastbar,icccret) + + if (ccflag == 'y') then + if (stormct < maxstorm) then + stormct = stormct + 1 + + if ( verb .ge. 3 ) then + print *,'AAA stormct= ',stormct,' ix= ',ix,' jx= ',jx + endif + + ! For a tcgen case, we will add in one additional check, + ! and that is to ensure the point is (mostly) over water. + ! Only do this check if the user has requested it (some + ! of the global models do not have a land-sea mask + ! included in the grib data files). + + point_is_over_water = 'u' + + if (trkrinfo%use_land_mask == 'y') then + call check_land_mask (imax,jmax,ix,jx,fract_land + & ,valid_pt,dx,dy,point_is_over_water,iclmret) + if (iclmret /= 0) then + print *,' ' + print *,'!!! ERROR from check_land_mask for ix= ',ix + & ,' jx= ',jx + print *,'!!! STOPPING PROGRAM' + stop 95 + endif + endif + + if (point_is_over_water /= 'n') then + maxmini(stormct) = ix + maxminj(stormct) = jx + endif + + else + + if ( verb .ge. 3 ) then + print *,'---max stormct reached, stormct= ', stormct + endif + + endif + else + + if ( verb .ge. 3 ) then + print *,'!!! contour check negative, ccflag= ',ccflag + endif + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* After check_closed_contour... *' + print *,'*-----------------------------------------------*' + print *,' ' + endif + + endif + +c Regardless of whether or not the found point turns out to have +c a closed contour, we don't want to find this local minimum or +c its 8 surrounding points again in a search on a subsequent +c iteration of this loop. + + masked_out(ix,jx) = .true. + masked_out(ix,jxp1) = .true. + masked_out(ixp1,jxp1) = .true. + masked_out(ixp1,jx) = .true. + masked_out(ixp1,jxm1) = .true. + masked_out(ix,jxm1) = .true. + masked_out(ixm1,jxm1) = .true. + masked_out(ixm1,jx) = .true. + masked_out(ixm1,jxp1) = .true. + + enddo search_loop + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine mask_based_on_wind_circ (imax,jmax,dx,dy,level + & ,valid_pt,masked_outc,trkrinfo + & ,ctlon,ctlat,cmodel_type,imbowret) +c +c ABSTRACT: This subroutine masks out grid points for a storm that +c is currently being tracked. It is called after a fix has been +c made at the current forecast hour. It is only used as a backup, +c that is, if the mslp data were not there and/or a fix position +c for mslp could not be made, then that means that the mask would +c not be able to get updated using the routine in subroutine +c check_closed_contour. But we still do need to update that mask, +c so we will instead do it based on wind circulation. We will go +c out radially from the center, starting at 40 km, then every +c 40 km from there on out. When the mean cyclonic Vt drops below +c 3 m/s, stop searching, and then mask out all grid points within +c that last-searched radius. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_outc Logical. T = data point is already accounted for, +c under the influence of another nearby max or min +c center; F = data point is available to be scanned by +c this subroutine for max or min centers. +c ctlon Fix longitude for the input parameter to this routine +c ctlon Fix latitude for the input parameter to this routine +c cmodel_type character, 'global' or 'regional' + + USE set_max_parms; USE trkrparms; USE grid_bounds + USE verbose_output; USE level_parms + + implicit none + + type (trackstuff) trkrinfo + + character(*) cmodel_type + integer, parameter :: numazim=24 + integer imax,jmax,level,imbowret,nlev,iazim,i,j + integer ibiret1,ibiret2,azimuth_ct,igvtret + integer jnfix,jsfix,iefix,iwfix + real vr(numazim),vt(numazim) + real dx,dy,ctlon,ctlat,rdist,bear,targlat,targlon + real xintrp_u,xintrp_v,grid_buffer,xmax_rdist_reached + real vt_mean,vt_azim_sum,xbear,dist,degrees + logical(1) valid_pt(imax,jmax),masked_outc(imax,jmax) + logical(1) searching_valid_pts + + imbowret = 0 + + select case (level) + case (850); nlev = nlev850 ! check module level_parms for + case (700); nlev = nlev700 ! the values of these.... + case (500); nlev = nlev500 + case (1020); nlev = levsfc + end select + + if (cmodel_type == 'regional') then + grid_buffer = 0.30 + else + grid_buffer = 0.0 + endif + + searching_valid_pts = .true. + + rdist = 40.0 ! units in km + xmax_rdist_reached = rdist ! units in km + + radial_loop: do while (searching_valid_pts) + + azimuth_ct = 0 + vt_azim_sum = 0.0 + vt = -999.0 + vr = -999.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 7.5 + + call distbear (ctlat,ctlon,rdist,bear,targlat,targlon) + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + xmax_rdist_reached = rdist + exit radial_loop + endif + + ! These calls to bilin_int_uneven pass a variable, level, + ! that contains the vertical level to pull the wind data + ! from, either 850, 700 or surface (which will be + ! indicated by a value/code of 1020). + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'u',xintrp_u,ibiret1) + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,level,'v',xintrp_v,ibiret2) + + if (ibiret1 == 0 .and. ibiret2 == 0) then + call getvrvt (ctlon,ctlat,targlon,targlat + & ,xintrp_u,xintrp_v,vr(iazim) + & ,vt(iazim),igvtret) + azimuth_ct = azimuth_ct + 1 + vt_azim_sum = vt_azim_sum + vt(iazim) + else + xmax_rdist_reached = rdist + exit radial_loop + endif + + enddo azimloop + + if (azimuth_ct > 0) then + ! Compute azimuthally-averaged Vt at this distance + vt_mean = vt_azim_sum / float(azimuth_ct) + else + vt_mean = -999.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: rdist= ',rdist,' azimuth_ct= ',azimuth_ct + & ,' vt_azim_sum= ',vt_azim_sum,' vt_mean= ',vt_mean + endif + + if (ctlat >= 0.0) then + if (vt_mean >= 3.0) then + ! For a NH storm, if the cyclonic mean Vt >= 3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + else + if (vt_mean <= -3.0 .and. vt_mean > -998.0) then + ! For a SH storm, if the cyclonic mean Vt <= -3.0, increment + ! rdist and cycle through to the next iteration of + ! radial_loop. + rdist = rdist + 40.0 + else + xmax_rdist_reached = rdist + exit radial_loop + endif + endif + + enddo radial_loop + + if ( verb .ge. 3 ) then + print *,'mbow: After radial_loop, rdist= ',rdist + & ,' xmax_rdist_reached= ',xmax_rdist_reached + endif + +c ----------------------------------------------------------------- +c At this point, we are done searching radially outwards away from +c the storm center. The max radial distance we reached is called +c xmax_rdist_reached. By getting to this spot in the subroutine, +c that means that we bumped out of radial_loop above because the +c rdist being used in that loop got to a radius at which the mean +c cyclonic Vt no longer was strong enough to continue the search +c outward, so we need to reduce it by 40 km here (back to the value +c for the last successful search). At a minimum, we will mask to a +c radius of 80 km. + + if (xmax_rdist_reached > 80.0) then + xmax_rdist_reached = xmax_rdist_reached - 40.0 + else + xmax_rdist_reached = 80.0 + endif + + if ( verb .ge. 3 ) then + print *,'mbow: After adjustment of xmax_rdist_reached, rdist= ' + & ,rdist,' xmax_rdist_reached= ',xmax_rdist_reached + endif + + bearloop: do i = 1,4 + + ! Now find the values of the longitude for the farthest west + ! and east points and find the values of the latitude for the + ! farthest north and south points. The i and j indices + ! associated with these lons and lats will be used to define + ! the bounds of the grid over which we scan to find points + ! that will update the mask. + + select case (i) + case (1); xbear = 0.0; + case (2); xbear = 90.0; + case (3); xbear = 180.0; + case (4); xbear = 270.0; + end select + + call distbear (ctlat,ctlon,xmax_rdist_reached,xbear + & ,targlat,targlon) + + if ( verb .ge. 3 ) then + print *,'mbow: distbear for i= ',i,' targlon= ',targlon + & ,' targlat= ',targlat + endif + + if (targlon >= glonmax) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon - 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon > glonmax for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmax= ',glonmax + imbowret = 95 + return + endif + endif + + if (targlon < glonmin) then + if (trkrinfo%gridtype == 'global') then + targlon = targlon + 360. ! We just GM-wrapped for the + ! full, regular, global grid. + else + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'targlon < glonmin for a regional grid, so we ' + print *,'cannot update the mask.' + print *,'targlon= ',targlon,' glonmin= ',glonmin + imbowret = 95 + return + endif + endif + + if (targlat > glatmax .or. targlat < glatmin) then + print *,' ' + print *,'WARNING: In subroutine mask_based_on_wind_circ,' + print *,'either targlat > glatmx or targlat < glatmin, so' + print *,'we cannot update the mask.' + print *,'targlat= ',targlat,' glatmin= ',glatmin + print *,' glatmin= ',glatmin + imbowret = 95 + return + cycle bearloop + endif + + ! Get the i & j starting and ending points for our loop where + ! we will update the mask.... + + if (i == 1) then + + ! Get j for northern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jnfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jnfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 2) then + + ! Get i for eastern longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iefix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + elseif (i == 3) then + + ! Get i for southern latitude. Fix targlat to the *nearest* + ! j-point (i.e., round it....) + + if (targlat >= 0.0) then ! N. Hemisphere + jsfix = int((glatmax - targlat)/dy + 1.0 + 0.5) + else ! S. Hemisphere + jsfix = ceiling((glatmax - targlat)/dy + 1.0 - 0.5) + endif + + elseif (i == 4) then + + ! Get i for western longitude. Fix targlon to the *nearest* + ! i-point (i.e., round it....) + + iwfix = int((targlon - glonmin)/dx + 1.0 + 0.5) + + endif + + enddo bearloop + + if ( verb .ge. 3 ) then + print *,'mbow: iwfix= ',iwfix,' iefix= ',iefix + & ,' jnfix= ',jnfix,' jsfix= ',jsfix + endif + + do i = iwfix,iefix + do j = jnfix,jsfix + + call calcdist (glon(i),glat(j),ctlon,ctlat,dist,degrees) + + if (dist < xmax_rdist_reached) then + masked_outc(i,j) = .true. + endif + + enddo + enddo + + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_closed_contour (imax,jmax,ix,jx,fxy,valid_pt + & ,masked_out,closed_contour,cmaxmin,trkrinfo + & ,num_requested_contours,contour_info + & ,get_last_isobar_flag,plastbar,rlastbar,icccret) +c +c ABSTRACT: This subroutine checks a field of data around an input +c (ix,jx) data point to see if a closed contour exists around +c that data point. It can check for a closed contour on a max or a +c min field, depending on the value of the input variable 'cmaxmin'. +c The algorithm works by examining rings of the 8 data points +c surrounding a data point that is in the contour interval. For +c example, in the diagram below, the X represents the location of +c the local minimum value which was passed into this routine with +c the coordinates (ix,jx), let's say it's 985 mb. And let's assume +c that the data values at points A-I are all in the 4 mb contour +c interval of 985-989 mb, and that all the surrounding points have +c data values >= 989. To test for a closed contour, we first check +c the ring of 8 points immediately around point X to see what their +c data values are. If a data value is found that is below the +c lower limit of this contour interval (985 mb) or lower than the +c local minimum value at the X point that we initially targeted +c (985 mb), then we do NOT have a closed contour, and we exit this +c subroutine. But in our example, that's not the case, and we have +c 5 points (B,D,E,F,G) that are in the interval. So in our next +c iteration of the loop, we set up 5 rings, each one set up around +c the points found in the first iteration (B,D,E,F,G), and we check +c the 8 points around each of those points. A logical array is +c used so that as soon as a point is found, it is flagged as being +c found. In this way, when we look at the ring around point D, for +c example, we won't pick point X again and set up another ring +c around it in the next ring iteration and end up in an infinite +c loop, going back and forth between point X and point D. While +c checking the 8 points in a ring, if a found data value is above +c our contour interval (i.e., >= 989 mb), we just ignore the +c point; we only mark points that are in our contour interval, +c and again, if we find a point below our contour interval, we +c exit the subroutine with a flag indicating a closed contour was +c NOT found. So in this method, we keep spreading out from the +c initial local minimum and creating and checking new rings until +c we either: (a) Hit the edge of the regional grid, in which case +c we consider a closed contour NOT found, (b) Run into a data +c point that has been marked as being under the influence of +c another nearby low, in which case we consider a closed contour +c NOT found, (c) Run into a point which is below (above) our +c contour interval for a min (max) check, in which case we +c consider a closed contour NOT found, or (d) we run out of +c points to keep searching, we have no rings left to create and +c check because all of the surrounding points are above (below) +c our contour interval for a min (max) check, and by default we +c consider this a closed contour and return to the calling +c subroutine a flag indicating such. +c +c + + + + + + + + + + +c + + + + + + + + + + +c + + A B + + + + + + +c + + C D X E + + + + +c + + + + F G + + + + +c + + + + + H I + + + +c + + + + + + + + + + +c + + + + + + + + + + +c +c UPDATE: This subroutine was updated to keep searching for +c multiple closed contours until it can't find anymore. The +c input parameter num_requested_contours dictates how many +c contours to search for. In the case of just trying to roughly +c locate new centers and establish that there is a closed +c circulation, num_requested_contours will = 1, and we will exit +c after finding that 1 contour. But for a check after making a +c full center fix, we set num_requested_contours = 999 so that +c we can keep searching for all closed contours around the low. +c In this 999 case, you will eventually get to a point where +c there is no closed contour. In that case, in the standard +c output you will see a message telling you that you hit a point +c that is not in the contour and that there is no closed contour, +c but you will also notice that the ccflag = y, meaning there is +c a closed contour (because you have found at least 1 closed +c contour along the way). The reason to keep searching for more +c closed contours is that we can then return the value of the +c outermost closed isobar. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c fxy input data array +c valid_pt Logical; bitmap indicating if valid data at that pt +c masked_out Logical. T = data point is already accounted for, under +c the influence of another nearby max or min center, +c F = data point is available to be scanned by this +c subroutine for max or min centers. +c cmaxmin character string ('max' or 'min') that tells this +c routine what we're looking for. +c trkrinfo derived type that holds/describes various tracker parms +c contour_info Type cint_stuff from module contours. Contains +c contour information +c num_requested_contours For the simple first_ges_center check, +c this will be 1 (we just want to know if there's at +c least 1 closed contour). For the verifying check after +c we've found a center, this will be 9999 (i.e., just keep +c searching for more contours) +c get_last_isobar_flag character ('y' or 'n') to indicate whether +c or not to report on the value of the last closed isobar +c and the radius of the last closed isobar. +c +c OUTPUT: +c closed_contour character; A returned value of 'y' indicates that +c this routine was able to find a closed contour. +c plastbar Contains the value of the last closed isobar (unrounded) +c rlastbar Contains the mean radius of the last closed isobar +c +c LOCAL: +c num_pts_in_all_contours Counter for the number of pts inside of +c the contour we're looking at +c next_ring_ct Counter for the number of points that have been +c tagged to be used as center points for the next +c iteration of multiple_ring_loop. +c next_contour_ct Counter for the number of points that have been +c tagged to be used as center points in the first iteration +c through single_contour_scan_loop as we begin to scan +c points in the *next* contour interval. This counter gets +c incremented when, for example, we are searching points +c around a current center point and we find one that is not +c in our current interval, but rather is in the next +c interval. We want to remember this point and store the +c location, so we increment this counter and store the +c location in next_contour_i and next_contour_j arrays. +c beyond_contour_ct Counter for the number of points that have been +c tagged to be used as center points for some subsequent +c iteration of successive_contours_loop. This is +c different from next_contour_ct, which is used to hold +c the locations of points that are definitely in the +c *next* contour interval. Here, we have points that we +c just store in a pool of potential points to be searched +c in future iterations. These points can come about in +c cases where there is a very intense, very compact low +c with a tight pressure gradient, such that multiple +c contour intervals could be spanned in between 2 adjacent +c gridpoints (this is especially the case if the contour +c interval you have chosen is small). You need to be +c careful with how you handle this array. Once you find +c that you have searchable points in next_contour_i or +c next_contour_j, do not just simply empty out this +c beyond_contour count and its i and j arrays. The +c reason being that some of these "beyond" points may end +c up being used and searched in subsequent iterations, but +c not if we just delete them now. + + + USE set_max_parms; USE trkrparms; USE contours; USE grid_bounds + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + type (cint_stuff) contour_info + + integer i,j,ir,iria,irja,irx,jrx,ix,jx,imax,jmax + integer nb,ibx,jby,nct,iflip + integer mr,ringct,ixp1,ixm1,jxp1,jxm1,nring,iter + integer icenx,jcenx,icccret,next_ring_ct,igicwret + integer num_pts_in_all_contours,next_contour_ct + integer beyond_contour_ct + integer num_pts_in_one_contour + integer num_requested_contours,num_found_contours + integer nm,im,jm,inall,insingle,isc_count,rlast_distct + character found_a_point_in_our_contour*1,closed_contour*1 + character found_a_point_below_contour*1 + character found_a_point_above_contour*1,get_last_isobar_flag*1 + character(*) cmaxmin + logical(1) still_scanning + logical(1) valid_pt(imax,jmax),masked_out(imax,jmax) + logical(1) point_is_already_in_our_contour(imax,jmax) + logical(1) point_is_already_in_next_contour(imax,jmax) + logical(1) point_is_already_in_beyond_pool(imax,jmax) + integer isni,isnj,inci,incj,ibci,ibcj,ihmi,ihmj,itmi,itmj + integer, allocatable :: search_next_i(:) + integer, allocatable :: search_next_j(:) + integer, allocatable :: next_contour_i(:) + integer, allocatable :: next_contour_j(:) + integer, allocatable :: beyond_contour_i(:) + integer, allocatable :: beyond_contour_j(:) + integer, allocatable :: hold_mask_i_loc(:) + integer, allocatable :: hold_mask_j_loc(:) + integer, allocatable :: temp_mask_i_loc(:) + integer, allocatable :: temp_mask_j_loc(:) + integer, allocatable :: ringposi(:),ringposj(:) + real,allocatable :: ringpos(:,:) + real fxy(imax,jmax),contvals(maxconts) + real contlo,conthi,xcentval,contlo_next,conthi_next + real dist,degrees,rlast_distsum,plastbar,rlastbar +c + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + allocate (search_next_i(imax*jmax),stat=isni) + allocate (search_next_j(imax*jmax),stat=isnj) + allocate (next_contour_i(imax*jmax),stat=inci) + allocate (next_contour_j(imax*jmax),stat=incj) + allocate (beyond_contour_i((imax*jmax)/2),stat=ibci) + allocate (beyond_contour_j((imax*jmax)/2),stat=ibcj) + allocate (hold_mask_i_loc(imax*jmax),stat=ihmi) + allocate (hold_mask_j_loc(imax*jmax),stat=ihmj) + allocate (temp_mask_i_loc(imax*jmax),stat=itmi) + allocate (temp_mask_j_loc(imax*jmax),stat=itmj) + if (isni /= 0 .or. isnj /= 0 .or. inci /= 0 .or. incj /= 0 .or. + & ibci /= 0 .or. ibcj /= 0 .or. ihmi /= 0 .or. ihmj /= 0 .or. + & itmi /= 0 .or. itmj /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various search, hold and temp arrays.' + print *,'!!! isni = ',isni,' isnj= ',isnj + print *,'!!! inci = ',inci,' incj= ',incj + print *,'!!! ibci = ',ibci,' ibcj= ',ibcj + print *,'!!! ihmi = ',ihmi,' ihmj= ',ihmj + print *,'!!! itmi = ',itmi,' itmj= ',itmj + print *,' ' + endif + + STOP 98 + endif + + closed_contour = 'n' + xcentval = fxy(ix,jx) + num_found_contours = 0 + next_contour_ct = 0 + beyond_contour_ct = 0 + num_pts_in_all_contours = 0 + hold_mask_i_loc = 0 + hold_mask_j_loc = 0 + beyond_contour_i = 0 + beyond_contour_j = 0 + point_is_already_in_our_contour = .false. + point_is_already_in_beyond_pool = .false. + icccret = 0 + isc_count = 0 + plastbar = -999.0 + rlastbar = -999.0 + + if ( verb .ge. 3 ) then + print *,' ' + print *,'*-----------------------------------------------*' + print *,'* Top of check_closed_contour, ix= ',ix,' jx= ',jx + print *,'*-----------------------------------------------*' + print *,' ' + print *,'fxy(ix,jx)= ',fxy(ix,jx),' xcentval= ',xcentval + endif + +c First, set up the contour intervals that will be used. In +c the original version of this code, we used preset +c standard intervals (984,988,992,996,1000,1004....). But upon +c further review, it was decided that this was too arbitrary. +c So instead, we consider the found min (max) value to be the +c bottom (top) of the list of contour intervals. In this way, +c we can clearly specify and screen storms based on the "depth" +c of the pressure field as compared to the surroundings. + + i = 1 + do while (i <= maxconts) + if (cmaxmin == 'min') then + contvals(i) = xcentval + float(i-1)*trkrinfo%contint + i = i + 1 + else + iflip = maxconts - i + 1 + contvals(iflip) = xcentval - float(i-1)*trkrinfo%contint + i = i + 1 + endif + enddo + +c This successive_contours loop is the master loop.... + + successive_contours_loop: do while (num_found_contours < + & num_requested_contours) + +c Find the contour interval in which the center value resides. +c Note that the lower bound is included for a min check, while +c the upper bound is included for a max check. Note also that +c this subroutine can be used to find the last closed contour, +c and part of that functionality shows up in the next while +c statement where we reference "num_found_contours" in the +c array indeces for the contour values. Basically, the way we +c do this is, for example, if our central value is 990.4 mb and +c our contour interval is 4 mb, then in the first run through +c successive_contours_loop we see if we have a closed contour in +c the interval 990.4-994.4. If yes, then the next time through +c this loop, we see if we have a closed contour in the interval +c 994.4-998.4. If yes, then the next loop check is for 998.4- +c 1002.4, and so on.... We stop searching if we find a value +c that is either below the xcentval input into this subroutine +c or below the lower value of the current contour interval (this +c would mean a change in the gradient and would indicate that, +c in the case of mslp, we are heading down towards another, +c different low). + + isc_count = isc_count + 1 + + point_is_already_in_next_contour = .false. + + i = 1 + do while (i < maxconts) + if (cmaxmin == 'min') then + if (contvals(i) <= xcentval .and. xcentval < contvals(i+1)) + & then + + if ( verb .ge. 3 ) then + print *,'At A, num_found_contours= ',num_found_contours + endif + + contlo = contvals(i+num_found_contours) + conthi = contvals(i+1+num_found_contours) + + if ( verb .ge. 3 ) then + print *,'At A, contlo= ',contlo,' conthi= ',conthi + endif + exit + + endif + else + if (contvals(i) < xcentval .and. xcentval <= contvals(i+1)) + & then + contlo = contvals(i-num_found_contours) + conthi = contvals(i-num_found_contours+1) + exit + endif + endif + i = i + 1 + enddo + + if ( verb .ge. 3 ) then + print *,' ' + print *,'num_found_contours= ',num_found_contours + print *,'contlo= ',contlo,' conthi= ',conthi + print *,'xcentval= ',xcentval + endif + + +c This single_contour_scan_loop is the main loop for searching +c for one individual contour. If it is determined that a contour +c exists, control is returned to the successive_contours_loop, +c and if more contours were requested to be found, then the +c search continues onward & outward.... + + temp_mask_i_loc = 0 + temp_mask_j_loc = 0 + + iter = 1 + num_pts_in_one_contour = 0 + still_scanning = .true. + + rlast_distsum = 0.0 + rlast_distct = 0 + + single_contour_scan_loop: do while (still_scanning) + +c print *,' ' +c print *,' top of single contour scan loop' +c print *,'+++ iter= ',iter +c print *,' N1: next_contour_ct= ',next_contour_ct + + if (iter == 1 .and. num_found_contours == 0) then + ! For the first iteration, we have only the first ring, + ! which is centered on the input minimum/maximum point. + ringct = 1 + search_next_i(1) = ix + search_next_j(1) = jx + +c point_is_already_in_our_contour(ix,jx) = .true. +c num_pts_in_one_contour = num_pts_in_one_contour + 1 +c temp_mask_i_loc(num_pts_in_one_contour) = ix +c temp_mask_j_loc(num_pts_in_one_contour) = jx + + else if (iter == 1 .and. num_found_contours > 0) then + ! This is the first iteration in a *new* contour. + ! That is, we have already found 1 or more previous + ! contours while in previous iterations of + ! successive_contours_loop and we are now beginning + ! to look for the next contour. + +c print *,' N2: next_contour_ct= ',next_contour_ct + + if (next_contour_ct == 0) then + ! This would be for the special case in which, for + ! example, you've got a very intense, compact storm + ! that "skips" a contour. That is, suppose the + ! min pressure of a storm is 982 mb, and we are + ! utilizing a 4-mb contour interval, but all + ! surrounding data points are, say, 987 mb or + ! higher. Then, next_contour_ct would be 0 since no + ! data points were found in the next contour interval + ! of 982-986 mb, but we can continue searching since the + ! gradient is still sloping the correct way. The code in + ! this if statement handles this special case. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'ALERT: next_contour_ct = 0 ' + endif + + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + +c print *,'b4 ZZ, ringct= ',ringct +c print *,'at ZZ, bcc= ',beyond_contour_ct +c & ,'contlo_next= ',contlo_next +c & ,'conthi_next= ',conthi_next + + bey_con_min_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_min_loop + endif + +c print *,'-- ZZ, ibx= ',ibx,' jby= ',jby +c & ,' fxy(ibx,jby)= ',fxy(ibx,jby) + + if (fxy(ibx,jby) >= contlo_next .and. + & fxy(ibx,jby) < conthi_next) then + +c print *,'>> ZZ HIT!!, ibx= ',ibx,' jby= ',jby +c +c print *,' +++ BEYOND in NEXT: i= ',ibx,' j= ',jby +c & ,' fxy= ',fxy(ibx,jby) + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + +c print *,'.. ZZ, next_contour_ct= ',next_contour_ct + + enddo bey_con_min_loop + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + +c print *,'At A, beyond_contour_ct= ',beyond_contour_ct +c print *,' contlo_next = ',contlo_next +c print *,' conthi_next = ',conthi_next + + bey_con_max_loop: do nb = 1,beyond_contour_ct + +c print *,'in bey_con_max_loop, nb= ',nb + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle bey_con_max_loop + endif + +c print *,'ibx= ',ibx,' jby= ',jby,' data= ' +c & ,fxy(ibx,jby) + + if (fxy(ibx,jby) > contlo_next .and. + & fxy(ibx,jby) <= conthi_next) then + + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = ibx + next_contour_j(next_contour_ct) = jby + +c print *,' ++ HIT! ibx= ',ibx,' jby= ',jby + + ! This point has now been identified as being in + ! the "next" contour interval, i.e., no longer in + ! the "beyond" contour pool. Therefore, set the + ! logical flag to indicate that this point is no + ! longer in the "beyond" contour pool. + + point_is_already_in_beyond_pool(ibx,jby) = .false. + + endif + enddo bey_con_max_loop + endif + + if (next_contour_ct > 0) then + ringct = next_contour_ct + else + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! XXX next_contour_ct not > 0 !!!' + print *,'next_contour_ct= ',next_contour_ct + print *,'beyond_contour_ct= ',beyond_contour_ct + print *,'ringct= ',ringct + print *,'next_ring_ct= ',next_ring_ct + print *,'cycling to top of successive_contours_loop..' + print *,' ' + endif + + ! The number of rings that we have available to search + ! in the next contour interval is 0, so cycle all the + ! way back to the top of the outer loop, which is + ! successive_contours_loop, so that we can increase the + ! contour bounds and search inside those new bounds. + ! Again, this is for the case in which we have an + ! intense, compact storm and we are using a small + ! contour interval, such that we are essentially + ! "skipping" over one of these intervals in one of the + ! loop iterations. We need to bump up the + ! num_found_contours by one in order to increase the + ! array index in the contvals array at the top of the + ! successive_contours_loop. It is kosher to do this + ! since the reason we are cycling back to the top of + ! that loop is that we are skipping over a contour + ! interval. + + num_found_contours = num_found_contours + 1 + cycle successive_contours_loop + + endif + + else + + ringct = next_contour_ct + + endif + + do nring = 1,ringct + search_next_i(nring) = next_contour_i(nring) + search_next_j(nring) = next_contour_j(nring) +c print *,'at A, nring= ',nring,' next_contour_i(nring)= ' +c & ,next_contour_i(nring),' next_contour_j(nring)= ' +c & ,next_contour_j(nring) + enddo + + next_contour_ct = 0 + + else + ringct = next_ring_ct + endif + + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + allocate (ringposi(ringct),stat=iria) + allocate (ringposj(ringct),stat=irja) + if (iria /= 0 .or. irja /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in check_closed_contour allocating' + print *,'!!! various ring arrays. iria = ',iria + print *,'!!! irja = ',irja + print *,' ' + endif + + STOP 98 + endif + +ctm +c print *,' ' +c print *,'ringct= ',ringct + + do nring = 1,ringct + ringposi(nring) = search_next_i(nring) + ringposj(nring) = search_next_j(nring) +ctm +c print *,'nring= ',nring,' ringposi= ',ringposi(nring) +c & ,' ringposj= ',ringposj(nring) + enddo + + next_ring_ct = 0 + + ! This next loop reviews the points that have been + ! labelled for the "beyond_contour" pool. As we get further + ! into successive iterations of successive_contours_loop, + ! some of these previously "beyond" points are now within + ! the contour interval range that we are checking, so we + ! need to go through the list of "beyond" points and remove + ! any that are no longer in that "beyond" category.... + + check_beyond_loop: do nb = 1,beyond_contour_ct + + ibx = beyond_contour_i(nb) + jby = beyond_contour_j(nb) + + if (.not. point_is_already_in_beyond_pool(ibx,jby)) + & then + ! This point may have been removed already in a + ! previous iteration of successive_contours_loop. + ! If this point is no longer in our pool of "beyond + ! contour" points, then just cycle out of this + ! iteration.... + cycle check_beyond_loop + endif + + ! Check to see if any of the points being searched in the + ! upcoming multiple_ring_loop are points that had previously + ! been saved as "beyond_contour" points. If so, remove + ! their status as "beyond_contour" points by setting the + ! logical flag to false. + + do nring = 1,ringct + + if (ibx == ringposi(nring) .and. jby == ringposj(nring)) + & then +c print *,' ' +c print *,'!!! beyond remove: ibx= ',ibx,' jby= ',jby + point_is_already_in_beyond_pool(ibx,jby) = .false. + endif + + enddo + + enddo check_beyond_loop + + +c In each iteration of single_contour_scan_loop, we can have a +c different number of rings to analyze. In the first +c iteration, we only have 1 ring, the initial ring around the +c local max/min that was input to this subroutine. Subsequent +c iterations will have a variable number of rings, depending on +c how many new data points within our contour interval were +c found in the previous iteration. + + multiple_ring_loop: do mr = 1,ringct + + icenx = ringposi(mr) + jcenx = ringposj(mr) + +ctm +c print *,' --- iter= ',iter,' mr= ',mr,' icenx= ',icenx +c & ,' jcenx= ',jcenx,' imax= ',imax,' jmax= ',jmax + + call get_ijplus1_check_wrap (imax,jmax,icenx,jcenx,ixp1,jxp1 + & ,ixm1,jxm1,trkrinfo,igicwret) + + if (igicwret /= 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NO CLOSED CONTOUR: The call to ' + print *,'!!! get_ijplus1_check_wrap indicates the' + print *,'!!! max/min contour extends past the edge of' + print *,'!!! our regional grid. ' + print *,' ' + print *,' ' + endif + + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c For each individual ring, we check all 8 points surrounding +c the center point. The points are numbered for each ring as +c shown in the diagram to the right of the "select case" +c statement just below. REMEMBER: The j in our grids +c increases from north to south, so that for a global grid, +c j=1 is at 90N and j=jmax is at 90S. + + individual_ring_loop: do ir = 1,9 + + select case (ir) + case (1); irx=ixm1; jrx=jcenx;! 2 3 4 + case (2); irx=ixm1; jrx=jxm1; ! + case (3); irx=icenx;jrx=jxm1; ! + case (4); irx=ixp1; jrx=jxm1; ! 1 (icenx,jcenx) 5 + case (5); irx=ixp1; jrx=jcenx;! + case (6); irx=ixp1; jrx=jxp1; ! + case (7); irx=icenx;jrx=jxp1; ! 8 7 6 + case (8); irx=ixm1; jrx=jxp1; ! + case (9); irx=icenx; jrx=jcenx; ! = center pt of ring + end select + +c Make sure the point we are looking at has valid data. +c This is an issue only on regional grids, where we have a +c buffer of bitmapped (null) data points surrounding the +c real grid. + +c print *,'ind ring loop: ir= ',ir,' irx= ',irx,' jrx= ',jrx + + if (.not. valid_pt(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a non-' + print *,'!!! valid point, meaning we are near the' + print *,'!!! bounds of the grid, or at least the ' + print *,'!!! bounds of the valid data for this ' + print *,'!!! grid. We will skip the' + print *,'!!! search for this center.' + print *,'!!! ' + print *,'!!! (i,j) of non-valid pt = (' + & ,irx,',',jrx,')' + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c Check to make sure that the point we are looking at is +c not considered under the influence of another nearby low. + + if (masked_out(irx,jrx)) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a point' + print *,'!!! that has been masked out, meaning it' + print *,'!!! belongs under the influence of ' + print *,'!!! another nearby low, so we will skip' + print *,'!!! the search for this center....' + print *,'!!! ' + print *,'!!! Min central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Masked-out value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of masked value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we have already hit this point on a previous ring +c check, then just ignore this point and cycle past it. + + if (point_is_already_in_our_contour(irx,jrx)) then +ctm +c print *,' ' +c print *,'Pt. AAA, already-in-contour.....' +c print *,'irx= ',irx,' jrx= ',jrx + cycle individual_ring_loop + endif + +c For a MIN check, check to see if the data point is below +c the contour interval or is below the local minimum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c For a MAX check, check to see if the data point is above +c the contour interval or is above the local maximum value +c passed into this subroutine. In either case, exit and +c consider this to NOT be a closed contour. +c +c For example, for mslp, this would be as we're moving +c outward away from lower pressures to higher pressures, +c and then all of a sudden we come upon a lower pressure. +c This probably means we're heading toward another low +c pressure area, so mark the point and return to the +c calling routine. + + found_a_point_below_contour = 'n' + found_a_point_above_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) < xcentval .or. fxy(irx,jrx) < contlo) + & then + found_a_point_below_contour = 'y' + endif + else + if (fxy(irx,jrx) > xcentval .or. fxy(irx,jrx) > conthi) + & then + found_a_point_above_contour = 'y' + endif + endif + + if (found_a_point_below_contour == 'y' .or. + & found_a_point_above_contour == 'y') then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! NOTE: In check_closed_contour, while ' + print *,'!!! checking points around (icenx,jcenx)=' + print *,'!!! (',icenx,',',jcenx,'), we hit a data' + print *,'!!! value that is less (greater) than the' + print *,'!!! current contour interval bound for a' + print *,'!!! min (max) and/or is less (greater) ' + print *,'!!! than the minimum (maximum) central ' + print *,'!!! value that we are centering the ' + print *,'!!! search on.' + print *,'!!! ' + print *,'!!! Central value = ',xcentval + print *,'!!! (i,j) of central value = (',ix,',',jx,')' + print *,'!!! ' + print *,'!!! Flagged value found = ',fxy(irx,jrx) + print *,'!!! (i,j) of flagged value = (',irx,',' + & ,jrx,')' + print *,'!!! ' + print *,'!!! Lower bound of contour interval = ' + & ,contlo + print *,'!!! Upper bound of contour interval = ' + & ,conthi + print *,'!!! Contour interval = ',trkrinfo%contint + print *,'!!! ' + print *,'!!! closed_contour flag = ',closed_contour + print *,'!!! ' + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + deallocate (ringposi); deallocate (ringposj) + deallocate (search_next_i); deallocate (search_next_j) + deallocate (next_contour_i); deallocate (next_contour_j) + deallocate (beyond_contour_i) + deallocate (beyond_contour_j) + deallocate (hold_mask_i_loc) + deallocate (hold_mask_j_loc) + deallocate (temp_mask_i_loc) + deallocate (temp_mask_j_loc) + icccret = 0 + return + endif + +c If we've made it this far, then we at least know that the +c gradient is still heading in the right direction. Do the +c check now to see if the value at this point is within our +c specific contour interval (there is the possibility that +c the value is beyond our interval, which will be checked +c for just below, and if that's the case, then that point +c will be processed in a subsequent iteration of this loop +c that encompasses that correct contour interval). + + found_a_point_in_our_contour = 'n' + if (cmaxmin == 'min') then + if (fxy(irx,jrx) >= contlo .and. fxy(irx,jrx) < conthi) + & then + found_a_point_in_our_contour = 'y' + endif + else + if (fxy(irx,jrx) > contlo .and. fxy(irx,jrx) <= conthi) + & then + found_a_point_in_our_contour = 'y' + endif + endif + + if (found_a_point_in_our_contour == 'y') then + ! We've found a data point in our interval, something + ! that is inside the closed contour, and it hasn't been + ! marked as being found in a previous iteration of this + ! loop, so mark it now and store the (i,j) location so + ! that we can scan a ring around this point in a + ! successive iteration of this loop for more potential + ! points within this interval... + + point_is_already_in_our_contour(irx,jrx) = .true. + + next_ring_ct = next_ring_ct + 1 + search_next_i(next_ring_ct) = irx + search_next_j(next_ring_ct) = jrx + +c print *,'at B, next_ring_ct= ',next_ring_ct +c & ,' search_next_i()= ',search_next_i(next_ring_ct) +c & ,' search_next_j()= ',search_next_j(next_ring_ct) + + num_pts_in_one_contour = num_pts_in_one_contour + 1 + temp_mask_i_loc(num_pts_in_one_contour) = irx + temp_mask_j_loc(num_pts_in_one_contour) = jrx + + if (get_last_isobar_flag == 'y') then + call calcdist (glon(ix),glat(jx) + & ,glon(irx),glat(jrx),dist,degrees) + rlast_distsum = rlast_distsum + dist + rlast_distct = rlast_distct + 1 + endif + +ctm +c print *,' ' +c print *,' PT IN! irx= ',irx,' jrx= ',jrx,' xval= ' +c & ,fxy(irx,jrx) +c print *,'next_ring_ct= ',next_ring_ct +c print *,'num_pts_in_one_contour= ' +c & ,num_pts_in_one_contour + endif + +c If we've made it this far AND the +c found_a_point_in_our_contour flag indicates that this +c point is not in our contour interval, then by default that +c means that this point is for a contour interval beyond +c what we're currently looking at. E.g., if we're looking +c at the contours around a 972 mb low and we're moving +c outward and currently checking the 984-988 mb contour +c interval, it means that we found, say, a gridpoint with +c 991 mb. So we want to mark that point for a future +c iteration of this loop that would be checking the +c 988-992 mb contour interval. + + if (found_a_point_in_our_contour /= 'y' .and. + & .not. point_is_already_in_next_contour(irx,jrx)) then + ! We've found a data point that is beyond our interval, + ! so this is not a concern for finding the bounds of + ! our current contour interval, but we want to mark + ! these points and remember them for the next iteration + ! of successive_scan_loop. (For example, suppose we + ! are currently searching for points in the 984-988 mb + ! range, and we find a point that is 990 -- mark it + ! here to be remembered when we scan for 988-992 mb). + if (cmaxmin == 'min') then + contlo_next = conthi + conthi_next = conthi + trkrinfo%contint + if (fxy(irx,jrx) >= contlo_next .and. + & fxy(irx,jrx) < conthi_next) then + ! "NEXT_CONTOUR" Comment: + ! We've found a point that is in the very next + ! contour interval.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. + else if (fxy(irx,jrx) >= conthi_next) then + ! "BEYOND_CONTOUR" Comment: + ! This point is at least 1 contour interval beyond + ! the next contour interval. Dump the info into + ! these i and j arrays. This info will be used if + ! in the next iteration of single_contour_scan_loop, + ! next_contour_ct = 0. That would mean that we + ! have, e.g., an intensely deep low with a sharp + ! mslp gradient that essentially "skips" over a + ! contour interval. E.g., if using a 4 mb interval, + ! we go from 947 to 953 AND there are NO + ! intervening gridpoints in the 948-952 interval. + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) + endif + else + contlo_next = contlo - trkrinfo%contint + conthi_next = contlo + if (fxy(irx,jrx) > contlo_next .and. + & fxy(irx,jrx) <= conthi_next) then + ! See "NEXT_CONTOUR" comment just above.... + next_contour_ct = next_contour_ct + 1 + next_contour_i(next_contour_ct) = irx + next_contour_j(next_contour_ct) = jrx + point_is_already_in_next_contour(irx,jrx) = .true. +c print *,'NEXT ncc= ',next_contour_ct +c & ,'next_contour_i()= ' +c & ,next_contour_i(next_contour_ct) +c & ,'next_contour_j()= ' +c & ,next_contour_j(next_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + else if (fxy(irx,jrx) <= contlo_next) then + ! See "BEYOND_CONTOUR" comment just above.... + beyond_contour_ct = beyond_contour_ct + 1 + beyond_contour_i(beyond_contour_ct) = irx + beyond_contour_j(beyond_contour_ct) = jrx + point_is_already_in_beyond_pool(irx,jrx) = .true. +c print *,'BEYOND bcc= ',beyond_contour_ct +c & ,'beyond_contour_i()= ' +c & ,beyond_contour_i(beyond_contour_ct) +c & ,'beyond_contour_j()= ' +c & ,beyond_contour_j(beyond_contour_ct) +c & ,' fxy= ',fxy(irx,jrx) + endif + endif + endif + + enddo individual_ring_loop + + enddo multiple_ring_loop + + if (next_ring_ct > 0) then + iter = iter + 1 + else + icccret = 0 + still_scanning = .false. + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) + num_found_contours = num_found_contours + 1 + closed_contour = 'y' + if (num_found_contours == 1) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Closed contour found ' + endif + + endif + endif + + enddo single_contour_scan_loop + + do insingle = 1,num_pts_in_one_contour + num_pts_in_all_contours = num_pts_in_all_contours + 1 + inall = num_pts_in_all_contours + hold_mask_i_loc(inall) = temp_mask_i_loc(insingle) + hold_mask_j_loc(inall) = temp_mask_j_loc(insingle) + enddo + + if (get_last_isobar_flag == 'y') then + if (cmaxmin == 'min') then + plastbar = conthi + else + plastbar = contlo + endif + if (rlast_distct > 0) then + rlastbar = rlast_distsum / float(rlast_distct) + rlastbar = rlastbar * 0.539638 ! convert km to nm + else + rlastbar = -999.0 + endif + endif + + enddo successive_contours_loop + + if ( verb .ge. 3 ) then + print *,' ' + print *,'END SUM: num of iterations = ',isc_count + endif + + do nm = 1,num_pts_in_all_contours + im = hold_mask_i_loc(nm) + jm = hold_mask_j_loc(nm) + masked_out(im,jm) = .true. + enddo + + if (allocated(search_next_i)) deallocate (search_next_i) + if (allocated(search_next_j)) deallocate (search_next_j) + if (allocated(next_contour_i)) deallocate (next_contour_i) + if (allocated(next_contour_j)) deallocate (next_contour_j) + if (allocated(beyond_contour_i)) deallocate (beyond_contour_i) + if (allocated(beyond_contour_j)) deallocate (beyond_contour_j) + if (allocated(hold_mask_i_loc)) deallocate (hold_mask_i_loc) + if (allocated(hold_mask_j_loc)) deallocate (hold_mask_j_loc) + if (allocated(temp_mask_i_loc)) deallocate (temp_mask_i_loc) + if (allocated(temp_mask_j_loc)) deallocate (temp_mask_j_loc) + if (allocated(ringposi)) deallocate (ringposi) + if (allocated(ringposj)) deallocate (ringposj) +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine check_land_mask (imax,jmax,ix,jx,fract_land,valid_pt + & ,dx,dy,point_is_over_water,iclmret) +c +c ABSTRACT: This subroutine looks at the values for the land-sea +c mask surrounding an input (i,j) position to determine if less +c than 50% of the area surrounding the input (i,j) position within +c 75 km radius is land. +c +c INPUT: +c imax Num pts in i-direction on grid +c jmax Num pts in j-direction on grid +c ix i index for location of local max or min +c jx j index for location of local max or min +c valid_pt Logical; bitmap indicating if valid data at that pt +c dx Grid spacing in x-direction +c dy Grid spacing in y-direction +c +c OUTPUT: +c fract_land Fraction of points/area that is covered by land +c point_is_over_water y/n: A value of 'y' is returned if <50% +c of the points/area is covered by land +c iclmret Return code from this routine +c + USE grid_bounds; USE tracked_parms + USE trkrparms; USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + + integer date_time(8) + character (len=10) big_ben(3) + + logical(1) valid_pt(imax,jmax) + character point_is_over_water*1 + integer, parameter :: numazim=8 + integer iazim,ibiret1,imax,jmax,ix,jx,iclmret,imct + real bear,targlat,targlon,xplon,yplat,rdist,xintrp_mask + real fract_land,dx,dy,xmask_sum +c + iclmret = 0 + +c First, calculate the longitude and latitude of the input ix and +c jx points. If the xplon value ends up being >360.0 (this can +c happen for basin-scale HWRF), don't worry about it. Just leave +c it be, as the trigonometry will work out the same for lons >360. + + xplon = glonmin + (ix-1)*dx + yplat = glatmax - (jx-1)*dy + + rdist = 75.0 ! (We will always look only 75 km radius out for + ! this particular land-sea mask application) + + imct = 0 + +c Now go around the storm via azimloop and get interpolated +c values of the land-sea mask at each azimuth at a radial +c distance of 75 km from the center point.... + + xmask_sum = 0.0 + + azimloop: do iazim = 1,numazim + + bear = ((iazim-1) * 15.) + 45.0 + + call distbear (yplat,xplon,rdist,bear,targlat,targlon) + + ! These calls to bilin_int_uneven pass a variable, level, + ! that is used for applications of interpolating wind + ! data. Here, we are instead interpolating the land-sea + ! mask data, so we don't care about the level, so just + ! pass a dummy value of 850, which never gets used. + + call bilin_int_uneven (targlat,targlon + & ,dx,dy,imax,jmax,trkrinfo,850,'m',xintrp_mask,ibiret1) + + if (ibiret1 == 0) then + xmask_sum = xmask_sum + xintrp_mask + imct = imct + 1 + else + iclmret = 95 + return + endif + + enddo azimloop + +c Now get the mask value directly at the point that was input to +c this routine.... + + xmask_sum = xmask_sum + lsmask(ix,jx) + imct = imct + 1 + +c Now get the mean land fraction.... + + if (imct > 0) then + + fract_land = xmask_sum / float(imct) + if (fract_land < 0.50) then + point_is_over_water = 'y' + if ( verb .ge. 3 ) then + print *,' ' + print *,'+++ Land check yes: Point is over water. ' + print *,' Land check value: fract_land= ',fract_land + endif + else + point_is_over_water = 'n' + if ( verb .ge. 3 ) then + print *,' ' + print *,'!!! Land check NO: Point is over land. ' + print *,' Land check value: fract_land= ',fract_land + endif + endif + + else + + iclmret = 95 + return + + endif +c + return + end +c +c--------------------------------------------------------------------- +c +c--------------------------------------------------------------------- + subroutine get_ijplus1_check_wrap (imax,jmax,i,j,iplus1,jplus1 + & ,iminus1,jminus1,trkrinfo,igicwret) +c +c ABSTRACT: This subroutine takes an (i,j) position input and +c returns the four neighboring (i,j) points to the east, south, +c west and north. The routine checks for wrap around the GM, so +c that if, for example, you are on a global 360x181 grid and you +c are at point i=360, then i+1 = 361, so you need something to +c adjust that back to i = 1. Likewise, if you are at i=1 and +c looking for point i-1, it will adjust it to be point 360 +c instead of the meaningless point 0 (i=0). + + USE trkrparms + USE verbose_output + + implicit none + + type (trackstuff) trkrinfo + integer i,j,imax,jmax,iplus1,jplus1,iminus1,jminus1,igicwret + + igicwret = 0 + + jplus1 = j + 1 + jminus1 = j - 1 + iplus1 = i + 1 + iminus1 = i - 1 + + if (iplus1 > imax) then + if (trkrinfo%gridtype == 'global') then + iplus1 = iplus1 - imax ! If wrapping east of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested eastern search boundary' + print *,'!!! is too close to the eastern bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested eastern ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! imax of regional grid = ',imax + print *,'!!! User-requested eastern i = ',iplus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (iminus1 < 1) then + if (trkrinfo%gridtype == 'global') then + iminus1 = imax + iminus1 ! If wrapping west of GM + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The' + print *,'!!! user-requested western search boundary' + print *,'!!! is too close to the western bounds of' + print *,'!!! this regional grid. When we check ' + print *,'!!! neighboring points, we are going past' + print *,'!!! the edge of the grid by one point. ' + print *,'!!! Cut back your requested western ' + print *,'!!! boundary by a degree or 2 in the ' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested western i = ',iminus1 + print *,' ' + endif + + igicwret = 98 + return + endif + endif + + if (jplus1 > jmax .or. jminus1 < 1) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR: Error in get_ijplus1_check_wrap. The ' + print *,'!!! user-requested northern or southern search' + print *,'!!! boundary is too close to the bounds of the' + print *,'!!! grid. Cut back your requested northern or' + print *,'!!! southern boundary by a degree or 2 in the' + print *,'!!! script and resubmit....' + print *,'!!! ' + print *,'!!! User-requested northern j = ',jminus1 + print *,'!!! User-requested southern j = ',jplus1 + print *,'!!! jmax of grid = ',jmax + print *,' ' + endif + + igicwret = 91 + return + endif + + return + end + +c------------------------------------------------------------------ +c +c------------------------------------------------------------------ + SUBROUTINE qsort(x,ind,n) +c +c Code converted using TO_F90 by Alan Miller +c Date: 2002-12-18 Time: 11:55:47 + + IMPLICIT NONE + INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(12, 60) + + REAL (dp), INTENT(IN) :: x(n) + INTEGER, INTENT(OUT) :: ind(n) + INTEGER, INTENT(IN) :: n + +c *************************************************************************** +c +c ROBERT RENKA +c OAK RIDGE NATL. LAB. +c +c THIS SUBROUTINE USES AN ORDER N*LOG(N) QUICK SORT TO SORT A REAL (dp) +c ARRAY X INTO INCREASING ORDER. THE ALGORITHM IS AS FOLLOWS. IND IS +c INITIALIZED TO THE ORDERED SEQUENCE OF INDICES 1,...,N, AND ALL INTERCHANGES +c ARE APPLIED TO IND. X IS DIVIDED INTO TWO PORTIONS BY PICKING A CENTRAL +c ELEMENT T. THE FIRST AND LAST ELEMENTS ARE COMPARED WITH T, AND +c INTERCHANGES ARE APPLIED AS NECESSARY SO THAT THE THREE VALUES ARE IN +c ASCENDING ORDER. INTERCHANGES ARE THEN APPLIED SO THAT ALL ELEMENTS +c GREATER THAN T ARE IN THE UPPER PORTION OF THE ARRAY AND ALL ELEMENTS +c LESS THAN T ARE IN THE LOWER PORTION. THE UPPER AND LOWER INDICES OF ONE +c OF THE PORTIONS ARE SAVED IN LOCAL ARRAYS, AND THE PROCESS IS REPEATED +c ITERATIVELY ON THE OTHER PORTION. WHEN A PORTION IS COMPLETELY SORTED, +c THE PROCESS BEGINS AGAIN BY RETRIEVING THE INDICES BOUNDING ANOTHER +c UNSORTED PORTION. +c +c INPUT PARAMETERS - N - LENGTH OF THE ARRAY X. +c +c X - VECTOR OF LENGTH N TO BE SORTED. +c +c IND - VECTOR OF LENGTH >= N. +c +c N AND X ARE NOT ALTERED BY THIS ROUTINE. +c +c OUTPUT PARAMETER - IND - SEQUENCE OF INDICES 1,...,N PERMUTED IN THE SAME +c FASHION AS X WOULD BE. THUS, THE ORDERING ON +c X IS DEFINED BY Y(I) = X(IND(I)). +c +c ********************************************************************* + + ! NOTE -- IU AND IL MUST BE DIMENSIONED >= LOG(N) WHERE LOG HAS BASE 2. + + !********************************************************************* + + INTEGER :: iu(21), il(21) + INTEGER :: m, i, j, k, l, ij, it, itt, indx + REAL :: r + REAL (dp) :: t + + ! LOCAL PARAMETERS - + + ! IU,IL = TEMPORARY STORAGE FOR THE UPPER AND LOWER + ! INDICES OF PORTIONS OF THE ARRAY X + ! M = INDEX FOR IU AND IL + ! I,J = LOWER AND UPPER INDICES OF A PORTION OF X + ! K,L = INDICES IN THE RANGE I,...,J + ! IJ = RANDOMLY CHOSEN INDEX BETWEEN I AND J + ! IT,ITT = TEMPORARY STORAGE FOR INTERCHANGES IN IND + ! INDX = TEMPORARY INDEX FOR X + ! R = PSEUDO RANDOM NUMBER FOR GENERATING IJ + ! T = CENTRAL ELEMENT OF X + + IF (n <= 0) RETURN + + ! INITIALIZE IND, M, I, J, AND R + + DO i = 1, n + ind(i) = i + END DO + m = 1 + i = 1 + j = n + r = .375 + + ! TOP OF LOOP + + 20 IF (i >= j) GO TO 70 + IF (r <= .5898437) THEN + r = r + .0390625 + ELSE + r = r - .21875 + END IF + + ! INITIALIZE K + + 30 k = i + + ! SELECT A CENTRAL ELEMENT OF X AND SAVE IT IN T + + ij = i + r*(j-i) + it = ind(ij) + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) > t) THEN + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + END IF + + ! INITIALIZE L + + l = j + + ! IF THE LAST ELEMENT OF THE ARRAY IS LESS THAN T, + ! INTERCHANGE IT WITH T + indx = ind(j) + IF (x(indx) >= t) GO TO 50 + ind(ij) = indx + ind(j) = it + it = indx + t = x(it) + + ! IF THE FIRST ELEMENT OF THE ARRAY IS GREATER THAN T, + ! INTERCHANGE IT WITH T + + indx = ind(i) + IF (x(indx) <= t) GO TO 50 + ind(ij) = indx + ind(i) = it + it = indx + t = x(it) + GO TO 50 + + ! INTERCHANGE ELEMENTS K AND L + + 40 itt = ind(l) + ind(l) = ind(k) + ind(k) = itt + + ! FIND AN ELEMENT IN THE UPPER PART OF THE ARRAY WHICH IS + ! NOT LARGER THAN T + + 50 l = l - 1 + indx = ind(l) + IF (x(indx) > t) GO TO 50 + + ! FIND AN ELEMENT IN THE LOWER PART OF THE ARRAY WHCIH IS NOT SMALLER THAN T + + 60 k = k + 1 + indx = ind(k) + IF (x(indx) < t) GO TO 60 + + ! IF K <= L, INTERCHANGE ELEMENTS K AND L + + IF (k <= l) GO TO 40 + + ! SAVE THE UPPER AND LOWER SUBSCRIPTS OF THE PORTION OF THE + ! ARRAY YET TO BE SORTED + + IF (l-i > j-k) THEN + il(m) = i + iu(m) = l + i = k + m = m + 1 + GO TO 80 + END IF + + il(m) = k + iu(m) = j + j = l + m = m + 1 + GO TO 80 + + + ! BEGIN AGAIN ON ANOTHER UNSORTED PORTION OF THE ARRAY + + 70 m = m - 1 + IF (m == 0) RETURN + i = il(m) + j = iu(m) + + 80 IF (j-i >= 11) GO TO 30 + IF (i == 1) GO TO 20 + i = i - 1 + + ! SORT ELEMENTS I+1,...,J. NOTE THAT 1 <= I < J AND J-I < 11. + + 90 i = i + 1 + IF (i == j) GO TO 70 + indx = ind(i+1) + t = x(indx) + it = indx + indx = ind(i) + IF (x(indx) <= t) GO TO 90 + k = i + + 100 ind(k+1) = ind(k) + k = k - 1 + indx = ind(k) + IF (t < x(indx)) GO TO 100 + + ind(k+1) = it + GO TO 90 + END SUBROUTINE qsort + +c +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT11/FORT31 + subroutine open_grib_files (inp,lugb,lugi,gfilename,ifilename + & ,lout,iret) + +C ABSTRACT: This subroutine must be called before any attempt is +C made to read from the input GRIB files. The GRIB and index files +C are opened with a call to baopenr. This call to baopenr was not +C needed in the cray version of this program (the files could be +C opened with a simple Cray assign statement), but the GRIB-reading +C utilities on the SP do require calls to this subroutine (it has +C something to do with the GRIB I/O being done in C on the SP, and +C the C I/O package needs an explicit open statement). +C +C INPUT: +c inp Contains user-input info on the date & data +C lugb The Fortran unit number for the GRIB data file +C lugi The Fortran unit number for the GRIB index file +c ifh integer index for lead time level +c gfilename If using individual files for each tau, gfilename will +c contain the grib data filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +c ifilename If using individual files for each tau, gfilename will +c contain the grib index filename for this tau. Otherwise, +c if using one big file for all taus, this contains dummy +c character data. +C lout The Fortran unit number for the output grib file +C +C OUTPUT: +C iret The return code from this subroutine + + USE inparms + USE verbose_output + + implicit none +c + type (datecard) inp + + logical(1) output_file_open + logical(1) file_open + character(*) gfilename,ifilename +c character(120) gopen_g_file,gopen_i_file +cPeng 05/28/2018 Bug Fixed for FV3-GFS Job Crashed. + character(255) gopen_g_file,gopen_i_file + character(2) lugb_c,lugi_c + character(6) enameb,enamei + integer igoret,iioret,iooret,lugb,lugi,lout,iret,nlen1,nlen2 + + iret=0 + + if (inp%file_seq == 'onebig') then + write(lugb_c,'(i2)')lugb + write(lugi_c,'(i2)')lugi + enameb='FORT'//adjustl(lugb_c) + enamei='FORT'//adjustl(lugi_c) + call get_environment_variable(enameb,gopen_g_file,status=igoret) + call get_environment_variable(enamei,gopen_i_file,status=iioret) + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + gopen_g_file(1:5) = "fort." + gopen_i_file(1:5) = "fort." + write(gopen_g_file(6:7),'(I2)') lugb + write(gopen_i_file(6:7),'(I2)') lugi + endif + else + nlen1 = len_trim(gfilename) + gopen_g_file = trim(gfilename(1:nlen1)) + nlen2 = len_trim(ifilename) + gopen_i_file = trim(ifilename(1:nlen2)) + endif + call baopenr (lugb,gopen_g_file,igoret) + call baopenr (lugi,gopen_i_file,iioret) + +c print *,'gopen_g_file= ',gopen_g_file,'....' +c print *,'gopen_i_file= ',gopen_i_file,'....' + + inquire (unit=lugb, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugb= ',lugb + & ,' is CLOSED' + endif + + inquire (unit=lugi, opened=file_open) + if (file_open) then + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is OPEN' + else + print *,'TEST open_grib_files, unit lugi= ',lugi + & ,' is CLOSED' + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'baopen: igoret= ',igoret,' iioret= ',iioret + & ,' iooret= ',iooret + endif + + if (igoret /= 0 .or. iioret /= 0 .or. iooret /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in sub open_grib_files opening grib file' + print *,'!!! or grib index file. baopen return codes:' + print *,'!!! grib file return code = igoret = ',igoret + print *,'!!! index file return code = iioret = ',iioret + print *,'!!! output file return code = iooret = ',iooret + endif + + iret = 113 + return + endif + + return + end + +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT12 + subroutine read_tcv_card (lucard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in the updated TC Vitals file +c for the current time and prints out those cards (storms) +c that have been selected to be processed. It also +c takes the initial positions from the tcv card for each +c storm and puts them into the slonfg & slatfg arrays. +c Note that this routine is reading in vitals in the +c standard format for TCs only. Any genesis vitals are +c read in in subroutine read_gen_vitals. +c +c INPUT: +c lucard integer unit number for tcvitals card +c trkrinfo derived type that contains info on the type of +c tracker run that we are performing. +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c numtcv number of storms read off of the input tcvitals file +c iret return code from this subroutine +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) + + USE def_vitals; USE set_max_parms; USE trkrparms + USE verbose_output + + implicit none + + type (tcvcard) tmpstorm(maxstorm_tc) + type (trackstuff) trkrinfo + integer isa,issa,ioa,iaa,ita,iret,ict,maxstorm + integer i,ii,lucard,numtcv +c------ + ii=1 + do while (.true. .and. ii <= maxstorm_tc) + read (lucard,21,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + 21 format (a4,1x,a3,1x,a9,1x,i8,1x,i4,1x,i3,a1,1x,i4,a1,1x,i3,1x + & ,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + numtcv = ii - 1 + + if (trkrinfo%type == 'midlat' .or. trkrinfo%type == 'tcgen') then + ! For the mid-latitude or tc genesis cases, the max number + ! of storms (maxstorm) allowed to be tracked throughout a + ! forecast is defined in module set_max_parms. + + if ( verb .ge. 3 ) then + print *,' ' + print *,'In read_tcv_card, tracker type of "midlat" or ' + print *,'"tcgen" indicates that this run of the tracker is' + print *,'for a midlat or a tcgen case....' + endif + + maxstorm = maxstorm_mg + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + slonfg = 0.0; slatfg = 0.0 + stcvtype = 'FOF' ! Found On the Fly by tracker (not on tcvitals) + stormswitch = 3 ! Initialize whole array to case of '3' + if (numtcv > 0) then + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the already-existing storms that' + print *,'were read in from the tc vitals file: ' + print *,' ' + endif + + ict = 0 + do i=1,numtcv + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + stcvtype(i) = 'TCV' ! Storm listed on tcvitals + +c if (trkrinfo%type == 'midlat') then +c storm(i)%tcv_center = 'MIDL' +c else if (trkrinfo%type == 'tcgen') then +c storm(i)%tcv_center = 'TCG ' +c endif +c write (storm(i)%tcv_storm_id,'(i4.4)') i +c write (storm(i)%tcv_storm_name,'(i4.4)') i + + enddo + endif + iret=0 + return + else + ! For the tracker cases, the max number of storms (maxstorm) + ! allowed to be tracked throughout a forecast is defined by + ! the number of vitals read in above. + maxstorm = numtcv + allocate (stormswitch(maxstorm),stat=isa) + allocate (storm(maxstorm),stat=issa) + allocate (slonfg(maxstorm,maxtime),stat=ioa) + allocate (slatfg(maxstorm,maxtime),stat=iaa) + allocate (stcvtype(maxstorm),stat=ita) + if (isa /= 0 .or. ioa /= 0 .or. iaa /= 0 .or. issa /= 0 .or. + & ita /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card allocating stormswitch,' + print *,'!!! slonfg, storm, slatfg or stcvtype arrays. ' + print *,'!!! isa = ',isa,' ioa= ',ioa,' iaa= ',iaa,' issa= ' + print *,'!!! ',issa,' ita= ',ita + endif + + iret = 97 + return + endif + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the storms to be processed: ' + print *,' ' + endif + + slonfg = 0.0; slatfg = 0.0 + stcvtype = ' ' ! Not needed for regular tracker run.... + ict=0 + do i=1,maxstorm + stormswitch(i) = 1 + storm(i) = tmpstorm(i) + ict = ict + 1 + + if ( verb .ge. 3 ) then + write (*,31) storm(i) + endif + + if (storm(i)%tcv_lonew == 'W') then + slonfg(i,1) = 360. - float(storm(i)%tcv_lon)/10.0 + else + slonfg(i,1) = float(storm(i)%tcv_lon)/10.0 + endif + if (storm(i)%tcv_latns == 'S') then + slatfg(i,1) = -1. * float(storm(i)%tcv_lat)/10.0 + else + slatfg(i,1) = float(storm(i)%tcv_lat)/10.0 + endif + enddo + + if (ict.gt.0) then + iret = 0 + return + else + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_tcv_card, num storms to be ' + print *,'!!! processed is not greater than 0 for a tracker' + print *,'!!! case. Check to see that you have the Fortran' + print *,'!!! unit assigned right in your script.' + endif + + iret = 99 + return + endif + + endif + + 31 format (a4,1x,a3,1x,a9,1x,i8.8,1x,i4.4,1x,i3,a1,1x,i4,a1,1x + & ,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_tcv_card reading unit ',lucard + endif + + iret = 98 +c + return + end +c----------------------------------------------------------------------- +c Added by JPENG 03/21/2018, regarding FTN compiler with FORT14 + subroutine read_gen_vitals (lgvcard,maxstorm,trkrinfo,numtcv,iret) +c +c ABSTRACT: This subroutine reads in a modified TC Vitals file +c for the current time and prints out those cards (storms) that +c have been selected to be processed. It also takes the initial +c positions from the tcv card for each storm and puts them into +c the slonfg & slatfg arrays. +c +c The reason that these are referred to as modified tcvitals is +c that the format is different from standard TC vitals format. +c These vitals are created by a previous run of this tracker +c executable, and the storm identifier is different than that +c for a standard tcvitals. The storm +c identifier contains the date/time that the storm was first +c identified, and the lat/lon position at which it was first +c identified. +c +c EXAMPLE: The following is a standard TC Vitals record, split +c up over 3 lines: +c +c NHC 01L ALBERTO 20060614 1200 343N 0807W 035 093 1004 1012 +c 0278 15 222 -999 -999 -999 -999 M -999 -999 -999 -999 72 +c 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: The following is the format for the "genesis" vitals, +c split over 3 lines, for the same system: +c +c 2006061000_F000_210N_0853W_01L 20060614 1200 343N 0807W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c EXAMPLE: If the vitals record is for a non-officially numbered +c system (i.e., any system that's not a TC being tracked +c by NHC or JTWC), then the storm number is replaced +c by the characters "FOF", for "Found On the Fly" by +c the tracker. +c +c 2006071500_F000_150N_0681W_FOF 20060718 1200 185N 0792W 035 093 +c 1004 1012 0278 15 222 -999 -999 -999 -999 M -999 -999 +c -999 -999 72 520N 410W -999 -999 -999 -999 +c +c NOTE: The "F000" in there at character positions 12-15 are to +c indicate the forecast hour within that forecast cycle +c that the storm was first detected. For a vitals record, +c this is always going to be 000 for fhr=0h, and really, +c it's not even needed. However, I'm keeping it in there +c in order to keep the storm ID format exactly the same +c as the output_atcf_sink forecast track record, which +c does have a use for that "FXXX" identifier in the +c output. +c +c INPUT: +c lgvcard integer unit number for tcgen-tcvitals card +c +c OUTPUT: +c maxstorm max # of storms to be handled for this case +c iret return code from this subroutine +c +c INPUT/OUTPUT: +c numtcv As an input, this variable contains the number of +c *tropical* cyclone vitals (i.e., regular tcvitals) that +c were read off of the input tcvitals file in subroutine +c read_tcv_card. This variable will be incremented for +c each "modified" vitals record that is read in this +c subroutine, and so as output, this variable will +c contain the combined total of tcvitals and modified +c vitals records. +c +c OTHER: +c stormswitch 1,2 or 3 (see more description under Main pgm section) +c slonfg first guess array for longitude +c slatfg first guess array for latitude +c storm contains the tcvitals info +c (storm, stormswitch, slonfg and slatfg are allocatable and are +c defined in module def_vitals) +c + USE def_vitals; USE set_max_parms; USE trkrparms; USE gen_vitals + USE verbose_output + + implicit none + + type (gencard) tmpstorm(maxstorm_mg) + type (trackstuff) trkrinfo + integer iret,maxstorm + integer i,ii,lgvcard,numtcv,num_mod_vit,vitix,iga +c------ + ! Read in all of the "genesis vitals" into a temp array. The + ! index for the first array member is one past the number of + ! tc vitals that were read in in subroutine read_tcv_card. + ii = numtcv + 1 + do while (.true. .and. ii <= maxstorm_mg) + read (lgvcard,24,END=801,ERR=891) tmpstorm(ii) + ii = ii + 1 + enddo + + 24 format (i10,2x,i3,1x,i3,a1,1x,i4,a1,1x,a3,1x,i8,1x,i4,1x,i3,a1,1x + & ,i4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x,4(i4,1x),a1) + + 801 continue + + num_mod_vit = ii - numtcv - 1 + + allocate (gstorm(maxstorm_mg),stat=iga) + if (iga /= 0) then + + if ( verb .ge. 1 ) then + print *,' ' + print *,'!!! ERROR in read_gen_vitals allocating gstorm array' + print *,'!!! iga = ',iga + endif + + iret = 97 + return + endif + + ! Initialize all "genesis dates" to 99999. Any new genesis + ! vitals that are read in below will bring in real dates, and + ! then we can test the date in output_gen_vitals to know if a + ! storm was already defined or not at the beginning of this + ! executable or if it was a new storm that was found. + + do i = 1,maxstorm_mg + gstorm(i)%gv_gen_date = 99999 + enddo + + ! If there are any TC vitals (i.e., officially named TCs + ! that are being numbered/tracked by either NHC or JTWC), then + ! we want to take the important information from those vitals + ! and put that into genesis vitals. This will enable us to + ! output *all* of these systems in the "gen_vitals" or + ! "gstorm" format. The one difference here is that for the + ! genesis date, we use the starting date of this forecast, not + ! the time that the storm first formed. Also, set the genesis + ! forecast hour (gv_gen_fhr) to be 0 for TCs that have a + ! TC vitals record. + + if (numtcv > 0) then + do i = 1,numtcv + gstorm(i)%gv_gen_date = storm(i)%tcv_ymd * 100 + + & storm(i)%tcv_hhmm / 100 + gstorm(i)%gv_gen_fhr = 0 + gstorm(i)%gv_gen_lat = storm(i)%tcv_lat + gstorm(i)%gv_gen_latns = storm(i)%tcv_latns + gstorm(i)%gv_gen_lon = storm(i)%tcv_lon + gstorm(i)%gv_gen_lonew = storm(i)%tcv_lonew + gstorm(i)%gv_gen_type = storm(i)%tcv_storm_id + gstorm(i)%gv_obs_ymd = storm(i)%tcv_ymd + gstorm(i)%gv_obs_hhmm = storm(i)%tcv_hhmm + gstorm(i)%gv_obs_lat = storm(i)%tcv_lat + gstorm(i)%gv_obs_latns = storm(i)%tcv_latns + gstorm(i)%gv_obs_lon = storm(i)%tcv_lon + gstorm(i)%gv_obs_lonew = storm(i)%tcv_lonew + if ( verb .ge. 3 ) then + write (*,34) gstorm(i) + endif + enddo + endif + + if (num_mod_vit > 0) then + + if ( verb .ge. 3 ) then + print *,' ' + print *,'Following are the vitals for storms that were' + print *,'read in from the modified (genesis) tc vitals file: ' + print *,' ' + endif + + do i=1,num_mod_vit + vitix = i + numtcv + stormswitch(vitix) = 1 + ! On the following line we are filling the array gstorm, + ! which is new in this subroutine. Note, however, that we + ! are not necessarily starting it at 1, but at the point in + ! the array after any TC Vitals may have been read in. + gstorm(vitix) = tmpstorm(vitix) + + if ( verb .ge. 3 ) then + write (*,34) gstorm(vitix) + endif + + ! For the sake of consistency (and sanity!!), we need to also + ! use the same "storm" array as was used in read_tcv_card, + ! since this "storm" array is used often throughout the rest + ! of this executable. + + write (storm(vitix)%tcv_storm_id,'(i4.4)') vitix + write (storm(vitix)%tcv_storm_name,'(i4.4)') vitix + storm(vitix)%tcv_ymd = gstorm(vitix)%gv_obs_ymd + storm(vitix)%tcv_hhmm = gstorm(vitix)%gv_obs_hhmm + storm(vitix)%tcv_lat = gstorm(vitix)%gv_obs_lat + storm(vitix)%tcv_latns = gstorm(vitix)%gv_obs_latns + storm(vitix)%tcv_lon = gstorm(vitix)%gv_obs_lon + storm(vitix)%tcv_lonew = gstorm(vitix)%gv_obs_lonew + storm(vitix)%tcv_stdir = gstorm(vitix)%gv_stdir + storm(vitix)%tcv_stspd = gstorm(vitix)%gv_stspd + + if (trkrinfo%type == 'midlat') then + storm(vitix)%tcv_center = 'MIDL' + else if (trkrinfo%type == 'tcgen') then + storm(vitix)%tcv_center = 'TCG ' + endif + + if (gstorm(vitix)%gv_obs_lonew == 'W') then + slonfg(vitix,1) = 360. - float(gstorm(vitix)%gv_obs_lon) + & / 10.0 + else + slonfg(vitix,1) = float(gstorm(vitix)%gv_obs_lon)/10.0 + endif + if (gstorm(vitix)%gv_obs_latns == 'S') then + slatfg(vitix,1) = -1. * float(gstorm(vitix)%gv_obs_lat)/10.0 + else + slatfg(vitix,1) = float(gstorm(vitix)%gv_obs_lat)/10.0 + endif + stcvtype(vitix) = 'FOF' ! Storm "Found On the Fly" by tracker + + enddo + endif + + 34 format (i10,1x,'F',i3.3,1x,i3.3,a1,1x,i4.4,a1,1x,a3,1x,i8,1x,i4.4 + & ,1x,i3.3,a1,1x,i4.4,a1,1x,i3,1x,i3,3(1x,i4),1x,i2,1x,i3,1x + & ,4(i4,1x),a1) + +c Update the total number of vitals that have been read in + + numtcv = numtcv + num_mod_vit + + goto 895 +c + 891 continue + + if ( verb .ge. 1 ) then + print *,'!!! ERROR in read_gen_vitals reading unit ',lgvcard + endif + + iret = 98 + + 895 continue +c + return + end diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/gettrk_main_gfs.f_original b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_main_gfs.f_original similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/gettrk_main_gfs.f_original rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_main_gfs.f_original diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/gettrk_modules.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_modules.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/gettrk_modules.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/gettrk_modules.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile index 93fbb373bb..d10da4fbe1 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc +FCOMP= mpiifort -nofree +CCOMP= mpiicc FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) CFLAGS= -O3 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_hera similarity index 85% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_hera index 9eed740cd4..c305c9f348 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_hera @@ -1,10 +1,10 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc -FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 -CFLAGS= -O2 -axSSE4.2,AVX,CORE-AVX2 +FCOMP= mpiifort -nofree +CCOMP= mpiicc +FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) +CFLAGS= -O2 gettrk_gfs: gettrk_main_gfs.f gettrk_modules.o module_waitfor.o cwaitfor.o @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_jet new file mode 100644 index 0000000000..b222f9874e --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_jet @@ -0,0 +1,37 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +FCOMP= mpiifort -nofree +CCOMP= mpiicc +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +CFLAGS= -O3 -axSSE4.2,AVX,CORE-AVX2 + +gettrk_gfs: gettrk_main_gfs.f gettrk_modules.o module_waitfor.o cwaitfor.o + @echo " " + @echo " Compiling the main tracking program and subroutines....." + $(FCOMP) $(FFLAGS) gettrk_modules.o module_waitfor.o cwaitfor.o gettrk_main_gfs.f $(LIBS) -o gettrk_gfs + @echo " " + +cwaitfor.o: cwaitfor.c + @echo " " + @echo " Compiling the waitfor C routine...." + $(CCOMP) $(CFLAGS) -c cwaitfor.c -o cwaitfor.o + +module_waitfor.o: module_waitfor.f + @echo " " + @echo " Compiling the waitfor fortran module...." + $(FCOMP) $(FFLAGS) -c module_waitfor.f -o module_waitfor.o + +gettrk_modules.o: gettrk_modules.f + @echo " " + @echo " Compiling the regular tracker fortran modules....." + $(FCOMP) $(FFLAGS) -c gettrk_modules.f -o gettrk_modules.o + @echo " " + +CMD = gettrk_gfs + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_orion index 3b1954d02b..e0a653476c 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_orion @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -CCOMP= icc +FCOMP= mpiifort -nofree +CCOMP= mpiicc #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) CFLAGS= -O3 diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/module_waitfor.f b/sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/module_waitfor.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/gettrk_gfs.fd/module_waitfor.f rename to sorc/ens_tracker.v1.1.15.4/sorc/gettrk_gfs.fd/module_waitfor.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/leadtime.f b/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/leadtime.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/leadtime.f rename to sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/leadtime.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile similarity index 94% rename from sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile index b12611766a..3a3c4a6615 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian leadtime: leadtime.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_hera new file mode 100644 index 0000000000..f1cdc0dc82 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +leadtime: leadtime.f + @echo " " + @echo " Compiling the TC track leading hrs generation....." + $(COMP) $(FFLAGS) leadtime.f -o leadtime + @echo " " + +CMD = leadtime + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_jet similarity index 76% rename from sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_jet index 7813159497..6a4bdf5319 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 leadtime: leadtime.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_orion similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_orion index 972c00b92e..9d8132c1de 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/test.sh b/sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/test.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/leadtime.fd/test.sh rename to sorc/ens_tracker.v1.1.15.4/sorc/leadtime.fd/test.sh diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile similarity index 94% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile index 694b1c7582..871d6b2464 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian tcvital_ch_cmc: tcvital_ch_cmc.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_hera new file mode 100644 index 0000000000..ac9b42e4c7 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +tcvital_ch_cmc: tcvital_ch_cmc.f + @echo " " + @echo " Compiling the TC vital_track program....." + $(COMP) $(FFLAGS) tcvital_ch_cmc.f -o tcvital_ch_cmc + @echo " " + +CMD = tcvital_ch_cmc + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_jet similarity index 77% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_jet index 4390374e0f..b67a0331f0 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 tcvital_ch_cmc: tcvital_ch_cmc.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_orion similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_orion index eeab8bf630..a9c45ea402 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/tcvital_ch_cmc.f b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/tcvital_ch_cmc.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_cmc.fd/tcvital_ch_cmc.f rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_cmc.fd/tcvital_ch_cmc.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile index b9f1799a1d..95ada24445 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian tcvital_ch_ecmwf: tcvital_ch_ecmwf.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_hera new file mode 100644 index 0000000000..dc03dace62 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +tcvital_ch_ecmwf: tcvital_ch_ecmwf.f + @echo " " + @echo " Compiling the TC_vital track program....." + $(COMP) $(FFLAGS) tcvital_ch_ecmwf.f -o tcvital_ch_ecmwf + @echo " " + +CMD = tcvital_ch_ecmwf + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_jet similarity index 77% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_jet index 22db6f8e69..8ea5794932 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 tcvital_ch_ecmwf: tcvital_ch_ecmwf.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_orion index bdb1229805..37e3850eb7 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/tcvital_ch_ecmwf.f b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/tcvital_ch_ecmwf.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ecmwf.fd/tcvital_ch_ecmwf.f rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ecmwf.fd/tcvital_ch_ecmwf.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile similarity index 94% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile index d99a817d50..ebf6042073 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian tcvital_ch_gfs: tcvital_ch_gfs.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_hera new file mode 100644 index 0000000000..cd4202ae68 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +tcvital_ch_gfs: tcvital_ch_gfs.f + @echo " " + @echo " Compiling the TC_vital track program....." + $(COMP) $(FFLAGS) tcvital_ch_gfs.f -o tcvital_ch_gfs + @echo " " + +CMD = tcvital_ch_gfs + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_jet similarity index 77% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_jet index 181e78fc73..e8347eb53e 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 tcvital_ch_gfs: tcvital_ch_gfs.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_orion similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_orion index 74cf1078ea..34cc8e0766 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/tcvital_ch_gfs.f b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/tcvital_ch_gfs.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_gfs.fd/tcvital_ch_gfs.f rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_gfs.fd/tcvital_ch_gfs.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile index bae6ff51dc..afb3e974da 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian tcvital_ch_navgem: tcvital_ch_navgem.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_hera new file mode 100644 index 0000000000..496db23075 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +tcvital_ch_navgem: tcvital_ch_navgem.f + @echo " " + @echo " Compiling the TC_vital track program....." + $(COMP) $(FFLAGS) tcvital_ch_navgem.f -o tcvital_ch_navgem + @echo " " + +CMD = tcvital_ch_navgem + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_jet similarity index 78% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_jet index 75130d23f1..78aba9d8b7 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 tcvital_ch_navgem: tcvital_ch_navgem.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_orion index 9855e91546..43f1ad8ffd 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/tcvital_ch_navgem.f b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/tcvital_ch_navgem.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_navgem.fd/tcvital_ch_navgem.f rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_navgem.fd/tcvital_ch_navgem.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile index 64051b5adb..84fc7025bf 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian tcvital_ch_ukmet: tcvital_ch_ukmet.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_hera new file mode 100644 index 0000000000..0f767275aa --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +tcvital_ch_ukmet: tcvital_ch_ukmet.f + @echo " " + @echo " Compiling the TC_vital track program....." + $(COMP) $(FFLAGS) tcvital_ch_ukmet.f -o tcvital_ch_ukmet + @echo " " + +CMD = tcvital_ch_ukmet + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_jet similarity index 77% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_jet index d13b42e23e..b9c704c63f 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 tcvital_ch_ukmet: tcvital_ch_ukmet.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_orion index eee7b1f65a..1a3eac2e1f 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/tcvital_ch_ukmet.f b/sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/tcvital_ch_ukmet.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ncep_tcv_ukmet.fd/tcvital_ch_ukmet.f rename to sorc/ens_tracker.v1.1.15.4/sorc/ncep_tcv_ukmet.fd/tcvital_ch_ukmet.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile similarity index 94% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile index 4e62e6902a..2102648cd9 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian readprob: readprob.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_hera new file mode 100644 index 0000000000..e20f7de0ff --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +readprob: readprob.f + @echo " " + @echo " Compiling the TC genesis probability program....." + $(COMP) $(FFLAGS) readprob.f -o readprob + @echo " " + +CMD = readprob + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_jet similarity index 75% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_jet index c5c05fc231..fad6a4b89e 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 readprob: readprob.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_orion similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_orion index c149d9522a..54164e14f1 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/readprob.f b/sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/readprob.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprob.fd/readprob.f rename to sorc/ens_tracker.v1.1.15.4/sorc/readprob.fd/readprob.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile similarity index 94% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile index 7552d7f673..8785d72ca5 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian readprobLL: readprobLL.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_hera new file mode 100644 index 0000000000..13951e237f --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +readprobLL: readprobLL.f + @echo " " + @echo " Compiling the TC genesis probability program....." + $(COMP) $(FFLAGS) readprobLL.f -o readprobLL + @echo " " + +CMD = readprobLL + +clean: + -rm -f *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_jet similarity index 76% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_jet index ddc94ceb1e..ae13917d21 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 readprobLL: readprobLL.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_orion similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_orion index 631f39741d..79dc8f3840 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/readprobLL.f b/sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/readprobLL.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/readprobLL.fd/readprobLL.f rename to sorc/ens_tracker.v1.1.15.4/sorc/readprobLL.fd/readprobLL.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile index 0cf3ce8ce1..5f69489de5 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian track_convert: track_convert.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_hera b/sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_hera new file mode 100644 index 0000000000..e283e96b6e --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_hera @@ -0,0 +1,17 @@ +SHELL= /bin/sh +COMP= mpiifort +FFLAGS= -O2 -check bounds -check format -xHost -fpe0 + +track_convert: track_convert.f + @echo " " + @echo " Compiling the TC track atcfunix to gen_format program....." + $(COMP) $(FFLAGS) track_convert.f -o track_convert + @echo " " + +CMD = track_convert + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_jet similarity index 78% rename from sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_jet index 3d173b7c49..9c9562a410 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_jet @@ -1,6 +1,6 @@ SHELL= /bin/sh -COMP= ifort -FFLAGS= -O2 -check bounds -check format -fpe0 -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -axSSE4.2,AVX,CORE-AVX2 track_convert: track_convert.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_orion index 316378c75a..6c26fd161b 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_orion @@ -1,5 +1,5 @@ SHELL= /bin/sh -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/track_convert.f b/sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/track_convert.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/readtcv.fd/track_convert.f rename to sorc/ens_tracker.v1.1.15.4/sorc/readtcv.fd/track_convert.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile similarity index 93% rename from sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile index 18b215f58c..314e05ec1c 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) rhum_g2: rhum_g2.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_hera similarity index 72% rename from sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_hera index 2af66ed684..67f7d8ac06 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_hera @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +FCOMP= mpiifort -nofree +FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) rhum_g2: rhum_g2.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_jet new file mode 100644 index 0000000000..6cb774b454 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_jet @@ -0,0 +1,19 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +FCOMP= mpiifort -nofree +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 + +rhum_g2: rhum_g2.f + @echo " " + @echo " Compiling the RH subroutines....." + $(FCOMP) $(FFLAGS) rhum_g2.f $(LIBS) -o rhum_g2.x + @echo " " + +CMD = rhum_g2.x + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_orion similarity index 94% rename from sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_orion index 6ba3cb2b58..a858a70d56 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_orion @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/rhum_g2.f b/sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/rhum_g2.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/rhum_g2.fd/rhum_g2.f rename to sorc/ens_tracker.v1.1.15.4/sorc/rhum_g2.fd/rhum_g2.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile similarity index 97% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile index 3fe1a0d2cb..ae4815fcdf 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian -i$(ISIZE) -r$(RSIZE) supvit_g1: supvit_main_g1.f supvit_modules.o diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_hera similarity index 93% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_hera index 75d076df71..e808deaf96 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_hera @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -COMP= ifort -FFLAGS= -O2 -g -check bounds -check format -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O2 -g -check bounds -check format -i$(ISIZE) -r$(RSIZE) supvit_g1: supvit_main_g1.f supvit_modules.o @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_jet new file mode 100644 index 0000000000..27343a37aa --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_jet @@ -0,0 +1,26 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 + +supvit_g1: supvit_main_g1.f supvit_modules.o + @echo " " + @echo " Compiling program that sorts and updates vitals records...." + $(COMP) $(FFLAGS) supvit_modules.o supvit_main_g1.f $(LIBS_SUP) -o supvit_g1 + @echo " " + +supvit_modules.o: supvit_modules.f + @echo " " + @echo " Compiling the modules....." + $(COMP) -c supvit_modules.f -o supvit_modules.o + @echo " " + +CMD = supvit_g1 + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) + diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_orion similarity index 97% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_orion index 686f82388e..f8ff8ae2f4 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_orion @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -i$(ISIZE) -r$(RSIZE) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/supvit_main_g1.f b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/supvit_main_g1.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/supvit_main_g1.f rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/supvit_main_g1.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/supvit_modules.f b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/supvit_modules.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g1.fd/supvit_modules.f rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g1.fd/supvit_modules.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile similarity index 97% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile index 38daee3368..f3052faf24 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian -i$(ISIZE) -r$(RSIZE) supvit_g2: supvit_main_g2.f supvit_modules.o diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_hera similarity index 93% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_hera index 16f5a622c7..c756895fc4 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_hera @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -COMP= ifort -FFLAGS= -O2 -g -check bounds -check format -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -O2 -g -check bounds -check format -i$(ISIZE) -r$(RSIZE) supvit_g2: supvit_main_g2.f supvit_modules.o @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_jet new file mode 100644 index 0000000000..e93169238b --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_jet @@ -0,0 +1,26 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 + +supvit_g2: supvit_main_g2.f supvit_modules.o + @echo " " + @echo " Compiling program that sorts and updates vitals records...." + $(COMP) $(FFLAGS) supvit_modules.o supvit_main_g2.f $(LIBS_SUP) -o supvit_g2 + @echo " " + +supvit_modules.o: supvit_modules.f + @echo " " + @echo " Compiling the modules....." + $(COMP) -c supvit_modules.f -o supvit_modules.o + @echo " " + +CMD = supvit_g2 + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) + diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_orion similarity index 97% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_orion index 9e928f0181..0e5f75691e 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_orion @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -i$(ISIZE) -r$(RSIZE) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/supvit_main_g2.f b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/supvit_main_g2.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/supvit_main_g2.f rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/supvit_main_g2.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/supvit_modules.f b/sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/supvit_modules.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/supvit_g2.fd/supvit_modules.f rename to sorc/ens_tracker.v1.1.15.4/sorc/supvit_g2.fd/supvit_modules.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile similarity index 93% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile index 7c6e9f0aa6..2bc5128ce7 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) tave_g1: tave_g1.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_hera similarity index 74% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_hera index b9e7e2330d..945277144f 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_hera @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -FFLAGS= -O2 -g -traceback -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +FCOMP= mpiifort -nofree +FFLAGS= -O2 -g -traceback -i$(ISIZE) -r$(RSIZE) tave_g1: tave_g1.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_jet new file mode 100644 index 0000000000..32b7c8f73f --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_jet @@ -0,0 +1,19 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +FCOMP= mpiifort -nofree +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 + +tave_g1: tave_g1.f + @echo " " + @echo " Compiling the mean field program and subroutines....." + $(FCOMP) $(FFLAGS) tave_g1.f $(LIBS) -o tave_g1.x + @echo " " + +CMD = tave_g1.x + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_orion similarity index 94% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_orion index 239b3be057..6d104c5a4b 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_orion @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/tave_g1.f b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/tave_g1.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g1.fd/tave_g1.f rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g1.fd/tave_g1.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile similarity index 93% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile index 3916189756..d524e809b1 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) tave_g2: tave_g2.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_hera similarity index 74% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_hera index 71bb4e3b26..cfe55f290b 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_hera @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +FCOMP= mpiifort -nofree +FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) tave_g2: tave_g2.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_jet new file mode 100644 index 0000000000..b8ae98812c --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_jet @@ -0,0 +1,19 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +FCOMP= mpiifort -nofree +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 + +tave_g2: tave_g2.f + @echo " " + @echo " Compiling the mean field program and subroutines....." + $(FCOMP) $(FFLAGS) tave_g2.f $(LIBS) -o tave_g2.x + @echo " " + +CMD = tave_g2.x + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_orion similarity index 94% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_orion index a59d7d885b..b78a2a8993 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_orion @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/tave_g2.f b/sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/tave_g2.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/tave_g2.fd/tave_g2.f rename to sorc/ens_tracker.v1.1.15.4/sorc/tave_g2.fd/tave_g2.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile index a007301e5d..df4749b82b 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile @@ -1,7 +1,7 @@ SHELL= /bin/bash ISIZE = 4 RSIZE = 8 -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian -free -I $(INC) -i$(ISIZE) -r$(RSIZE) ukm_hires_merge: ukmhires.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_hera similarity index 78% rename from sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_hera index d1f9803470..ed68dc505b 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_hera @@ -1,7 +1,7 @@ SHELL= /bin/bash -COMP= ifort +COMP= mpiifort #FFLAGS= -r8 -i4 -list -traceback -FFLAGS=-O2 -convert big_endian -traceback -mkl -free -axSSE4.2,AVX,CORE-AVX2 +FFLAGS=-O2 -convert big_endian -traceback -mkl -free ukm_hires_merge: ukmhires.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_jet new file mode 100644 index 0000000000..525127f936 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_jet @@ -0,0 +1,20 @@ +SHELL= /bin/bash +ISIZE = 4 +RSIZE = 8 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -free -I $(INC) -i$(ISIZE) -r$(RSIZE)-axSSE4.2,AVX,CORE-AVX2 + +ukm_hires_merge: ukmhires.f + @echo " " + @echo " Compiling program UKMET 8-piece-GRIB to NCEP GRIB1...." + $(COMP) $(FFLAGS) ukmhires.f $(LIBS) -o ukm_hires_merge + @echo " " + +CMD = ukm_hires_merge + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) + diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_orion index b07aa0dcae..a49b591620 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_orion @@ -1,7 +1,7 @@ SHELL= /bin/bash ISIZE = 4 RSIZE = 8 -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian -free -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/ukmhires.f b/sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/ukmhires.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ukmet.fd/ukmhires.f rename to sorc/ens_tracker.v1.1.15.4/sorc/ukmet.fd/ukmhires.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile similarity index 93% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile index edd713a84c..7908d40b3b 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) ushear_g1: ushear_g1.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_hera similarity index 75% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_hera index 8b6c174e48..35b83cc508 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_hera @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -FFLAGS= -O2 -g -traceback -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +FCOMP= mpiifort -nofree +FFLAGS= -O2 -g -traceback -i$(ISIZE) -r$(RSIZE) ushear_g1: ushear_g1.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_jet new file mode 100644 index 0000000000..1ad2320457 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_jet @@ -0,0 +1,19 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +FCOMP= mpiifort -nofree +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 + +ushear_g1: ushear_g1.f + @echo " " + @echo " Compiling the mean field program and subroutines....." + $(FCOMP) $(FFLAGS) ushear_g1.f $(LIBS) -o ushear_g1.x + @echo " " + +CMD = ushear_g1.x + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_orion similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_orion index 07cd363a0f..c822567942 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_orion @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/ushear_g1.f b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/ushear_g1.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g1.fd/ushear_g1.f rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g1.fd/ushear_g1.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile similarity index 93% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile index 22c626fe78..3b401be9e6 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) ushear_g2: ushear_g2.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_hera similarity index 74% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_hera index 322a66b114..0edd1dbe23 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_hera @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +FCOMP= mpiifort -nofree +FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) ushear_g2: ushear_g2.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_jet new file mode 100644 index 0000000000..418e06578d --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_jet @@ -0,0 +1,19 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +FCOMP= mpiifort -nofree +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 + +ushear_g2: ushear_g2.f + @echo " " + @echo " Compiling the mean field program and subroutines....." + $(FCOMP) $(FFLAGS) ushear_g2.f $(LIBS) -o ushear_g2.x + @echo " " + +CMD = ushear_g2.x + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_orion similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_orion index 174fad0756..fe1e70e674 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_orion @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/ushear_g2.f b/sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/ushear_g2.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/ushear_g2.fd/ushear_g2.f rename to sorc/ens_tracker.v1.1.15.4/sorc/ushear_g2.fd/ushear_g2.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile similarity index 93% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile index 818354c0da..622d3984da 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) vint_g1: vint_g1.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_hera similarity index 74% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_hera index d997d18409..0e21f2798d 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_hera @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -FFLAGS= -O2 -g -traceback -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +FCOMP= mpiifort -nofree +FFLAGS= -O2 -g -traceback -i$(ISIZE) -r$(RSIZE) vint_g1: vint_g1.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_jet new file mode 100644 index 0000000000..7a772503f6 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_jet @@ -0,0 +1,19 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +FCOMP= mpiifort -nofree +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 + +vint_g1: vint_g1.f + @echo " " + @echo " Compiling the interpolation program....." + $(FCOMP) $(FFLAGS) vint_g1.f $(LIBS) -o vint_g1.x + @echo " " + +CMD = vint_g1.x + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_orion similarity index 94% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_orion index aba309d7d2..c4e28668df 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_orion @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/vint_g1.f b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/vint_g1.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g1.fd/vint_g1.f rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g1.fd/vint_g1.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile similarity index 93% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile index d0128fec4c..723ad75815 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) vint_g2: vint_g2.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_hera similarity index 74% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_hera index d3ad33eadd..4a57884c7f 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_hera @@ -1,8 +1,8 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree -FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 +FCOMP= mpiifort -nofree +FFLAGS= -O2 -fpe0 -I $(INC) -i$(ISIZE) -r$(RSIZE) vint_g2: vint_g2.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_jet new file mode 100644 index 0000000000..fe94164855 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_jet @@ -0,0 +1,19 @@ +SHELL= /bin/sh +ISIZE = 4 +RSIZE = 8 +FCOMP= mpiifort -nofree +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 + +vint_g2: vint_g2.f + @echo " " + @echo " Compiling the mean field program and subroutines....." + $(FCOMP) $(FFLAGS) vint_g2.f $(LIBS) -o vint_g2.x + @echo " " + +CMD = vint_g2.x + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_orion similarity index 94% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_orion index b80e0d7802..13b3063d4f 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_orion @@ -1,7 +1,7 @@ SHELL= /bin/sh ISIZE = 4 RSIZE = 8 -FCOMP= ifort -nofree +FCOMP= mpiifort -nofree #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/vint_g2.f b/sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/vint_g2.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/vint_g2.fd/vint_g2.f rename to sorc/ens_tracker.v1.1.15.4/sorc/vint_g2.fd/vint_g2.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/ecmwfensh.f b/sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/ecmwfensh.f similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/ecmwfensh.f rename to sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/ecmwfensh.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile b/sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile similarity index 95% rename from sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile rename to sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile index 2e551c011e..eb4d32bc7f 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile +++ b/sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile @@ -1,7 +1,7 @@ SHELL= /bin/bash ISIZE = 4 RSIZE = 8 -COMP= ifort +COMP= mpiifort FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) wsr_ecmwfensh: ecmwfensh.f diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile_cray b/sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile_cray rename to sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_cray diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile_dell b/sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile_dell rename to sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_dell diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_hera similarity index 79% rename from sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile_jet rename to sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_hera index 1799c86d6a..7e9bc6d7f1 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile_jet +++ b/sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_hera @@ -1,6 +1,6 @@ SHELL= /bin/bash -COMP= ifort -FFLAGS= -r8 -i4 -list -traceback -axSSE4.2,AVX,CORE-AVX2 +COMP= mpiifort +FFLAGS= -r8 -i4 -list -traceback wsr_ecmwfensh: ecmwfensh.f @echo " " diff --git a/sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_jet b/sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_jet new file mode 100644 index 0000000000..43621f233c --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_jet @@ -0,0 +1,20 @@ +SHELL= /bin/bash +ISIZE = 4 +RSIZE = 8 +COMP= mpiifort +FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) -axSSE4.2,AVX,CORE-AVX2 + +wsr_ecmwfensh: ecmwfensh.f + @echo " " + @echo " Compiling program ECMWF GRIB1 to NCEP GRIB1...." + $(COMP) $(FFLAGS) ecmwfensh.f $(LIBS) -o wsr_ecmwfensh + @echo " " + +CMD = wsr_ecmwfensh + +clean: + -rm -f *.o *.mod + +install: + mv $(CMD) ../../exec/$(CMD) + diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile_orion b/sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_orion similarity index 96% rename from sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile_orion rename to sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_orion index 69160f9045..9d80594444 100644 --- a/sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile_orion +++ b/sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_orion @@ -1,7 +1,7 @@ SHELL= /bin/bash ISIZE = 4 RSIZE = 8 -COMP= ifort +COMP= mpiifort #FFLAGS= -O3 -g -traceback -convert big_endian -I $(INC) -i$(ISIZE) -r$(RSIZE) FFLAGS= -g -O3 -ftz -traceback -fpe0 -xHOST -axcore-avx512 -qno-opt-dynamic-align -I $(INC) -i$(ISIZE) -r$(RSIZE) diff --git a/sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile_wcoss b/sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_wcoss similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/sorc/wsr_ecmwfens.fd/makefile_wcoss rename to sorc/ens_tracker.v1.1.15.4/sorc/wsr_ecmwfens.fd/makefile_wcoss diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/Makefile b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/Makefile similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/Makefile rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/Makefile diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/Makefile_4_cray b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/Makefile_4_cray similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/Makefile_4_cray rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/Makefile_4_cray diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/Makefile_4_dell b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/Makefile_4_dell similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/Makefile_4_dell rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/Makefile_4_dell diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/PaxHeader/README.md b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/PaxHeader/README.md similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/PaxHeader/README.md rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/PaxHeader/README.md diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/README.md b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/README.md similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/README.md rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/README.md diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/README_4_tclogg b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/README_4_tclogg similarity index 51% rename from sorc/ens_tracker.v1.1.15.2/tclogg/README_4_tclogg rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/README_4_tclogg index cb41517509..da0b180daa 100644 --- a/sorc/ens_tracker.v1.1.15.2/tclogg/README_4_tclogg +++ b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/README_4_tclogg @@ -1,18 +1,16 @@ -#------ 06/05/2020 --------------------------------------- +#------ 07/23/2020 --------------------------------------- The FSU genesis package could be run on CRAY/DELL, it needs pygrib. -Venus: /gpfs/dell2/emc/modeling/noscrub/Jiayi.Peng/ens_tracker.v1.1.15.4 -Surge: /gpfs/hps3/emc/ensemble/save/Jiayi.Peng/ens_tracker.v1.1.15.4 -(1)cd ./tclogg .... for compile -module unload anaconda2/latest .... for dell - +(1)cd ./ush/FSUgenesisPY .... for compile make -f Makefile_4_cray ....to create a module file -.OR. make -f Makefile_4_dell ....to create a module file chmod +x ./bin/tclogg_track +module unload anaconda2/latest .... for dell +make -f Makefile_4_dell ....to create a module file + (2)./control/dell/jfsu_tc_genesis_00.ecf_dell ./control/cray/jfsu_tc_genesis_00.ecf_cray -Inside: module use ${NWROOT}/ens_tracker.${ens_tracker_ver}/tclogg/modulefiles +Inside: module use ${NWROOT}/ens_tracker.${ens_tracker_ver}/ush/FSUgenesisPY/modulefiles module load tclogg bsub < jfsu_tc_genesis_00.ecf_dell .OR. bsub < jfsu_tc_genesis_00.ecf_cray diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/__init__.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/__init__.py similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/__init__.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/__init__.py diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/bin/tclogg_track b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/bin/tclogg_track similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/bin/tclogg_track rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/bin/tclogg_track diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/PaxHeader/model_config.cfg b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/PaxHeader/model_config.cfg similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/PaxHeader/model_config.cfg rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/PaxHeader/model_config.cfg diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/__init__.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/__init__.py similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/__init__.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/__init__.py diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/coefficients.cfg b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/coefficients.cfg similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/coefficients.cfg rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/coefficients.cfg diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/forecast_models.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/forecast_models.py similarity index 99% rename from sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/forecast_models.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/forecast_models.py index 3cad9dc475..5504ea9b52 100644 --- a/sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/forecast_models.py +++ b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/forecast_models.py @@ -6,8 +6,8 @@ import sys import numpy as np -import metpy.calc as mpcalc -import pygrib +import vorpy.calc as mpcalc +import grib2py if sys.version_info[0] == 2: from ConfigParser import RawConfigParser @@ -109,7 +109,7 @@ def filepath(self, fhr ) : return pathlib.Path(self.filename(fhr=fhr)) def read_grib(self, fhour, varname, grib_kwargs, flip_lat=False, scale=None): - with pygrib.open(str(self.filepath(fhr=fhour))) as gbfile: + with grib2py.open(str(self.filepath(fhr=fhour))) as gbfile: try: tmpdata = gbfile.select(**grib_kwargs)[0] except ValueError as exc: diff --git a/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/forecast_models.py_bak b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/forecast_models.py_bak new file mode 100644 index 0000000000..16541eea50 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/forecast_models.py_bak @@ -0,0 +1,869 @@ +import logging +import pathlib +from datetime import timedelta +from enum import Enum +import os +import sys + +import numpy as np +import vorpy.calc as mpcalc +import pygrib + +if sys.version_info[0] == 2: + from ConfigParser import RawConfigParser + FileNotFoundError = RuntimeError +else: + from configparser import RawConfigParser + + +def subset_grid(data_grid , y , x , extent ) : + ymin = y - extent['ym'] + ymax = y + extent['yp'] + xmin = x - extent['xm'] + xmax = x + extent['xp'] + return data_grid[ymin:ymax, xmin:xmax] + + +class Models(Enum): + gfs = 1 + cmc = 2 + ukm = 3 + ecm = 4 + nav = 5 + + +class ModelDefinition: + def __init__( + self, + model_name, + basin, + rundate=None, + fname_template=None + ): + self.model_name = model_name + self.basin = basin + self.rundate = rundate +# self.fname_template = None + self.fname_template = fname_template + self.model_num = Models[model_name].value + self._data = {} + self._data_fhr = None + self._latdiff = None + self._londiff = None + self.load_coefficients() + self.load_config() + + def load_config(self): + config = RawConfigParser({'min_fhr': 0, 'max_fhr': 180, 'delta_fhr': 6}) + config.read( + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'model_config.cfg')) +# self.fname_template = config.get(self.model_name, 'fname_template') + self.min_fhr = config.getint(self.model_name, 'min_fhr') + self.max_fhr = config.getint(self.model_name, 'max_fhr') + 1 + self.delta_fhr = config.getint(self.model_name, 'delta_fhr') + + @property + def deg2box(self): + return { + 'ym': int(round(2 / self._latdiff, 0)), + 'yp': int(round(2 / self._latdiff, 0)) + 1, + 'xm': int(round(2 / self._londiff, 0)), + 'xp': int(round(2 / self._londiff, 0)) + 1, + } + + @property + def deg5box(self): + return { + 'ym': int(round(5 / self._latdiff, 0)), + 'yp': int(round(5 / self._latdiff, 0)) + 1, + 'xm': int(round(5 / self._londiff, 0)), + 'xp': int(round(5 / self._londiff, 0)) + 1, + } + + def load_coefficients(self, ): + config = RawConfigParser() + config.read( + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'coefficients.cfg')) + self.coefficients = config + + @property + def vthresh(self) : + return self.coefficients.getfloat('vthresh.{}'.format(self.basin), self.model_name) + + @property + def ththresh(self) : + return self.coefficients.getfloat('ththresh.{}'.format(self.basin), self.model_name) + + @property + def wsthresh(self) : + return self.coefficients.getfloat('wsthresh.{}'.format(self.basin), self.model_name) + + @property + def f_hours(self, ): + return range(self.min_fhr, self.max_fhr, self.delta_fhr) + + def filename(self, fhr ) : + return self.fname_template.format(date=self.rundate, fhr=fhr) + + def filepath(self, fhr ) : + return pathlib.Path(self.filename(fhr=fhr)) + + def read_grib(self, fhour, varname, grib_kwargs, flip_lat=False, scale=None): + with pygrib.open(str(self.filepath(fhr=fhour))) as gbfile: + try: + tmpdata = gbfile.select(**grib_kwargs)[0] + except ValueError as exc: + logging.error("{grib_kwargs} not found in" + "{filepath}".format( + grib_kwargs=grib_kwargs, + filepath=self.filepath(fhour), + )) + raise exc + + if scale is not None: + tmpdata.values *= scale + logging.debug('{fhr:03} - {varname} : max:{max}, min:{min}'.format( + fhr=fhour, + varname=varname, + max=np.max(tmpdata.values), + min=np.min(tmpdata.values))) + if flip_lat: + tmpvalues = np.flipud(tmpdata.values) + self._data[varname] = tmpvalues.copy() + else: + tmpvalues = tmpdata.values + self._data[varname] = tmpvalues.copy() + + if 'lat1d' not in self._data: + lats, lons = tmpdata.latlons() + if flip_lat: + self._data['lat1d'] = lats[::-1, 0] + else: + self._data['lat1d'] = lats[:, 0] + self._data['lon1d'] = lons[0, :] + self._latdiff = self._data['lat1d'][1] - self._data['lat1d'][0] + self._londiff = self._data['lon1d'][1] - self._data['lon1d'][0] + logging.debug('Lat Extent: {latmin}:{latmax}:{latdiff}'.format( + latmin=self._data['lat1d'][0], + latmax=self._data['lat1d'][-1], + latdiff=self._latdiff)) + logging.debug('Lon Extent: {lonmin}:{lonmax}:{londiff}'.format( + lonmin=self._data['lon1d'][0], + lonmax=self._data['lon1d'][-1], + londiff=self._londiff)) + + return self._data[varname] + + def check_for_input_files(self): + for fh in self.f_hours: + filepath = self.filepath(fhr=fh) + if not filepath.is_file(): + logging.warning("File Not Found: {filepath}".format(filepath=filepath)) + raise FileNotFoundError('All files are not ready yet') + logging.info('All files are available') + + def get_mslp_min(self, mslp_grid, y, x): + ''' Check local min (probably not needed anymore) + Check if local min has "closed" contours at 5 degrees + ''' + sarea = mslp_grid[y - 1:y + 2, x - 1:x + 2] + pmin = np.min(sarea) + pminx = np.where(sarea == np.min(sarea))[1][0] + pminy = np.where(sarea == np.min(sarea))[0][0] + if pminx == 1 and pminy == 1: + sarea = subset_grid(mslp_grid, y, x, self.deg5box) + pmint = np.min(sarea[0, :]) + pminb = np.min(sarea[-1, :]) + pminl = np.min(sarea[:, 0]) + pminr = np.min(sarea[:, -1]) + if (pmin + 200) <= np.min([pmint, pminb, pminl, pminr]): + return pmin + return None + + def get_interior_max(self, data_grid, y, x): + '''Checks for maximum not on boundary of grid ''' + sub_area = subset_grid(data_grid, y, x, self.deg2box) + local_max = np.max(sub_area) + interior_max = np.max(sub_area[1:-1, 1:-1]) + if local_max == interior_max: + return local_max + else: + return None + + def get_wspd_max(self, data_grid, y, x): + wsarea = subset_grid(data_grid, y, x, self.deg5box) + wsmax = np.max(wsarea) + return wsmax + + def check_minmax_criteria(self, fhour , y, x): + pmin = self.get_mslp_min(self.data(fhour, 'mslp'), y, x) + if pmin is None: + return False, None + vmax = self.get_interior_max(self.data(fhour, 'vor'), y, x) + if vmax is None: + return False, None + thmax = self.get_interior_max(self.data(fhour, 'thck'), y, x) + if thmax is None: + return False, None + wsmax = self.get_wspd_max(self.data(fhour, 'ws925'), y, x) + if wsmax < 10: + logging.debug("Fail on wspd: ({pmin}, {vmax}, {thmax}, {wsmax})".format( + pmin=pmin, + vmax=vmax, + thmax=thmax, + wsmax=wsmax, + )) + return False, None + return True, (pmin, vmax, thmax, wsmax) + + def calc_B_parameters(self, fh , y , x ): + if self.basin == 'epac': + return self.calc_epac_B_parameters(fh, y, x) + elif self.basin == 'natl': + return self.calc_natl_B_parameters(fh, y, x) + + def calc_natl_B_parameters(self, fh , y , x ) : + raise NotImplementedError + + def calc_epac_B_parameters(self, fh , y , x ) : + raise NotImplementedError + + def basin_bbox(self): + if self.basin == 'natl': + return { + 'latmin': 5., + "lonmin": 260., + "lonmax": 340., + "latmax": 45., + } + elif self.basin == 'epac': + return { + 'latmin': 5., + "lonmin": 180., + "lonmax": 275., + "latmax": 33., + } + + def data(self, fhour , varname ) : + raise NotImplementedError('Instantiate a specific ModelDefinition') + + +class GFSDefinition(ModelDefinition): + def data(self, fhour , varname ) : + if (varname in self._data) and (fhour == self._data_fhr): + return self._data[varname] + + if self._data_fhr is None: + self._data_fhr = fhour + + if fhour != self._data_fhr: + self._data = {} + self._data_fhr = fhour + + short_name_to_gribkwargs = { + 'mslp': { + 'shortName': 'mslet', + }, + 'u925': { + 'shortName': 'u', + 'level': 925 + }, + 'u800': { + 'shortName': 'u', + 'level': 800 + }, + 'u850': { + 'shortName': 'u', + 'level': 850 + }, + 'v850': { + 'shortName': 'v', + 'level': 850 + }, + 'u200': { + 'shortName': 'u', + 'level': 200 + }, + 'v925': { + 'shortName': 'v', + 'level': 925 + }, + 'v800': { + 'shortName': 'v', + 'level': 800 + }, + 'v200': { + 'shortName': 'v', + 'level': 200 + }, + 'gh850': { + 'shortName': 'gh', + 'level': 850 + }, + 'rh600': { + 'shortName': 'r', + 'level': 600 + }, + 'gh250': { + 'shortName': 'gh', + 'level': 250 + }, + 'lh': { + 'shortName': 'lhtfl' + }, + 'cape': { + 'shortName': 'cape' + } + } + + if varname == 'lh' and fhour == 0: + return None + + grib_kwargs = short_name_to_gribkwargs.get(varname, None) + if grib_kwargs is not None: + return self.read_grib(fhour, varname, grib_kwargs, flip_lat=True) + + if (varname == 'lon1d') or (varname == 'lat1d'): + if varname not in self._data: + _ = self.data(fhour, 'mslp') + return self._data[varname] + + if varname == 'ws925': + u925 = self.data(fhour, 'u925') + v925 = self.data(fhour, 'v925') + self._data[varname] = np.sqrt((u925 * u925) + (v925 * v925)) + logging.debug('{fhr:03} - {varname} : max:{max}, min:{min}'.format( + fhr=fhour, + varname=varname, + max=np.max(self._data[varname]), + min=np.min(self._data[varname]))) + return self._data[varname] + + if varname == 'vor': + u850 = self.data(fhour, 'u850') + v850 = self.data(fhour, 'v850') + dx, dy = mpcalc.lat_lon_grid_deltas(self.data(fhour, 'lon1d'), + self.data(fhour, 'lat1d')) + vor = mpcalc.vorticity(u850, v850, dx, dy, dim_order='yx').magnitude * 10**5 # noqa pylint: disable=E1123, E731 + self._data[varname] = vor + logging.debug('{fhr:03} - {varname} : max:{max}, min:{min}'.format( + fhr=fhour, + varname=varname, + max=np.max(self._data[varname][180]), + min=np.min(self._data[varname][180]))) + return self._data[varname] + + if varname == 'thck': + gh850 = self.data(fhour, 'gh850') + gh250 = self.data(fhour, 'gh250') + self._data[varname] = gh250 - gh850 + logging.debug('{fhr:03} - {varname} : max:{max}, min:{min}'.format( + fhr=fhour, + varname=varname, + max=np.max(self._data[varname]), + min=np.min(self._data[varname]))) + return self._data[varname] + + raise ValueError("{varname} not recognised".format(varname=varname)) + + def calc_natl_B_parameters(self, fh , y , x ) : + cape = self.data(fh, 'cape') + pmin = self.data(fh, 'pmin') + lat = self.data(fh, 'lat1d') + capeavg = np.mean(subset_grid(cape, y, x, self.deg5box)) + + B48 = 0.8900867 - (0.0256533 * fh) + B120 = -1.880649 + (0.0011505 * capeavg) + B168 = -98.17343449 - (0.15732243 * lat[y]) + (0.09715203 * pmin / 100) + return B48, B120, B168 + + def calc_epac_B_parameters(self, fh , y , x ) : + rh600avg = np.mean(subset_grid(self.data(fh, 'rh600'), y, x, self.deg5box)) + capeavg = np.mean(subset_grid(self.data(fh, 'cape'), y, x, self.deg5box)) + + B48 = 0.001593571 - (0.038912763 * fh) + (0.002013083 * capeavg) + B120 = -6.58554986 + (0.001894241 * capeavg) + (0.054472663 * rh600avg) + B168 = -4.558516018 + (0.001943481 * capeavg) + (0.011236631 * fh) + + return B48, B120, B168 + + +class CMCDefinition(ModelDefinition): + def data(self, fhour , varname ) : + if (varname in self._data) and (fhour == self._data_fhr): + return self._data[varname] + + if self._data_fhr is None: + self._data_fhr = fhour + + if fhour != self._data_fhr: + self._data = {} + self._data_fhr = fhour + + short_name_to_gribkwargs = { + 'mslp': { + 'shortName': 'msl', + }, + 'gh250': { + 'shortName': 'gh', + 'level': 250 + }, + 'gh850': { + 'shortName': 'gh', + 'level': 850 + }, + 'u925': { + 'shortName': 'u', + 'level': 925 + }, + 'u850': { + 'shortName': 'u', + 'level': 850 + }, + 'v925': { + 'shortName': 'v', + 'level': 925 + }, + 'v850': { + 'shortName': 'v', + 'level': 850 + }, + # end of common variables + 'rh700': { + 'shortName': 'r', + 'level': 700 + }, + 't1000': { + 'shortName': 't', + 'level': 1000 + }, + 't700': { + 'shortName': 't', + 'level': 700 + }, + 'gh1000': { + 'shortName': 'gh', + 'level': 1000 + }, + 'gh700': { + 'shortName': 'gh', + 'level': 700 + }, + } + + grib_kwargs = short_name_to_gribkwargs.get(varname, None) + if grib_kwargs is not None: + return self.read_grib(fhour, varname, grib_kwargs, flip_lat=False) + + if (varname == 'lon1d') or (varname == 'lat1d'): + if varname not in self._data: + _ = self.data(fhour, 'mslp') + return self._data[varname] + + if varname == 'ws925': + u925 = self.data(fhour, 'u925') + v925 = self.data(fhour, 'v925') + self._data[varname] = np.sqrt((u925 * u925) + (v925 * v925)) + return self._data[varname] + + if varname == 'vor': + u850 = self.data(fhour, 'u850') + v850 = self.data(fhour, 'v850') + dx, dy = mpcalc.lat_lon_grid_deltas(self.data(fhour, 'lon1d'), + self.data(fhour, 'lat1d')) + vor = mpcalc.vorticity(u850, v850, dx, dy, dim_order='yx').magnitude * 10**5 # noqa pylint: disable=E1123, E731 + self._data[varname] = vor + return self._data[varname] + + if varname == 'thck': + gh850 = self.data(fhour, 'gh850') + gh250 = self.data(fhour, 'gh250') + self._data[varname] = gh250 - gh850 + return self._data[varname] + + if varname == 'lapse': + z700 = self.data(fhour, 'gh700') + z1000 = self.data(fhour, 'gh1000') + t700 = self.data(fhour, 't700') + t1000 = self.data(fhour, 't1000') + lapse = ((t700 - t1000) / (z700 - z1000)) * -1000. + self._data[varname] = lapse + return self._data[varname] + + raise ValueError("{varname} not recognised".format(varname=varname)) + + def calc_natl_B_parameters(self, fh , y , x ) : + rh7 = self.data(fh, 'rh700') + thmax = self.data(fh, 'thmax') + lon1d = self.data(fh, 'lon1d') + rh7avg = np.mean(subset_grid(rh7, y, x, self.deg5box)) + + B48 = -138.61238982 - (0.02995572 * fh) + (0.01463614 * thmax) + B120 = -70.587026869 - (0.008904298 * fh) + (0.0007159913 * thmax) + (0.023088343 * + rh7avg) + B168 = 14.44851 - (0.00001181653 * lon1d[x]**3) + (0.000001956435 * + (lon1d[x]**3) * np.log(lon1d[x])) + return B48, B120, B168 + + def calc_epac_B_parameters(self, fh , y , x ) : + rh700 = self.data(fh, 'rh700') + lapse = self.data(fh, 'lapse') # ((t700 - t1000) / (z700 - z1000)) * -1000. + + ci = np.mean(subset_grid(lapse, y, x, self.deg5box)) + rh7avg = np.mean(subset_grid(rh700, y, x, self.deg5box)) + rh7max = np.max(subset_grid(rh700, y, x, self.deg5box)) + rh7pert = rh7max - rh7avg + + B48 = -9.39795675 - (0.02069718 * fh) + (1.64429686 * ci) + _fh = np.max([fh, 6]) # limit calculation to fh >= 6 + B120 = -21.066118 + (3.2084092 * np.sqrt(_fh)) - ( + 0.553976 * np.sqrt(_fh) * np.log(_fh)) + (0.0682341 * rh7avg) + (1.4274026 * ci) + B168 = -1.0463184 - (0.0653109 * rh7pert) + + return B48, B120, B168 + + +class ECMDefinition(ModelDefinition): + def filename(self, fhr ) : + valid_time = self.rundate + timedelta(hours=fhr) + if fhr == 0: + valid_time = self.rundate + timedelta(minutes=1) + return self.fname_template.format(date=self.rundate, fhr=fhr, valid_time=valid_time) + + def data(self, fhour , varname ) : + if (varname in self._data) and (fhour == self._data_fhr): + return self._data[varname] + + if self._data_fhr is None: + self._data_fhr = fhour + + if fhour != self._data_fhr: + self._data = {} + self._data_fhr = fhour + + short_name_to_gribkwargs = { + 'mslp': { + 'shortName': 'msl', + }, + 'gh250': { + 'shortName': 'gh', + 'level': 250 + }, + 'gh850': { + 'shortName': 'gh', + 'level': 850 + }, + 'u925': { + 'shortName': 'u', + 'level': 925 + }, + 'u850': { + 'shortName': 'u', + 'level': 850 + }, + 'v925': { + 'shortName': 'v', + 'level': 925 + }, + 'v850': { + 'shortName': 'v', + 'level': 850 + }, + # end of common variables + 'rh700': { + 'shortName': 'r', + 'level': 700 + }, + } + + grib_kwargs = short_name_to_gribkwargs.get(varname, None) + if grib_kwargs is not None: + return self.read_grib(fhour, varname, grib_kwargs, flip_lat=True) + + if (varname == 'lon1d') or (varname == 'lat1d'): + if varname not in self._data: + _ = self.data(fhour, 'mslp') + return self._data[varname] + + if varname == 'ws925': + u925 = self.data(fhour, 'u925') + v925 = self.data(fhour, 'v925') + self._data[varname] = np.sqrt((u925 * u925) + (v925 * v925)) + return self._data[varname] + + if varname == 'vor': + u850 = self.data(fhour, 'u850') + v850 = self.data(fhour, 'v850') + dx, dy = mpcalc.lat_lon_grid_deltas(self.data(fhour, 'lon1d'), + self.data(fhour, 'lat1d')) + vor = mpcalc.vorticity(u850, v850, dx, dy, dim_order='yx').magnitude * 10**5 # noqa pylint: disable=E1123, E731 + self._data[varname] = vor + return self._data[varname] + + if varname == 'thck': + gh850 = self.data(fhour, 'gh850') + gh250 = self.data(fhour, 'gh250') + self._data[varname] = gh250 - gh850 + return self._data[varname] + + raise ValueError("{varname} not recognised".format(varname=varname)) + + def calc_natl_B_parameters(self, fh , y , x ) : + lat1d = self.data(fh, 'lat1d') + _fh = np.max([fh, 6]) # limit calculation to fh >= 6 + + B48 = 1.75210835 - (0.03068216 * fh) + B120 = -4.04742679 + (0.23514675 * _fh) - (0.04361315 * _fh * np.log(_fh)) + B168 = -2.47137493 + (0.01664906 * fh) - (0.08487111 * lat1d[y]) + + return B48, B120, B168 + + def calc_epac_B_parameters(self, fh , y , x ) : + lat1d = self.data(fh, 'lat1d') + rh700 = self.data(fh, 'rh700') + rh7avg = np.mean(subset_grid(rh700, y, x, self.deg5box)) + + B48 = 1.60378303 - (0.02585508 * fh) + B120 = -3.42303304 + (0.03809354 * rh7avg) + B168 = -2.68929248 + (0.02009218 * fh) - (0.09296465 * lat1d[y]) + + return B48, B120, B168 + + +class UKMDefinition(ModelDefinition): + @property + def f_hours(self, ): + return [0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 72, 84, 96] + + def data(self, fhour , varname ) : + if (varname in self._data) and (fhour == self._data_fhr): + return self._data[varname] + + if self._data_fhr is None: + self._data_fhr = fhour + + if fhour != self._data_fhr: + self._data = {} + self._data_fhr = fhour + + short_name_to_gribkwargs = { + 'mslp': { + 'shortName': 'prmsl', + }, + 'gh250': { + 'shortName': 'gh', + 'level': 250 + }, + 'gh850': { + 'shortName': 'gh', + 'level': 850 + }, + 'u925': { + 'shortName': 'u', + 'level': 925 + }, + 'u850': { + 'shortName': 'u', + 'level': 850 + }, + 'v925': { + 'shortName': 'v', + 'level': 925 + }, + 'v850': { + 'shortName': 'v', + 'level': 850 + }, + # end of common variables + 'rh700': { + 'shortName': 'r', + 'level': 700 + }, + } + + grib_kwargs = short_name_to_gribkwargs.get(varname, None) + if grib_kwargs is not None: + logging.info("Reading data from {filepath}".format(self.filepath(fhr=fhour))) + scale = 100 if varname == "mslp" else None + return self.read_grib(fhour, varname, grib_kwargs, flip_lat=False, scale=scale) + + if (varname == 'lon1d') or (varname == 'lat1d'): + if varname not in self._data: + _ = self.data(fhour, 'mslp') + return self._data[varname] + + if varname == 'ws925': + u925 = self.data(fhour, 'u925') + v925 = self.data(fhour, 'v925') + self._data[varname] = np.sqrt((u925 * u925) + (v925 * v925)) + return self._data[varname] + + if varname == 'vor': + u850 = self.data(fhour, 'u850') + v850 = self.data(fhour, 'v850') + dx, dy = mpcalc.lat_lon_grid_deltas(self.data(fhour, 'lon1d'), + self.data(fhour, 'lat1d')) + vor = mpcalc.vorticity(u850, v850, dx, dy, dim_order='yx').magnitude * 10**5 # noqa pylint: disable=E1123, E731 + self._data[varname] = vor + return self._data[varname] + + if varname == 'thck': + gh850 = self.data(fhour, 'gh850') + gh250 = self.data(fhour, 'gh250') + self._data[varname] = gh250 - gh850 + return self._data[varname] + + raise ValueError("{varname} not recognised".format(varname=varname)) + + def calc_natl_B_parameters(self, fh , y , x ) : + lat1d = self.data(fh, 'lat1d') + _fh = np.max([fh, 6]) # limit calculation to fh >= 6 + + B48 = 1.48780513 - (0.03959059 * fh) + B120 = -3.24102228 + (0.33487425 * _fh) - (0.0655981 * _fh * + np.log(_fh)) - (0.08108531 * lat1d[y]) + B168 = -0.3103372 + (0.0153531 * fh) - (0.1962274 * lat1d[y]) + return B48, B120, B168 + + def calc_epac_B_parameters(self, fh , y , x ) : + lat1d = self.data(fh, 'lat1d') + wsmax = self.data(fh, 'wsmax') + _fh = np.max([fh, 6]) # limit calculation to fh >= 6 + + B48 = -2.73565789 - (0.03587089 * fh) + (0.27783204 * lat1d[y]) + B120 = -6.9776412459 + (0.0023649661 * _fh * _fh) - ( + 0.0004762703 * _fh * _fh * np.log(_fh)) + (0.1964929968 * + lat1d[y]) + (0.1038416607 * wsmax) + B168 = -3.37351112 + (0.01374208 * fh) + + return B48, B120, B168 + + +class NAVDefinition(ModelDefinition): + def data(self, fhour , varname ) : + if (varname in self._data) and (fhour == self._data_fhr): + return self._data[varname] + + if self._data_fhr is None: + self._data_fhr = fhour + + if fhour != self._data_fhr: + self._data = {} + self._data_fhr = fhour + + short_name_to_gribkwargs = { + 'mslp': { + 'shortName': 'prmsl', + }, + 'gh250': { + 'shortName': 'gh', + 'level': 250 + }, + 'gh850': { + 'shortName': 'gh', + 'level': 850 + }, + 'u925': { + 'shortName': 'u', + 'level': 925 + }, + 'u850': { + 'shortName': 'u', + 'level': 850 + }, + 'v925': { + 'shortName': 'v', + 'level': 925 + }, + 'v850': { + 'shortName': 'v', + 'level': 850 + }, + # end of common variables + 'rh700': { + 'shortName': 'r', + 'level': 700 + }, + } + + grib_kwargs = short_name_to_gribkwargs.get(varname, None) + if grib_kwargs is not None: + logging.info("Reading data from {}".format(self.filepath(fhr=fhour))) + scale = 100 if varname == "mslp" else None + fix_coords = True if 'lat1d' not in self._data else False + _ = self.read_grib(fhour, varname, grib_kwargs, scale=scale) + if fix_coords: + lat1d = self._data['lat1d'] + lon1d = self._data['lon1d'] + + latdiff = lat1d[1] - lat1d[0] + londiff = lon1d[1] - lon1d[0] + + self._data['lat1d'] = lat1d / latdiff / 2 + self._data['lon1d'] = lon1d / londiff / 2 + 23.5926 + self._latdiff = self._data['lat1d'][1] - self._data['lat1d'][0] + self._londiff = self._data['lon1d'][1] - self._data['lon1d'][0] + return self._data[varname] + + if (varname == 'lon1d') or (varname == 'lat1d'): + if varname not in self._data: + _ = self.data(fhour, 'mslp') + return self._data[varname] + + if varname == 'ws925': + u925 = self.data(fhour, 'u925') + v925 = self.data(fhour, 'v925') + self._data[varname] = np.sqrt((u925 * u925) + (v925 * v925)) + return self._data[varname] + + if varname == 'vor': + u850 = self.data(fhour, 'u850') + v850 = self.data(fhour, 'v850') + dx, dy = mpcalc.lat_lon_grid_deltas(self.data(fhour, 'lon1d'), + self.data(fhour, 'lat1d')) + vor = mpcalc.vorticity(u850, v850, dx, dy, dim_order='yx').magnitude * 10**5 # noqa pylint: disable=E1123, E731 + self._data[varname] = vor + return self._data[varname] + + if varname == 'thck': + gh850 = self.data(fhour, 'gh850') + gh250 = self.data(fhour, 'gh250') + self._data[varname] = gh250 - gh850 + return self._data[varname] + + raise ValueError("{varname} not recognised".format(varname=varname)) + + def calc_natl_B_parameters(self, fh , y , x ) : + rh7 = self.data(fh, 'rh700') + rh7_subset = subset_grid(rh7, y, x, self.deg5box) + rh7pert = np.max(rh7_subset) - np.mean(rh7_subset) + thmax = self.data(fh, 'thmax') + + B48 = -119.06409326 - (0.03690551 * fh) + (0.01271121 * thmax) + B120 = -1.035610445 - (0.031858673 * rh7pert) + (0.004873447 * fh) + B168 = -4.29567898 - (0.08242899 * rh7pert) + (0.0283467 * fh) + return B48, B120, B168 + + def calc_epac_B_parameters(self, fh , y , x ) : + rh7 = self.data(fh, 'rh700') + rh7_subset = subset_grid(rh7, y, x, self.deg5box) + rh7pert = np.max(rh7_subset) - np.mean(rh7_subset) + pmin = self.data(fh, 'pmin') + _fh = np.max([fh, 6]) # limit calculation to fh >= 6 + + B48 = 288.64793884 - (0.03378358 * _fh) - (0.28535942 * pmin / 100) + B120 = -1.288778 + (0.0004799016 * _fh * _fh) - (0.000002834551 * + _fh**3) - (0.03034144 * rh7pert) + B168 = -2.475415 + (11.70829 * _fh**-0.5) + (0.0000009045917 * _fh**3) - (0.08280565 * + rh7pert) + + return B48, B120, B168 + + +READERS = { + 'gfs': GFSDefinition, + 'cmc': CMCDefinition, + 'ecm': ECMDefinition, + 'ukm': UKMDefinition, + 'nav': NAVDefinition, +} diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/io_utils.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/io_utils.py similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/io_utils.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/io_utils.py diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/model_config.cfg b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/model_config.cfg similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/model_config.cfg rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/model_config.cfg diff --git a/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/model_config.cfg_bak b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/model_config.cfg_bak new file mode 100644 index 0000000000..87859cb415 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/model_config.cfg_bak @@ -0,0 +1,5 @@ +[gfs] +fname_template = /gpfs/dell1/nco/ops/com/gfs/prod/gfs.{date:%Y%m%d}/{date:%H}/gfs.t{date:%H}z.pgrb2.0p25.f{fhr:03} +# min_fhr = 0 +# max_fhr = 120 +# delta_fhr = 6 diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/tracker.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/tracker.py similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/tracker.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/tracker.py diff --git a/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/tracker.py_12162019 b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/tracker.py_12162019 new file mode 100644 index 0000000000..5fc0739cab --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/tracker.py_12162019 @@ -0,0 +1,124 @@ +import datetime +import logging + +import numpy as np + +from genesis_guidance import forecast_models +from genesis_guidance.tracker_utils import search_for_object, dist_match +import genesis_guidance.io_utils as io + + +def tctracker(model , rundate , topdir , basins ): + + basin = basins[0] + model_definition = forecast_models.READERS[model](model_name=model, + basin=basin, + rundate=rundate) + logging.info(model_definition.basin_bbox()) + + dist_output_path = io.cases_output_path(topdir, model, basin, rundate, 'dist') + if dist_output_path.exists(): + logging.info('Output file already exists : {dist_output_path}'.format( + dist_output_path=dist_output_path)) + return None + + # check that necessary model output files exist. + # Exit if not all data are available. + model_definition.check_for_input_files() + + outputs = {} + for basin in basins: + # Initialize arrays + outputs[basin] = { + 'finalinfo': None, + 'finaltcinfo': None, + } + # Loop through each forecast hour file. + for fh in model_definition.f_hours: + vtime = rundate + datetime.timedelta(hours=fh) + for basin in basins: + model_definition.basin = basin + allpinfo, alltcinfo = search_for_object(fh, model_definition, vtime, rundate) + + if allpinfo is not None: + if outputs[basin]['finalinfo'] is None: + outputs[basin]['finalinfo'] = allpinfo + else: + outputs[basin]['finalinfo'] = np.vstack( + (outputs[basin]['finalinfo'], allpinfo)) + + if alltcinfo is not None: + if outputs[basin]['finaltcinfo'] is None: + outputs[basin]['finaltcinfo'] = alltcinfo + else: + outputs[basin]['finaltcinfo'] = np.vstack( + (outputs[basin]['finaltcinfo'], alltcinfo)) + + # TODO: Make this relative to the Model definition tau array so it's more dynamic + if model == 'ukm': + tdiff_crit = 12 # hours between forecast points for matching. + else: + tdiff_crit = 6 + logging.info('Tracks complete -- Writing out data') + for basin, data in outputs.items(): + # write out info for all disturbances tracked per basin + dist_output_path = io.cases_output_path(topdir, model, basin, rundate, 'dist') + io.save_output_text(data=data['finalinfo'], filepath=dist_output_path) + + # write out info for all TC matching points tracked per basin + tc_output_path = io.cases_output_path(topdir, model, basin, rundate, 'tc') + io.save_output_text(data=data['finaltcinfo'], filepath=tc_output_path) + + # match up disturbance track points and write out storm relative files + for storm_id, match_data in dist_match(data['finalinfo'], tdiff_crit=tdiff_crit): + output_path = io.tracker_output_path(topdir, model, basin, rundate, storm_id, + 'dist') + io.save_output_text(data=match_data, filepath=output_path) + + # match up TC track points and write out storm relative files + for storm_id, match_data in dist_match(data['finaltcinfo'], tdiff_crit=tdiff_crit): + output_path = io.tracker_output_path(topdir, model, basin, rundate, storm_id, 'tc') + io.save_output_text(data=match_data, filepath=output_path) + + return outputs + + +def main(): + import argparse + + logging.getLogger().setLevel(logging.INFO) + logging.basicConfig(format=('%(asctime)s | %(filename)-19s:%(lineno)-3d |' + ' %(levelname)-8s | %(message)s'), ) + parser = argparse.ArgumentParser(description='Run NHCs Genesis Guidance algorithm') + parser.add_argument('--date', type=str, required=True) + parser.add_argument('--model', type=str, default='gfs') + parser.add_argument('--basin', type=str, nargs='*', default=['natl', 'epac']) + parser.add_argument('--odir', type=str, default='./') + parser.add_argument('--debug', type=bool, default=False) + + args = parser.parse_args() + rundate = datetime.datetime.strptime(args.date, "%Y%m%d%H") + if args.debug: + logging.getLogger().setLevel(logging.DEBUG) + + basins = args.basin + if isinstance(basins, str): # if only 1 basin passed, make it a list + basins = [ + basins, + ] + + logging.debug('Running Tracker on {basin}'.format(basin=basins)) + tctracker(model=args.model, + rundate=rundate, + topdir=args.odir, + basins=basins, + ) + logging.info('TCLOGG Complete for {basin} {date} {model}'.format( + model=args.model, + date=rundate, + basin=basins, + )) + + +if __name__ == '__main__': + main() diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/tracker_utils.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/tracker_utils.py similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/genesis_guidance/tracker_utils.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/genesis_guidance/tracker_utils.py diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/pygrib.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/grib2py.py similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/pygrib.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/grib2py.py diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/make_modulefile.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/make_modulefile.py similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/make_modulefile.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/make_modulefile.py diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/make_modulefile.py_bak b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/make_modulefile.py_bak similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/make_modulefile.py_bak rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/make_modulefile.py_bak diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/make_modulefile_4_cray.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/make_modulefile_4_cray.py similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/make_modulefile_4_cray.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/make_modulefile_4_cray.py diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/make_modulefile_4_dell.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/make_modulefile_4_dell.py similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/make_modulefile_4_dell.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/make_modulefile_4_dell.py diff --git a/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/modulefiles/tclogg b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/modulefiles/tclogg new file mode 100644 index 0000000000..2acb810503 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/modulefiles/tclogg @@ -0,0 +1,7 @@ +#%Module + +module-whatis "Sets environment for tclogg tracker" +module load python/3.6.3 +prepend-path PATH /gpfs/dell2/emc/modeling/noscrub/emc.glopara/git/tracker/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/bin +prepend-path PYTHONPATH /gpfs/dell2/emc/modeling/noscrub/emc.glopara/git/tracker/ens_tracker.v1.1.15.4/ush/FSUgenesisPY +prepend-path PYTHONPATH /usrx/local/prod/packages/python/3.6.3/lib/python3.6 diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/pathlib.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/pathlib.py similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/pathlib.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/pathlib.py diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/metpy/__init__.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/vorpy/__init__.py similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/metpy/__init__.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/vorpy/__init__.py diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/metpy/calc.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/vorpy/calc.py similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/metpy/calc.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/vorpy/calc.py diff --git a/sorc/ens_tracker.v1.1.15.2/tclogg/metpy/test_calc.py b/sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/vorpy/test_calc.py similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/tclogg/metpy/test_calc.py rename to sorc/ens_tracker.v1.1.15.4/ush/FSUgenesisPY/vorpy/test_calc.py diff --git a/sorc/ens_tracker.v1.1.15.2/ush/atcf2xml.pl b/sorc/ens_tracker.v1.1.15.4/ush/atcf2xml.pl similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/atcf2xml.pl rename to sorc/ens_tracker.v1.1.15.4/ush/atcf2xml.pl diff --git a/sorc/ens_tracker.v1.1.15.2/ush/atcf_2_cxml.sh b/sorc/ens_tracker.v1.1.15.4/ush/atcf_2_cxml.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/atcf_2_cxml.sh rename to sorc/ens_tracker.v1.1.15.4/ush/atcf_2_cxml.sh diff --git a/sorc/ens_tracker.v1.1.15.2/ush/data_check.sh b/sorc/ens_tracker.v1.1.15.4/ush/data_check.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/data_check.sh rename to sorc/ens_tracker.v1.1.15.4/ush/data_check.sh diff --git a/sorc/ens_tracker.v1.1.15.2/ush/data_check_cens.sh b/sorc/ens_tracker.v1.1.15.4/ush/data_check_cens.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/data_check_cens.sh rename to sorc/ens_tracker.v1.1.15.4/ush/data_check_cens.sh diff --git a/sorc/ens_tracker.v1.1.15.2/ush/data_check_gdas.sh b/sorc/ens_tracker.v1.1.15.4/ush/data_check_gdas.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/data_check_gdas.sh rename to sorc/ens_tracker.v1.1.15.4/ush/data_check_gdas.sh diff --git a/sorc/ens_tracker.v1.1.15.2/ush/data_check_gfs.sh b/sorc/ens_tracker.v1.1.15.4/ush/data_check_gfs.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/data_check_gfs.sh rename to sorc/ens_tracker.v1.1.15.4/ush/data_check_gfs.sh diff --git a/sorc/ens_tracker.v1.1.15.2/ush/data_check_gfs_180hr.sh b/sorc/ens_tracker.v1.1.15.4/ush/data_check_gfs_180hr.sh similarity index 92% rename from sorc/ens_tracker.v1.1.15.2/ush/data_check_gfs_180hr.sh rename to sorc/ens_tracker.v1.1.15.4/ush/data_check_gfs_180hr.sh index 97da883567..9788bcde18 100755 --- a/sorc/ens_tracker.v1.1.15.2/ush/data_check_gfs_180hr.sh +++ b/sorc/ens_tracker.v1.1.15.4/ush/data_check_gfs_180hr.sh @@ -12,7 +12,9 @@ export SLEEP_INT=60 SLEEP_LOOP_MAX=`expr $SLEEP_TIME / $SLEEP_INT` if [ ${cmodel} = "gfs" ]; then - datdir=${gfsdir}/gfs.${PDY}/${cyc} +# datdir=${gfsdir}/gfs.${PDY}/${cyc} + export COMPONENT=${COMPONENT:-atmos} + datdir=${gfsdir}/gfs.${PDY}/${cyc}/${COMPONENT} vit_incr=${FHOUT_CYCLONE:-6} fcstlen=${FHMAX_CYCLONE:-180} fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) diff --git a/sorc/ens_tracker.v1.1.15.2/ush/data_check_ukmet.sh b/sorc/ens_tracker.v1.1.15.4/ush/data_check_ukmet.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/data_check_ukmet.sh rename to sorc/ens_tracker.v1.1.15.4/ush/data_check_ukmet.sh diff --git a/sorc/ens_tracker.v1.1.15.2/ush/ens_trak_ave.sh b/sorc/ens_tracker.v1.1.15.4/ush/ens_trak_ave.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/ens_trak_ave.sh rename to sorc/ens_tracker.v1.1.15.4/ush/ens_trak_ave.sh diff --git a/sorc/ens_tracker.v1.1.15.2/ush/ens_trak_ave_2d.sh b/sorc/ens_tracker.v1.1.15.4/ush/ens_trak_ave_2d.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/ens_trak_ave_2d.sh rename to sorc/ens_tracker.v1.1.15.4/ush/ens_trak_ave_2d.sh diff --git a/sorc/ens_tracker.v1.1.15.2/ush/extrkr_fsu.sh b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_fsu.sh similarity index 81% rename from sorc/ens_tracker.v1.1.15.2/ush/extrkr_fsu.sh rename to sorc/ens_tracker.v1.1.15.4/ush/extrkr_fsu.sh index 8c45512cb5..dd4bc95abf 100755 --- a/sorc/ens_tracker.v1.1.15.2/ush/extrkr_fsu.sh +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_fsu.sh @@ -38,8 +38,10 @@ cd $TRKDATA #unlink $HOMEens_tracker/tclogg/genesis_guidance/model_config.cfg #ln -sf ${namelist} $HOMEens_tracker/tclogg/genesis_guidance/model_config.cfg -export file_name=${gfsdir}/gfs.{date:%Y%m%d}/{date:%H}/gfs.t{date:%H}z.pgrb2.0p25.f{fhr:03} -tclogg_track --date ${ymdh} --odir $TRKDATA --fname_template=${file_name} +#export file_name=${gfsdir}/gfs.{date:%Y%m%d}/{date:%H}/gfs.t{date:%H}z.pgrb2.0p25.f{fhr:03} +export COMPONENT=${COMPONENT:-atmos} +export file_name=${gfsdir}/gfs.{date:%Y%m%d}/{date:%H}/${COMPONENT}/gfs.t{date:%H}z.pgrb2.0p25.f{fhr:03} +${BINens_tracker}/tclogg_track --date ${ymdh} --odir $TRKDATA --fname_template=${file_name} #if [ "$SENDCOM" = 'YES' ]; then # cp -r ${TRKDATA}/tracker ${COMOUT}/. diff --git a/sorc/ens_tracker.v1.1.15.2/ush/extrkr_g1.sh b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_g1.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/extrkr_g1.sh rename to sorc/ens_tracker.v1.1.15.4/ush/extrkr_g1.sh diff --git a/sorc/ens_tracker.v1.1.15.2/ush/extrkr_g2.sh b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_g2.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/extrkr_g2.sh rename to sorc/ens_tracker.v1.1.15.4/ush/extrkr_g2.sh diff --git a/sorc/ens_tracker.v1.1.15.4/ush/extrkr_g2.sh_10152019 b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_g2.sh_10152019 new file mode 100755 index 0000000000..f589a92609 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_g2.sh_10152019 @@ -0,0 +1,2285 @@ +#!/bin/ksh +export PS4=' + extrkr_g2.sh line $LINENO: ' + +set +x +############################################################################## +echo " " +echo "------------------------------------------------" +echo " TC Track in model GRIB2 output " +echo " Models:GFS, GEFS, CENS, FENS, NAVGEM " +echo "------------J.Peng Jan.21, 2015 ----------------" +echo "Current time is: `date`" +echo " " +############################################################################## +set -x + +loopnum=$1 +cmodel=$2 +ymdh=$3 +pert=$4 +TRKDATA=$5 + +#userid=$LOGNAME +export trkrtype=tracker +export gribver=2 + +USE_OPER_VITALS=YES +# USE_OPER_VITALS=NO +# USE_OPER_VITALS=INIT_ONLY + +############################################################################## +# +# FLOW OF CONTROL +# +# 1. Define data directories and file names for the input model +# 2. Process input starting date/cycle information +# 3. Update TC Vitals file and select storms to be processed +# 4. Cut apart input GRIB files to select only the needed parms and hours +# 5. Execute the tracker +# 6. Copy the output track files to various locations +# +############################################################################## +msg="has begun for ${cmodel} ${pert} at ${cyc}z" +postmsg "$jlogfile" "$msg" + +# This script runs the hurricane tracker using operational GRIB model output. +# This script makes sure that the data files exist, it then pulls all of the +# needed data records out of the various GRIB forecast files and puts them +# into one, consolidated GRIB file, and then runs a program that reads the TC +# Vitals records for the input day and updates the TC Vitals (if necessary). +# It then runs gettrk, which actually does the tracking. +# +# Environmental variable inputs needed for this scripts: +# PDY -- The date for data being processed, in YYYYMMDD format +# cyc -- The numbers for the cycle for data being processed (00, 06, 12, 18) +# cmodel -- Model being processed (gfs, mrf, ukmet, ecmwf, nam, ngm, ngps, +# gdas, gfdl, ens (ncep ensemble)) +# envir -- 'prod' or 'test' +# SENDCOM -- 'YES' or 'NO' +# stormenv -- This is only needed by the tracker run for the GFDL model. +# 'stormenv' contains the name/id that is used in the input +# grib file names. +# pert -- This is only needed by the tracker run for the NCEP ensemble. +# 'pert' contains the ensemble member id (e.g., n2, p4, etc.) +# which is used as part of the grib file names. +# +# For testing script interactively in non-production set following vars: +# gltrkdir - Directory for output tracks +# archsyndir - Directory with syndir scripts/exec/fix +# + +qid=$$ +#----------------------------------------------# +# Get input date information # +#----------------------------------------------# + +export jobid=${jobid:-testjob} +export SENDCOM=${SENDCOM:-NO} +export PHASEFLAG=n +export WCORE_DEPTH=1.0 +#export PHASE_SCHEME=vtt +#export PHASE_SCHEME=cps +export PHASE_SCHEME=both +export STRUCTFLAG=n +export IKEFLAG=n + +# Define tracker working directory for this ensemble member + +if [ ! -d $TRKDATA ] +then + mkdir -p $TRKDATA +fi +cd $TRKDATA + +if [ ${#PDY} -eq 0 -o ${#cyc} -eq 0 -o ${#cmodel} -eq 0 ] +then + set +x + echo " " + echo "FATAL ERROR: Something wrong with input parameters." + echo "PDY= ${PDY}, cyc= ${cyc}, cmodel= ${cmodel}" + set -x + err_exit "FAILED ${jobid} -- BAD INPUTS AT LINE $LINENO IN TRACKER SCRIPT - ABNORMAL EXIT" +else + set +x + echo " " + echo " #-----------------------------------------------------------------#" + echo " At beginning of tracker script, the following imported variables " + echo " are defined: " + echo " PDY ................................... $PDY" + echo " cyc ................................... $cyc" + echo " cmodel ................................ $cmodel" + echo " jobid ................................. $jobid" + echo " envir ................................. $envir" + echo " SENDCOM ............................... $SENDCOM" + echo " " + set -x +fi + +scc=`echo ${PDY} | cut -c1-2` +syy=`echo ${PDY} | cut -c3-4` +smm=`echo ${PDY} | cut -c5-6` +sdd=`echo ${PDY} | cut -c7-8` +shh=${cyc} +symd=`echo ${PDY} | cut -c3-8` +syyyy=`echo ${PDY} | cut -c1-4` +CENT=`echo ${PDY} | cut -c1-2` + +#------J.Peng----01-21-2015--------- +#export COMOUTatcf=${COMOUTatcf:-${COMROOT}/nhc/${envir}/atcf} +#export archsyndir=${archsyndir:-${COMROOT}/arch/prod/syndat} +archsyndir=${archsyndir:-${COMINsyn:?}} +#export gltrkdir=${gltrkdir:-${COMROOT}/hur/${envir}/global} +gltrkdir=${gltrkdir:-${COMOUThur:?}} + +#----------------------------------------------------------------# +# +# --- Define data directories and data file names --- +# +# Convert the input model to lowercase letters and check to see +# if it's a valid model, and assign a model ID number to it. +# This model ID number is passed into the Fortran program to +# let the program know what set of forecast hours to use in the +# ifhours array. Also, set the directories for the operational +# input GRIB data files and create templates for the file names. +# +#----------------------------------------------------------------# + +cmodel=`echo ${cmodel} | tr "[A-Z]" "[a-z]"` + +case ${cmodel} in + + gfs) set +x; echo " " ; + echo " ++ operational GFS chosen" ; + echo " "; set -x ; +# gfsdir=${gfsdir:-${COMROOT}/gfs/prod/gfs.${PDY}} ; + gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}} ; + gfsgfile=gfs.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=6 ; + fcstlen=240 ; + fcsthrs=$(seq -w -s' ' 0 $vit_incr $fcstlen) ; + atcfnum=15 ; + atcfname="avnx" ; + atcfout="avnx" ; + atcffreq=600 ; + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + modtyp='global' ; + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=0 ; + model=1 ;; + + ens) set +x; echo " " ; + echo " ++ operational ensemble member ${pert} chosen"; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + + ensdira=${ensdira:-${COMIN:?}/pgrb2ap5} ; + ensgfilea=ge${pert}.t${cyc}z.pgrb2a.0p50.f ; + ensdirb=${ensdirb:-${COMIN:?}/pgrb2bp5} ; + ensgfileb=ge${pert}.t${cyc}z.pgrb2b.0p50.f ; + + vit_incr=6 ; + fcstlen=240 ; + fcsthrs=$(seq -w -s' ' 0 $vit_incr $fcstlen) ; + atcfnum=91 ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + atcfname="a${pert_posneg}${pert_num}" ; + atcfout="a${pert_posneg}${pert_num}" ; + atcffreq=600 ; + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=1 ; + modtyp='global' ; + model=10 ;; + + cmc) set +x; echo " " ; + echo " ++ operational CMC chosen" ; + echo " "; set -x ; + cmcdir=${cmcdir:-${DCOM:?}} ; + cmcgfile=CMC_glb_latlon.24x.24_${PDY}${cyc}_P ; + cmcgfile2=_NCEP.grib2 ; + + vit_incr=6 ; + fcstlen=240 ; + fcsthrs=$(seq -w -s' ' 0 $vit_incr $fcstlen) ; + + atcfnum=39 ; + atcfname="cmc " ; + atcfout="cmc" ; + atcffreq=600 ; + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + modtyp='global' ; + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=0 ; + model=15 ;; + + cens) set +x; echo " " ; + echo " ++ Canadian ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; +# ccedir=${ccedir:-${COMROOT}/gens/prod/cmce.${PDY}/${cyc}/pgrb2a}; +# ccedir=${ccedir:-${COMIN:?}/pgrb2a} ; +# ccegfile=cmc_ge${pert}.t${cyc}z.pgrb2af ; +# 2016121200_CMC_naefs_hr_latlon0p5x0p5_P210_010.grib2 ; + ccedir=${ccedir:-/dcom/us007003/${PDY}/wgrbbul/cmcens_gb2} ; + pert_num=` echo "${pert}" | cut -c2-3` ; + ccegfile=${PDY}${cyc}_CMC_naefs_hr_latlon0p5x0p5_P ; + ccegfile2=_0${pert_num}.grib2 ; + + vit_incr=6 ; + fcstlen=240 ; +# fcsthrs=$(seq -f%02g -s' ' 0 $vit_incr $fcstlen) ; + fcsthrs=$(seq -w -s' ' 0 $vit_incr $fcstlen) ; + + atcfnum=91 ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + atcfname="c${pert_posneg}${pert_num}" ; + atcfout="c${pert_posneg}${pert_num}" ; + + atcffreq=600 ; + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=1 ; + modtyp='global' ; + model=16 ;; + + fens) set +x; echo " " ; + echo " ++ FNMOC ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + +# fensdir=${fensdir:-${DCOMROOT}/us007003/${PDY}/wgrbbul/fnmocens_gb2}; + fensdir=${fensdir:-${DCOM:?}} ; + pert_num=` echo "${pert}" | cut -c2-3` ; + fensgfile=ENSEMBLE.MET.fcst_et0${pert_num}. ; + gensgfile=.${PDY}${cyc} ; + + vit_incr=6 ; + fcstlen=240 ; + fcsthrs=$(seq -w -s' ' 0 $vit_incr $fcstlen) ; + atcfnum=91 ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; +# pert_num=` echo "${pert}" | cut -c2-3` ; +# atcfname="f${pert_posneg}${pert_num}" ; +# atcfout="f${pert_posneg}${pert_num}" ; + atcfname="n${pert_posneg}${pert_num}" ; + atcfout="n${pert_posneg}${pert_num}" ; + + atcffreq=600 ; + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=1 ; + modtyp='global' ; + model=22 ;; + + ngps) set +x ; + echo " "; echo " ++ operational NAVGEM chosen" ; + echo " " ; + set -x ; + +# ngpsdir=${ngpsdir:-${COMROOT}/fnmoc/prod/navgem.${PDY}}; + ngpsdir=${ngpsdir:-${DCOM:?}} ; +#J.Peng-05-12-2016 ngpsgfile=navgem_${PDY}${cyc}f ; +# ngemgfile=.grib2 ; + ngpsgfile=US058GMET-OPSbd2.NAVGEM ; + ngemgfile=-${PDY}${cyc}-NOAA-halfdeg.gr2 ; + + vit_incr=6 ; + fcstlen=180 ; +#J.Peng-05-12-2016 fcsthrs='000 006 012 018 024 030 036 042 048 054 060 +# 066 072 084 096 108 120 132 144 156 168 180'; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + atcfnum=29 ; + atcfname="ngx" ; + atcfout="ngx" ; + atcffreq=600 ; + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + modtyp='global' ; + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=0 ; + model=7 ;; + + *) msg="FATAL ERROR: Model $cmodel is not recognized." ; + echo "$msg"; postmsg $jlogfile "$msg" ; + err_exit "FAILED ${jobid} -- UNKNOWN cmodel IN TRACKER SCRIPT - ABNORMAL EXIT";; + +esac + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${cmodel} = "gfs" ]; then + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET HGT:900 HGT:850 HGT:800 HGT:750 HGT:700 HGT:650 HGT:600 HGT:550 HGT:500 HGT:450 HGT:400 HGT:350 HGT:300 TMP:500 TMP:450 TMP:400 TMP:350 TMP:300" + + elif [ ${cmodel} = "ngps" ]; then + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV PRMSL HGT:925 HGT:850 HGT:700 HGT:500 HGT:400 HGT:300 TMP:500 TMP:400 TMP:300" + + elif [ ${cmodel} = "ens" ]; then + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET HGT:925 HGT:850 HGT:700 HGT:500 HGT:400 HGT:300 TMP:500 TMP:400 TMP:300" + + elif [ ${cmodel} = "cens" ]; then + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV PRMSL HGT:925 HGT:850 HGT:700 HGT:500 HGT:250 TMP:500 TMP:250" + + elif [ ${cmodel} = "fens" ]; then + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV PRMSL HGT:925 HGT:850 HGT:700 HGT:500 HGT:250 TMP:500 TMP:250" + fi + + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc GH:300 GH:400 GH:500 GH:925 T:300 T:400 T:500" +else + if [ ${cmodel} = "gfs" ]; then + wgrib_parmlist=" HGT:850 HGT:700 UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET" + elif [ ${cmodel} = "ens" ]; then + wgrib_parmlist=" HGT:850 HGT:700 UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET" + else + wgrib_parmlist=" HGT:850 HGT:700 UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV ABSV:850 ABSV:700 PRMSL" + fi + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc" +fi + +#---------------------------------------------------------------# +# +# -------- TC Vitals processing -------- +# +# Check Steve Lord's operational tcvitals file to see if any +# vitals records were processed for this time by his system. +# If there were, then you'll find a file in /com/gfs/prod/gfs.yymmdd +# with the vitals in it. Also check the raw TC Vitals file in +# /com/arch/prod/syndat , since this may contain storms that Steve's +# system ignored (Steve's system will ignore all storms that are +# either over land or very close to land); We still want to track +# these inland storms, AS LONG AS THEY ARE NHC STORMS (don't +# bother trying to track inland storms that are outside of NHC's +# domain of responsibility -- we don't need that info). +# UPDATE 3/27/09 MARCHOK: The SREF is run at off-synoptic times +# (03,09,15,21Z). There are no tcvitals issued at these offtimes, +# so the updating of the "old" tcvitals is critical for running +# the tracker on SREF. For updating the old tcvitals for SREF, +# we need to look 3h back, not 6h as for the other models that +# run at synoptic times. +#--------------------------------------------------------------# + +old_ymdh=`${NDATE:?} -${vit_incr} ${PDY}${cyc}` +old_4ymd=`echo ${old_ymdh} | cut -c1-8` +old_ymd=`echo ${old_ymdh} | cut -c3-8` +old_hh=`echo ${old_ymdh} | cut -c9-10` +old_str="${old_ymd} ${old_hh}00" + +future_ymdh=`${NDATE:?} ${vit_incr} ${PDY}${cyc}` +future_4ymd=`echo ${future_ymdh} | cut -c1-8` +future_ymd=`echo ${future_ymdh} | cut -c3-8` +future_hh=`echo ${future_ymdh} | cut -c9-10` +future_str="${future_ymd} ${future_hh}00" + +if [ ${modtyp} = 'global' ] +then +# synvitdir=${COMROOT}/gfs/prod/gfs.${PDY} + synvitdir=${COMINgfs:?}/${cyc} + synvitfile=gfs.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/gfs/prod/gfs.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd}/${old_hh} + synvitold_file=gfs.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/gfs/prod/gfs.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd}/${future_hh} + synvitfuture_file=gfs.t${future_hh}z.syndata.tcvitals.tm00 +else +# synvitdir=${COMROOT}/nam/prod/nam.${PDY} + synvitdir=${COMINnam:?} + synvitfile=nam.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/nam/prod/nam.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=nam.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/nam/prod/nam.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=nam.t${future_hh}z.syndata.tcvitals.tm00 +fi + +set +x +echo " " +echo " -----------------------------" +echo " " +echo " Now sorting and updating the TC Vitals file. Please wait...." +echo " " +set -x + +current_str="${symd} ${cyc}00" + +if [ -s ${synvitdir}/${synvitfile} -o\ + -s ${synvitold_dir}/${synvitold_file} -o\ + -s ${synvitfuture_dir}/${synvitfuture_file} ] +then + grep "${old_str}" ${synvitold_dir}/${synvitold_file} \ + >${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${synvitdir}/${synvitfile} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${future_str}" ${synvitfuture_dir}/${synvitfuture_file} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} +else + set +x + echo " " + echo " There is no (synthetic) TC vitals file for ${cyc}z in ${synvitdir}," + echo " nor is there a TC vitals file for ${old_hh}z in ${synvitold_dir}." + echo " nor is there a TC vitals file for ${future_hh}z in ${synvitfuture_dir}," + echo " Checking the raw TC Vitals file ....." + echo " " + set -x +fi + +# Take the vitals from Steve Lord's /com/gfs/prod tcvitals file, +# and cat them with the NHC-only vitals from the raw, original +# /com/arch/prod/synda_tcvitals file. Do this because the nwprod +# tcvitals file is the original tcvitals file, and Steve runs a +# program that ignores the vitals for a storm that's over land or +# even just too close to land, and for tracking purposes for the +# US regional models, we need these locations. Only include these +# "inland" storm vitals for NHC (we're not going to track inland +# storms that are outside of NHC's domain of responsibility -- we +# don't need that info). +# UPDATE 5/12/98 MARCHOK: awk logic is added to screen NHC +# vitals such as "89E TEST", since TPC +# does not want tracks for such storms. + +grep "${old_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${current_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${future_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + + +# IMPORTANT: When "cat-ing" these files, make sure that the vitals +# files from the "raw" TC vitals files are first in order and Steve's +# TC vitals files second. This is because Steve's vitals file has +# been error-checked, so if we have a duplicate tc vitals record in +# these 2 files (very likely), program supvit.x below will +# only take the last vitals record listed for a particular storm in +# the vitals file (all previous duplicates are ignored, and Steve's +# error-checked vitals records are kept). + +cat ${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} ${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} \ + >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +#--------------------------------------------------------------# +# Now run a fortran program that will read all the TC vitals +# records for the current dtg and the dtg from 6h ago, and +# sort out any duplicates. If the program finds a storm that +# was included in the vitals file 6h ago but not for the current +# dtg, this program updates the 6h-old first guess position +# and puts these updated records as well as the records from +# the current dtg into a temporary vitals file. It is this +# temporary vitals file that is then used as the input for the +# tracking program. +#--------------------------------------------------------------# + +oldymdh=`${NDATE:?} -${vit_incr} ${PDY}${cyc}` +oldyy=`echo ${oldymdh} | cut -c3-4` +oldmm=`echo ${oldymdh} | cut -c5-6` +olddd=`echo ${oldymdh} | cut -c7-8` +oldhh=`echo ${oldymdh} | cut -c9-10` +oldymd=${oldyy}${oldmm}${olddd} + +futureymdh=`${NDATE:?} 6 ${PDY}${cyc}` +futureyy=`echo ${futureymdh} | cut -c3-4` +futuremm=`echo ${futureymdh} | cut -c5-6` +futuredd=`echo ${futureymdh} | cut -c7-8` +futurehh=`echo ${futureymdh} | cut -c9-10` +futureymd=${futureyy}${futuremm}${futuredd} + +echo "&datenowin dnow%yy=${syy}, dnow%mm=${smm}," >${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&dateoldin dold%yy=${oldyy}, dold%mm=${oldmm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dold%dd=${olddd}, dold%hh=${oldhh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&datefuturein dfuture%yy=${futureyy}, dfuture%mm=${futuremm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dfuture%dd=${futuredd}, dfuture%hh=${futurehh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&hourinfo vit_hr_incr=${vit_incr}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period." + echo "!!! File ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} is empty." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi + +fi + +# For tcgen cases, filter to use only vitals from the ocean +# basin of interest.... + +if [ ${trkrtype} = 'tcgen' ] + then + + if [ ${numvitrecs} -gt 0 ] + then + + fullvitfile=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + cp $fullvitfile ${TRKDATA}/vitals.all_basins.${atcfout}.${PDY}${cyc} + basin=` echo $regtype | cut -c1-2` + + if [ ${basin} = 'al' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "L") print $0}' \ + >${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + if [ ${basin} = 'ep' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "E") print $0}' \ + >${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + if [ ${basin} = 'wp' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "W") print $0}' \ + >${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + + cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + + fi + +fi + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Before running the program to read, sort and update the vitals, +# first run the vitals through some awk logic, the purpose of +# which is to convert all the 2-digit years into 4-digit years. +# We need this logic to ensure that all the vitals going +# into supvit.f have uniform, 4-digit years in their records. +# +# 1/8/2000: sed code added by Tim Marchok due to the fact that +# some of the vitals were getting past the syndata/qctropcy +# error-checking with a colon in them; the colon appeared +# in the character immediately to the left of the date, which +# was messing up the "(length($4) == 8)" statement logic. +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +sed -e "s/\:/ /g" ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} > ${TRKDATA}/tempvit +mv ${TRKDATA}/tempvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +awk ' +{ + yycheck = substr($0,20,2) + if ((yycheck == 20 || yycheck == 19) && (length($4) == 8)) { + printf ("%s\n",$0) + } + else { + if (yycheck >= 0 && yycheck <= 50) { + printf ("%s20%s\n",substr($0,1,19),substr($0,20)) + } + else { + printf ("%s19%s\n",substr($0,1,19),substr($0,20)) + } + } +} ' ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 + +mv ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +if [ ${numvitrecs} -gt 0 ] +then + + export pgm=supvit_g2 + . prep_step + + export FORT31=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + export FORT51=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_g2 <${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + suvrcc=$? + + if [ ${suvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit.x, " + echo "!!! which is the program that updates the TC Vitals file." + echo "!!! Return code from supvit.x = ${suvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING supvit_g2 IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + + touch ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#----------------------------------------------------------------- +# In this section, check to see if the user requested the use of +# operational TC vitals records for the initial time only. This +# option might be used for a retrospective medium range forecast +# in which the user wants to initialize with the storms that are +# currently there, but then let the model do its own thing for +# the next 10 or 14 days.... + + +if [ ${USE_OPER_VITALS} = 'INIT_ONLY' ]; then + + if [ ${init_flag} = 'yes' ]; then + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are currently at." + echo " " + set -x + else + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are now *PAST*." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + fi + +elif [ ${USE_OPER_VITALS} = 'NO' ]; then + + set +x + echo " " + echo "NOTE: User has requested that historical vitals not be used...." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#------------------------------------------------------------------# +# Now select all storms to be processed, that is, process every +# storm that's listed in the updated vitals file for the current +# forecast hour. If there are no storms for the current time, +# then exit. +#------------------------------------------------------------------# + +numvitrecs=`cat ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period " + echo "!!! in the UPDATED vitals file." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi +fi + +set +x +echo " " +echo " *--------------------------------*" +echo " | STORM SELECTION |" +echo " *--------------------------------*" +echo " " +set -x + +ict=1 +while [ $ict -le 15 ] +do + stormflag[${ict}]=3 + let ict=ict+1 +done + +dtg_current="${symd} ${cyc}00" +stormmax=` grep "${dtg_current}" ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +if [ ${stormmax} -gt 15 ] +then + stormmax=15 +fi + +sct=1 +while [ ${sct} -le ${stormmax} ] +do + stormflag[${sct}]=1 + let sct=sct+1 +done + + +#---------------------------------------------------------------# +# +# -------- "Genesis" Vitals processing -------- +# +# May 2006: This entire genesis tracking system is being +# upgraded to more comprehensively track and categorize storms. +# One thing that has been missing from the tracking system is +# the ability to keep track of storms from one analysis cycle +# to the next. That is, the current system has been very +# effective at tracking systems within a forecast, but we have +# no methods in place for keeping track of storms across +# difference initial times. For example, if we are running +# the tracker on today's 00z GFS analysis, we will get a +# position for various storms at the analysis time. But then +# if we go ahead and run again at 06z, we have no way of +# telling the tracker that we know about the 00z position of +# this storm. We now address that problem by creating +# "genesis" vitals, that is, when a storm is found at an +# analysis time, we not only produce "atcfunix" output to +# detail the track & intensity of a found storm, but we also +# produce a vitals record that will be used for the next +# run of the tracker script. These "genesis vitals" records +# will be of the format: +# +# YYYYMMDDHH_AAAH_LLLLX_TYP +# +# Where: +# +# YYYYMMDDHH = Date the storm was FIRST identified +# by the tracker. +# AAA = Abs(Latitude) * 10; integer value +# H = 'N' for norther hem, 'S' for southern hem +# LLLL = Abs(Longitude) * 10; integer value +# X = 'E' for eastern hem, 'W' for western hem +# TYP = Tropical cyclone storm id if this is a +# tropical cyclone (e.g., "12L", or "09W", etc). +# If this is one that the tracker instead "Found +# On the Fly (FOF)", we simply put those three +# "FOF" characters in there. +genvitfile=${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} +touch ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + +if [ -f $genvitfile ]; then + d6ago_ymdh=` ${NDATE:?} -6 ${PDY}${cyc}` + d6ago_4ymd=` echo ${d6ago_ymdh} | cut -c1-8` + d6ago_ymd=` echo ${d6ago_ymdh} | cut -c3-8` + d6ago_hh=` echo ${d6ago_ymdh} | cut -c9-10` + d6ago_str="${d6ago_ymd} ${d6ago_hh}00" + + d6ahead_ymdh=` ${NDATE:?} 6 ${PDY}${cyc}` + d6ahead_4ymd=` echo ${d6ahead_ymdh} | cut -c1-8` + d6ahead_ymd=` echo ${d6ahead_ymdh} | cut -c3-8` + d6ahead_hh=` echo ${d6ahead_ymdh} | cut -c9-10` + d6ahead_str="${d6ahead_ymd} ${d6ahead_hh}00" + + syyyym6=` echo ${d6ago_ymdh} | cut -c1-4` + smmm6=` echo ${d6ago_ymdh} | cut -c5-6` + sddm6=` echo ${d6ago_ymdh} | cut -c7-8` + shhm6=` echo ${d6ago_ymdh} | cut -c9-10` + + syyyyp6=` echo ${d6ahead_ymdh} | cut -c1-4` + smmp6=` echo ${d6ahead_ymdh} | cut -c5-6` + sddp6=` echo ${d6ahead_ymdh} | cut -c7-8` + shhp6=` echo ${d6ahead_ymdh} | cut -c9-10` + + set +x + echo " " + echo " d6ago_str= --->${d6ago_str}<---" + echo " current_str= --->${current_str}<---" + echo " d6ahead_str= --->${d6ahead_str}<---" + echo " " + #echo " Listing and contents of ${genvitfile} follow " + #echo " for the times 6h ago, current and 6h ahead:" + #echo " " + set -x + + #ls -la ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + #cat ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + + grep "${d6ago_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${d6ahead_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + num_gen_vits=`cat ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} | wc -l` +else + echo "WARNING: Genesis Vitals from previous run(s) not found!" +fi + +echo "&datenowin dnow%yy=${syyyy}, dnow%mm=${smm}," >${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6agoin d6ago%yy=${syyyym6}, d6ago%mm=${smmm6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ago%dd=${sddm6}, d6ago%hh=${shhm6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6aheadin d6ahead%yy=${syyyyp6}, d6ahead%mm=${smmp6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ahead%dd=${sddp6}, d6ahead%hh=${shhp6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + +num_gen_vits=0 + +if [ ${num_gen_vits} -gt 0 ] +then + export pgm=supvit_gen + . prep_step + + export FORT31=${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + export FORT51=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_gen <${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + sgvrcc=$? + + if [ ${sgvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit_gen, " + echo "!!! which is the program that updates the genesis vitals file." + echo "!!! Return code from supvit_gen = ${sgvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING SUPVIT_GEN IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + + touch ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + +fi + + +#-----------------------------------------------------------------# +# +# ------ CUT APART INPUT GRIB FILES ------- +# +# For the selected model, cut apart the GRIB input files in order +# to pull out only the variables that we need for the tracker. +# Put these selected variables from all forecast hours into 1 big +# GRIB file that we'll use as input for the tracker. +# +#-----------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------" +echo " NOW CUTTING APART INPUT GRIB FILES TO " +echo " CREATE 1 BIG GRIB INPUT FILE " +echo " -----------------------------------------" +echo " " +set -x + +regflag=`grep NHC ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +# -------------------------------------------------- +# Process GFS data +# -------------------------------------------------- + +if [ ${model} -eq 1 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gfsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc} + fi + + rm ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f* + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gfsgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gfs wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gfsdir:?}/${gfsgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GFS file is missing" + echo "!!! Check for the existence of this file:" + echo "!!! GFS File: ${gfsdir}/${gfsgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING GFS FILE ${gfsdir}/${gfsgfile}${fhour} IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${gfsdir}/${gfsgfile}${fhour} + ${WGRIB2:?} -s $gfile >gfs.ix + export err=$?; err_chk + + for parm in ${wgrib_parmlist} + do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m " gfs.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} ;; + "SurfaceV") + grep "VGRD:10 m " gfs.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} ;; + *) + grep "${parm}" gfs.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} ;; + esac + done + + gfs_master_file=${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + gfs_cat_file=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfs_master_file} ${gfs_master_file}.ix + export err=$?; err_chk + + g1=${gfs_master_file} + x1=${gfs_master_file}.ix + cat ${gfs_master_file} >>${gfs_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gfsgribfile.${PDY}${cyc} ${TRKDATA}/gfsixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + + catfile=${TRKDATA}/gfs.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for model= $cmodel and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} $ffile $ifile + export err=$?; err_chk + +# gparm=7 +# namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z +# . prep_step +# echo "&timein ifcsthour=${fhour}," >${namelist} +# echo " iparm=${gparm}," >>${namelist} +# echo " gribver=${gribver}," >>${namelist} +# echo " g2_jpdtn=${g2_jpdtn}/" >>${namelist} +# echo " g2_model=${model}/" >>${namelist} +# ln -s -f ${ffile} fort.11 +# ln -s -f ${FIXens_tracker}/gfs_hgt_levs.txt fort.16 +# ln -s -f ${ifile} fort.31 +# ln -s -f ${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} fort.51 +# ${EXECens_tracker}/vint.x <${namelist} +# rcc1=$? + +# gparm=11 +# namelist=${TRKDATA}/vint_input.${PDY}${cyc}.t +# . prep_step +# echo "&timein ifcsthour=${fhour}," >${namelist} +# echo " iparm=${gparm}," >>${namelist} +# echo " gribver=${gribver}," >>${namelist} +# echo " g2_jpdtn=${g2_jpdtn}/" >>${namelist} +# echo " g2_model=${model}/" >>${namelist} +# ln -s -f ${ffile} fort.11 +# ln -s -f ${FIXens_tracker}/gfs_tmp_levs.txt fort.16 +# ln -s -f ${ifile} fort.31 +# ln -s -f ${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} fort.51 +# ${EXECens_tracker}/vint.x <${namelist} +# rcc2=$? + + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}/" >>${namelist} + echo " g2_model=${model}/" >>${namelist} + +# ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} +# ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + + . prep_step + + # Input files + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave.x <${namelist} + export err=$?; err_chk +# rcc3=$? + +# if [ $rcc1 -eq 0 -a $rcc2 -eq 0 -a $rcc3 -eq 0 ]; then +# echo " " +# else +# mailfile=${FIXens_tracker}/errmail.${cmodel}.${PDY}${cyc} +# echo "FATAL ERROR: CPS/WC interp failure for $cmodel ${PDY}${cyc}" >${mailfile} +# mail -s "GFS Failure (CPS/WC int) $cmodel ${PDY}${cyc}" ${userid} <${mailfile} +# err_exit +# fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} +# zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} +# cat ${zfile} ${tavefile} >>${catfile} + + cat ${tavefile} >>${catfile} +# rm $tavefile $zfile + + set +x + echo " " + echo "Date in interpolation for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + + fi + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gfsixfile.${PDY}${cyc} + +fi + +# -------------------------------------------------- +# Process NCEP Ensemble perturbation, if selected +# -------------------------------------------------- + +if [ ${model} -eq 10 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ensdira:?}/${ensgfilea}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: ENSEMBLE ${PERT} File missing: ${ensdira}/${ensgfilea}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING GEFS FILE ${ensdira}/${ensgfilea}${fhour} IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${TRKDATA}/${ensgfilea}${fhour} + cat ${ensdira}/${ensgfilea}${fhour} ${ensdirb}/${ensgfileb}${fhour} > $gfile + + ${WGRIB2:?} -s $gfile >ens.ix + export err=$?; err_chk + + for parm in ${wgrib_parmlist} + do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m " ens.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ;; + "SurfaceV") + grep "VGRD:10 m " ens.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ;; + *) + grep "${parm}" ens.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ;; + esac + done + done + ${GRB2INDEX:?} ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/ens${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for pert= $pert and fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + + gparm=7 + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + . prep_step + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}/" >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ens_hgt_levs.txt + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_${pert}.${PDY}${cyc}.z.f${fhour} + ${EXECens_tracker}/vint.x <${namelist} + rcc1=$? + + gparm=11 + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.t + . prep_step + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}/" >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ens_tmp_levs.txt + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_${pert}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint.x <${namelist} + rcc2=$? + + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + . prep_step + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}/" >>${namelist} + echo " g2_model=${model}/" >>${namelist} + ffile=${TRKDATA}/${cmodel}_${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}_${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + export FORT11=${ffile} + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/tave.x <${namelist} + rcc3=$? + + if [ $rcc1 -eq 0 -a $rcc2 -eq 0 -a $rcc3 -eq 0 ]; then + echo " " + else +# mailfile=${FIXens_tracker}/errmail.${cmodel}.${pert}.${PDY}${cyc} +# echo "CPS/WC interp failure for $cmodel $pert ${PDY}${cyc}" >${mailfile} +# mail -s "GEFS Failure (CPS/WC int) $cmodel $pert ${PDY}${cyc}" ${userid} <${mailfile} + msg="FATAL ERROR: CPS/WC interp failure for $cmodel $pert ${PDY}${cyc}" + echo "$msg"; postmsg $jlogfile "$msg" + err_exit "$msg" + fi + + tavefile=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}_${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + rm $tavefile $zfile + + set +x + echo " " + echo "Date in interpolation for pert= $pert and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + + gfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------------------------------ +# Process Canadian (CMC) hi-res deterministic, if selected +# ------------------------------------------------------ + +if [ ${model} -eq 15 ]; then + + if [ $loopnum -eq 1 ]; then + if [ -s ${TRKDATA}/cmcgribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/cmcgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + if [ ! -s ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} ]; then + set +x + echo " " + echo "FATAL ERROR: CMC File missing ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + fi + + gfile=${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} + ${WGRIB2:?} -s $gfile >cmc.ix + export err=$?; err_chk + + for parm in ${wgrib_parmlist}; do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m above" cmc.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cmcgribfile.${PDY}${cyc} ;; + + "SurfaceV") + grep "VGRD:10 m above" cmc.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cmcgribfile.${PDY}${cyc} ;; + *) + grep "${parm}" cmc.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cmcgribfile.${PDY}${cyc} ;; + esac + done + done +# J.Peng--04-05-2017--changing grid from 180E to 0E------------ +# wgrib2 junk -small_grib 0:359.76 -90:90 junk2 +# cp ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/junk +# rm -f ${TRKDATA}/cmcgribfile.${PDY}${cyc} +# ${WGRIB2} ${TRKDATA}/junk -small_grib 0:359.76 -90:90 ${TRKDATA}/junk2 +# cp ${TRKDATA}/junk2 ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + ${GRB2INDEX:?} ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/cmcixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +fi + +# ------------------------------------------------------ +# Process Canadian Ensemble perturbation, if selected +# ------------------------------------------------------ +if [ ${model} -eq 16 ]; then + if [ $loopnum -eq 1 ]; then + + if [ -s ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + +# if [ ! -s ${ccedir:?}/${ccegfile}${fhour} ]; then + if [ ! -s ${ccedir:?}/${ccegfile}${fhour}${ccegfile2} ]; then + set +x + echo " " +# echo "FATAL ERROR: CANADIAN ENSEMBLE ${PERT} missing: ${ccegfile}${fhour}" + echo "FATAL ERROR: CANADIAN ENSEMBLE ${PERT} missing: ${ccegfile}${fhour}${ccegfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING CMC ENSEMBLE FILE ${ccegfile}${fhour}${ccegfile2} IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + +# gfile=${ccedir}/${ccegfile}${fhour} + gfile=${ccedir}/${ccegfile}${fhour}${ccegfile2} + ${WGRIB2:?} -s $gfile >ens.ix + export err=$?; err_chk + + for parm in ${wgrib_parmlist}; do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m " ens.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ;; + "SurfaceV") + grep "VGRD:10 m " ens.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ;; + *) + grep "${parm}" ens.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ;; + esac + done + done + + ${GRB2INDEX:?} ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk +# Process the cyclone phase variables, if requested + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + . prep_step + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_hgt_levs.txt + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + ${EXECens_tracker}/vint.x <${namelist} + rcc1=$? + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + gparm=11 + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + . prep_step + echo "&timein ifcsthour=${fhour}, iparm=${gparm}/" >${namelist} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_tmp_levs.txt + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint.x <${namelist} + rcc2=$? + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + . prep_step + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + export FORT11=${ffile} + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/tave.x <${namelist} + rcc3=$? + + if [ $rcc1 -eq 0 -a $rcc2 -eq 0 -a $rcc3 -eq 0 ]; then + echo " " + else +# mailfile=${FIXens_tracker}/errmail.${cmodel}.${pert}.${PDY}${cyc} +# echo "CPS/WC interp failure for $cmodel $pert ${PDY}${cyc}" >${mailfile} +# mail -s "ENS Failure (CPS/WC int) $cmodel $pert ${PDY}${cyc}" jiayi.peng@noaa.gov <${mailfile} + msg="FATAL ERROR: CPS/WC interp failure for $cmodel $pert ${PDY}${cyc}" + echo "$msg"; postmsg $jlogfile "$msg" + err_exit "$msg" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} +# rm $tavefile $zfile + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + gribfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------ +# Process FNMOC Ensemble, if selected +# ------------------------------ +if [ ${model} -eq 22 ] +then + if [ $loopnum -eq 1 ] + then + if [ -s ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ] + then + rm ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + fensfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + + let attempts=1 + while [ $attempts -le 30 ]; do + if [ -s $fensfile ]; then + break + else + sleep 60 + let attempts=attempts+1 + fi + done + if [ $attempts -gt 30 ] && [ ! -s $fensfile ]; then + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "$fensfile" >> ${DATA}/missing_fens.txt + exit + else + err_exit "$fensfile still not available after waiting 30 minutes... exiting" + fi + fi + + export pgm=wgrib2 + #${WGRIB2:?} -set_byte 1 11 1 -s $fensfile >fens.ix # set_byte resets local table to 1 to remove error messages + ${WGRIB2:?} -s $fensfile >fens.ix + err=$? + if [ $err -ne 0 ]; then + err_exit "Error reading $fensfile" + fi + + for parm in ${wgrib_parmlist} + do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m " fens.ix | ${WGRIB2:?} -i $fensfile -append -grib \ + ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ;; + + "SurfaceV") + grep "VGRD:10 m " fens.ix | ${WGRIB2:?} -i $fensfile -append -grib \ + ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ;; + *) + grep "${parm}" fens.ix | ${WGRIB2:?} -i $fensfile -append -grib \ + ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ;; + esac + done + unset pgm + done + + ${GRB2INDEX:?} ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + export err=$?; err_chk +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc}.z + . prep_step + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_hgt_levs.txt + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + ${EXECens_tracker}/vint.x <${namelist} + rcc1=$? + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + gparm=11 + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc} + . prep_step + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_tmp_levs.txt + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint.x <${namelist} + rcc2=$? + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + . prep_step + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + export FORT11=${ffile} + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/tave.x <${namelist} + rcc3=$? + + if [ $rcc1 -eq 0 -a $rcc2 -eq 0 -a $rcc3 -eq 0 ]; then + echo " " + else +# mailfile=${FIXens_tracker}/errmail.${cmodel}.${pert}.${PDY}${cyc} +# echo "CPS/WC interp failure for $cmodel $pert ${PDY}${cyc}" >${mailfile} +# mail -s "ENS Failure (CPS/WC int) $cmodel $pert ${PDY}${cyc}" jiayi.peng@noaa.gov <${mailfile} + msg="FATAL ERROR: CPS/WC interp failure for $cmodel $pert ${PDY}${cyc}" + echo "$msg"; postmsg $jlogfile "$msg" + err_exit "$msg" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} +# rm $tavefile $zfile + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ixfile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} +fi + +# ------------------------------ +# Process NAVGEM, if selected +# ------------------------------ +if [ ${model} -eq 7 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} ] + then + set +x + echo " " + echo "FATAL ERROR: NAVGEM File missing ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" >> ${DATA}/missing_ngps.txt + exit + else + err_exit "MISSING NAVGEM FILE at $0:$LINENO" + fi + fi + + gfile=${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} + ${WGRIB2:?} -s $gfile >ngps.ix + export err=$?; err_chk + + for parm in ${wgrib_parmlist} + do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m above" ngps.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ;; + + "SurfaceV") + grep "VGRD:10 m above" ngps.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ;; + *) + grep "${parm}" ngps.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ;; + esac + done + done + + ${GRB2INDEX:?} ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ${TRKDATA}/ngpsixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + . prep_step + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngem_hgt_levs.txt + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + ${EXECens_tracker}/vint.x <${namelist} + rcc1=$? + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + gparm=11 + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + . prep_step + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngem_tmp_levs.txt + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint.x <${namelist} + rcc2=$? + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + gparm=11 + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + . prep_step + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + export FORT11=${ffile} + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/tave.x <${namelist} + rcc3=$? + + if [ $rcc1 -eq 0 -a $rcc2 -eq 0 -a $rcc3 -eq 0 ]; then + echo " " + else +# mailfile=${FIXens_tracker}/errmail.${cmodel}.${pert}.${PDY}${cyc} +# echo "CPS/WC interp failure for $cmodel $pert ${PDY}${cyc}" >${mailfile} +# mail -s "ENS Failure (CPS/WC int) $cmodel $pert ${PDY}${cyc}" jiayi.peng@noaa.gov <${mailfile} + msg="FATAL ERROR: CPS/WC interp failure for $cmodel $pert ${PDY}${cyc}" + echo "$msg"; postmsg $jlogfile "$msg" + err_exit "$msg" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ngpsixfile.${PDY}${cyc} +fi + +#------------------------------------------------------------------------# +# Now run the tracker # +#------------------------------------------------------------------------# +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW EXECUTING TRACKER......" +echo " -----------------------------------------------" +echo " " +set -x + +ist=1 +while [ $ist -le 15 ] +do + if [ ${stormflag[${ist}]} -ne 1 ] + then + set +x; echo "Storm number $ist NOT selected for processing"; set -x + else + set +x; echo "Storm number $ist IS selected for processing...."; set -x + fi + let ist=ist+1 +done + +namelist=${TRKDATA}/input.${atcfout}.${PDY}${cyc} +ATCFNAME=` echo "${atcfname}" | tr '[a-z]' '[A-Z]'` + +if [ ${cmodel} = 'sref' ]; then + export atcfymdh=` ${NDATE:?} -3 ${scc}${syy}${smm}${sdd}${shh}` +else + export atcfymdh=${scc}${syy}${smm}${sdd}${shh} +fi + +contour_interval=100.0 +write_vit=n +want_oci=.TRUE. + +echo "&datein inp%bcc=${scc},inp%byy=${syy},inp%bmm=${smm}," >${namelist} +echo " inp%bdd=${sdd},inp%bhh=${shh},inp%model=${model}," >>${namelist} +echo " inp%modtyp='${modtyp}'," >>${namelist} +echo " inp%lt_units='${lead_time_units}'," >>${namelist} +echo " inp%file_seq='${file_sequence}'," >>${namelist} +echo " inp%nesttyp='${nest_type}'/" >>${namelist} +echo "&atcfinfo atcfnum=${atcfnum},atcfname='${ATCFNAME}'," >>${namelist} +echo " atcfymdh=${atcfymdh},atcffreq=${atcffreq}/" >>${namelist} +echo "&trackerinfo trkrinfo%westbd=${trkrwbd}," >>${namelist} +echo " trkrinfo%eastbd=${trkrebd}," >>${namelist} +echo " trkrinfo%northbd=${trkrnbd}," >>${namelist} +echo " trkrinfo%southbd=${trkrsbd}," >>${namelist} +echo " trkrinfo%type='${trkrtype}'," >>${namelist} +echo " trkrinfo%mslpthresh=${mslpthresh}," >>${namelist} +echo " trkrinfo%v850thresh=${v850thresh}," >>${namelist} +echo " trkrinfo%gridtype='${modtyp}'," >>${namelist} +echo " trkrinfo%contint=${contour_interval}," >>${namelist} +echo " trkrinfo%want_oci=${want_oci}," >>${namelist} +echo " trkrinfo%out_vit='${write_vit}'," >>${namelist} +echo " trkrinfo%gribver=${gribver}," >>${namelist} +echo " trkrinfo%g2_jpdtn=${g2_jpdtn}/" >>${namelist} +echo "&phaseinfo phaseflag='${PHASEFLAG}'," >>${namelist} +echo " phasescheme='${PHASE_SCHEME}'," >>${namelist} +echo " wcore_depth=${WCORE_DEPTH}/" >>${namelist} +echo "&structinfo structflag='${STRUCTFLAG}'," >>${namelist} +echo " ikeflag='${IKEFLAG}'/" >>${namelist} +echo "&fnameinfo gmodname='${atcfname}'," >>${namelist} +echo " rundescr='${rundescr}'," >>${namelist} +echo " atcfdescr='${atcfdescr}'/" >>${namelist} +echo "&verbose verb=3/" >>${namelist} +echo "&waitinfo use_waitfor='n'," >>${namelist} +echo " wait_min_age=10," >>${namelist} +echo " wait_min_size=100," >>${namelist} +echo " wait_max_wait=1800," >>${namelist} +echo " wait_sleeptime=5," >>${namelist} +echo " per_fcst_command=''/" >>${namelist} + +export pgm=gettrk_g2 +. prep_step + +export FORT11=${gribfile} +export FORT12=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} +export FORT14=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes +export FORT31=${ixfile} + +if [ ${trkrtype} = 'tracker' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT61=${TRKDATA}/trak.${atcfout}.all.${stormenv}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${stormenv}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${stormenv}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${stormenv}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${stormenv}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${stormenv}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${stormenv}.${PDY}${cyc} + else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${PDY}${cyc} + fi +else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${regtype}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${regtype}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${regtype}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${regtype}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${regtype}.${PDY}${cyc} +fi + +if [ ${atcfname} = 'aear' ] +then + export FORT65=${TRKDATA}/trak.${atcfout}.initvitl.${PDY}${cyc} +fi + +if [ ${write_vit} = 'y' ] +then + export FORT67=${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} +fi + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${stormenv}.${PDY}${cyc} + else + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${PDY}${cyc} + fi +fi + +if [ ${STRUCTFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${stormenv}.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${stormenv}.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${stormenv}.${PDY}${cyc} + else + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${PDY}${cyc} + fi +fi + +if [ ${IKEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${stormenv}.${PDY}${cyc} + else + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${PDY}${cyc} + fi +fi + +if [ ${trkrtype} = 'midlat' -o ${trkrtype} = 'tcgen' ]; then + export FORT77=${TRKDATA}/trkrmask.${atcfout}.${regtype}.${PDY}${cyc} +fi + +msg="$pgm start for $atcfout at ${cyc}z" +postmsg "$jlogfile" "$msg" + +set +x +echo "+++ TIMING: BEFORE gettrk ---> `date`" +set -x + +set +x +echo " " +echo "TIMING: Before call to gettrk at `date`" +echo " " +set -x + +${EXECens_tracker}/gettrk_g2 <${namelist} +gettrk_rcc=$? +if [ ${gettrk_rcc} -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR: An error occurred while running gettrk.x, " + echo "!!! which is the program that actually gets the track." + echo "!!! Return code from gettrk.x = ${gettrk_rcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING gettrk_g2 IN TRACKER SCRIPT- ABNORMAL EXIT" +fi + +set +x +echo " " +echo "TIMING: After call to gettrk at `date`" +echo " " +set -x + +set +x +echo "+++ TIMING: AFTER gettrk ---> `date`" +set -x + +#--------------------------------------------------------------# +# Now copy the output track files to different directories +#--------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW COPYING OUTPUT TRACK FILES TO COM " +echo " -----------------------------------------------" +echo " " +set -x + + +if [ -s ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} ]; then + cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh}|cut -c1-95 > ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} ${TRKDATA}/long.${atcfout}.atcfunix.${PDY}${shh} + cp ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} +fi + +msg="$pgm end for $atcfout at ${cyc}z completed normally" +postmsg "$jlogfile" "$msg" + +# Copy atcf files to NHC archives. We'll use Steve Lord's original script, +# distatcf.sh, to do this, and that script requires the input atcf file to +# have the name "attk126", so first copy the file to that name, then call +# the distatcf.sh script. After that's done, then copy the full 0-72h +# track into the /com/hur/prod/global track archive file. + +if [ "$SENDCOM" = 'YES' ] +then + + glatuxarch=${glatuxarch:-${gltrkdir}/tracks.atcfunix.${syy}} + cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} >>${glatuxarch} + + if [ ${cmodel} = 'gfdl' ] + then + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} ${COMOUT}/${stormenv}.${PDY}${cyc}.trackeratcfunix + else + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} ${COMOUT}/${atcfout}.t${cyc}z.cyclone.trackatcfunix + fi + + if [ "$SENDDBN" = 'YES' ] + then + if [ ${cmodel} != 'gfdl' ] + then + $DBNROOT/bin/dbn_alert MODEL ENS_TRACKER $job ${COMOUT}/${atcfout}.t${cyc}z.cyclone.trackatcfunix + fi + fi + +# ------------------------------------------ +# Cat atcfunix files to storm trackers files +# ------------------------------------------ +# +# We need to parse apart the atcfunix file and distribute the forecasts to +# the necessary directories. To do this, first sort the atcfunix records +# by forecast hour (k6), then sort again by ocean basin (k1), storm number (k2) +# and then quadrant radii wind threshold (k12). Once you've got that organized +# file, break the file up by putting all the forecast records for each storm +# into a separate file. Then, for each file, find the corresponding atcfunix +# file in the storm trackers directory and dump the atcfunix records for that storm +# in there. NOTE: Only do this if the model run is NOT for the CMC or +# ECMWF ensemble. The reason is that we do NOT want to write out the individual +# member tracks to the atcfunix file. We only want to write out the ensemble +# mean track to the atcfunix file, and the mean track is calculated and written +# out in a separate script. + + if [ ${cmodel} = 'gfdl' ] + then + auxfile=${COMOUT}/${stormenv}.${PDY}${cyc}.trackeratcfunix + else + auxfile=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + fi + + sort -k6 ${auxfile} | sort -k1 -k2 -k12 >atcfunix.sorted + old_string="XX, XX" + + ict=0 + while read unixrec + do + storm_string=` echo "${unixrec}" | cut -c1-6` + if [ "${storm_string}" = "${old_string}" ] + then + echo "${unixrec}" >>atcfunix_file.${ict} + else + let ict=ict+1 + echo "${unixrec}" >atcfunix_file.${ict} + old_string="${storm_string}" + fi + done >$COMOUTatcf/${at}${NO}${syyyy}/ncep_a${at}${NO}${syyyy}.dat + set +x + echo " " + echo "+++ Adding records to TPC ATCFUNIX directory: $COMOUTatcf/${at}${NO}${syyyy}/ncep_${at}${NO}${syyyy}" + echo " " + set -x + done + fi +fi diff --git a/sorc/ens_tracker.v1.1.15.2/ush/extrkr_gen_g1.sh b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_g1.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/extrkr_gen_g1.sh rename to sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_g1.sh diff --git a/sorc/ens_tracker.v1.1.15.2/ush/extrkr_gen_g2.sh b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_g2.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/extrkr_gen_g2.sh rename to sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_g2.sh diff --git a/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_g2.sh_10152019 b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_g2.sh_10152019 new file mode 100755 index 0000000000..84f0ebd6d5 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_g2.sh_10152019 @@ -0,0 +1,2683 @@ +#!/bin/ksh +export PS4=' + extrkr_gen_g2.sh line $LINENO: ' + +set +x +############################################################################## +echo " " +echo "------------------------------------------------" +echo "xxxx - Track vortices in model GRIB2 output" +echo "J.Peng---10-07-2014-----------------------------" +echo " " +echo "Current time is: `date`" +echo " " +############################################################################## +set -x + +loopnum=$1 +cmodel=$2 +pert=$3 +TRKDATA=$4 + +export gribver=2 +#init_flag=$5 +# USE_OPER_VITALS=NO +# USE_OPER_VITALS=INIT_ONLY +USE_OPER_VITALS=YES + +############################################################################## +# +# FLOW OF CONTROL +# +# 1. Define data directories and file names for the input model +# 2. Process input starting date/cycle information +# 3. Update TC Vitals file and select storms to be processed +# 4. Cut apart input GRIB files to select only the needed parms and hours +# 5. Execute the tracker +# 6. Copy the output track files to various locations +# +############################################################################## +msg="has begun for ${cmodel} ${pert} at ${cyc}z" +postmsg "$jlogfile" "$msg" +######################################## + +# This script runs the hurricane tracker using operational GRIB model output. +# This script makes sure that the data files exist, it then pulls all of the +# needed data records out of the various GRIB forecast files and puts them +# into one, consolidated GRIB file, and then runs a program that reads the TC +# Vitals records for the input day and updates the TC Vitals (if necessary). +# It then runs gettrk, which actually does the tracking. +# +# Environmental variable inputs needed for this scripts: +# PDY -- The date for data being processed, in YYYYMMDD format +# cyc -- The numbers for the cycle for data being processed (00, 06, 12, 18) +# cmodel -- Model being processed (gfs, mrf, ukmet, ecmwf, nam, ngm, ngps, +# gdas, gfdl, ens (ncep ensemble)) +# envir -- 'prod' or 'test' +# SENDCOM -- 'YES' or 'NO' +# stormenv -- This is only needed by the tracker run for the GFDL model. +# 'stormenv' contains the name/id that is used in the input +# grib file names. +# pert -- This is only needed by the tracker run for the NCEP ensemble. +# 'pert' contains the ensemble member id (e.g., n2, p4, etc.) +# which is used as part of the grib file names. +# +# For testing script interactively in non-production set following vars: +# archsyndir - Directory with syndir scripts/exec/fix +# + +qid=$$ +#----------------------------------------------# +# Get input date information # +#----------------------------------------------# + +#export PDY=${PDY:-$1} +#export cyc=${cyc:-$2} +#export cmodel=${cmodel:-$3} +export jobid=${jobid:-testjob} +export SENDCOM=${SENDCOM:-NO} +export PHASEFLAG=y +export WCORE_DEPTH=1.0 +#export PHASE_SCHEME=vtt +#export PHASE_SCHEME=cps +export PHASE_SCHEME=both +export STRUCTFLAG=n +export IKEFLAG=n + +if [ ! -d $TRKDATA ] +then + mkdir -p $TRKDATA +fi +cd $TRKDATA + +if [ ${#PDY} -eq 0 -o ${#cyc} -eq 0 -o ${#cmodel} -eq 0 ] +then + set +x + echo " " + echo "FATAL ERROR: Something wrong with input parameters." + echo "PDY= ${PDY}, cyc= ${cyc}, cmodel= ${cmodel}" + set -x + err_exit "BAD INPUTS AT LINE $LINENO IN extrkr_gen_g2.sh" +else + set +x + echo " " + echo " #-----------------------------------------------------------------#" + echo " At beginning of tracker script, the following imported variables " + echo " are defined: " + echo " PDY ................................... $PDY" + echo " cyc ................................... $cyc" + echo " cmodel ................................ $cmodel" + echo " jobid ................................. $jobid" + echo " envir ................................. $envir" + echo " SENDCOM ............................... $SENDCOM" + echo " " + set -x +fi + +scc=`echo ${PDY} | cut -c1-2` +syy=`echo ${PDY} | cut -c3-4` +smm=`echo ${PDY} | cut -c5-6` +sdd=`echo ${PDY} | cut -c7-8` +shh=${cyc} +symd=`echo ${PDY} | cut -c3-8` +syyyy=`echo ${PDY} | cut -c1-4` +CENT=`echo ${PDY} | cut -c1-2` + +#export archsyndir=${archsyndir:-${COMROOT}/arch/prod/syndat} +archsyndir=${archsyndir:-${COMINsyn:?}} + +#----------------------------------------------------------------# +# +# --- Define data directories and data file names --- +# +# Convert the input model to lowercase letters and check to see +# if it's a valid model, and assign a model ID number to it. +# This model ID number is passed into the Fortran program to +# let the program know what set of forecast hours to use in the +# ifhours array. Also, set the directories for the operational +# input GRIB data files and create templates for the file names. +# While only 1 of these sets of directories and file name +# templates is used during a particular run of this script, +# "gfsvitdir" is used every time, because that is the directory +# that contains the error-checked TC vitals file that Steve Lord +# produces, and so it is included after the case statement. +# +# NOTE: The varible PDY is now defined within the J-Jobs that +# call this script. Therefore there is no reason to do this +# here. +# +# NOTE: The script that processes the ECMWF data defines PDY as +# the current day, and in this script we need PDY to be +# yesterday's date (for the ecmwf ONLY). So instead, the ecmwf +# script will pass the variable PDYm1 to this script, and in the +# case statement below we change that to PDY. +# +# NOTE: Do NOT try to standardize this script by changing all of +# these various data directories' variable names to be all the +# same, such as "datadir". As you'll see in the data cutting +# part of this script below, different methods are used to cut +# apart different models, thus it is important to know the +# difference between them.... +#----------------------------------------------------------------# + +cmodel=`echo ${cmodel} | tr "[A-Z]" "[a-z]"` + +case ${cmodel} in + + gfs) set +x; echo " " ; + echo " ++ operational GFS chosen" ; + echo " "; set -x ; +# gfsdir=${gfsdir:-${COMROOTp1}/gfs/prod/gfs.${PDY}} ; + gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}} ; + gfsgfile=gfs.t${cyc}z.pgrb2.0p25.f ; + vit_incr=6 + + fcstlen=120 ; + fcsthrs=$(seq -w -s' ' 0 $vit_incr $fcstlen) ; + atcfnum=15 ; + atcfname="gfso" ; + atcfout="gfso" ; + atcffreq=600 ; + + rundescr="xxxx" ; + atcfdescr="xxxx" ; + + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + modtyp='global' ; + + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=0 ; + model=1 ;; + +# g2_jpdtn sets the variable that will be used as "JPDTN" for +# the call to getgb2, if gribver=2. jpdtn=1 for ens data, +# jpdtn=0 for deterministic data. + + ngps) set +x; echo " " ; + echo " ++ operational NAVGEM chosen" ; + echo " "; set -x ; +# ngpsdir=${ngpsdir:-${COMROOTp1}/fnmoc/prod/navgem.${PDY}}; + ngpsdir=${ngpsdir:-${DCOM:?}} ; +#J.Peng-05-12-2016 ngpsgfile=navgem_${PDY}${cyc}f ; +# ngemgfile=.grib2 ; + ngpsgfile=US058GMET-OPSbd2.NAVGEM ; + ngemgfile=-${PDY}${cyc}-NOAA-halfdeg.gr2 ; + + vit_incr=6 + fcstlen=120 ; +#J.Peng-05-12-2016 fcsthrs=' 000 006 012 018 024 030 036 042 048 054 +# 060 066 072 084 096 108 120' ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + atcfnum=29 ; + atcfname="ngx " ; + atcfout="ngx" ; + atcffreq=600 ; + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + modtyp='global' ; + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=0 ; + model=7 ;; + + cmc) set +x; echo " " ; + echo " ++ operational CMC chosen" ; + echo " "; set -x ; + cmcdir=${cmcdir:-${DCOM:?}} ; +# cmcdir=/gpfs/hps/emc/ensemble/noscrub/Jiayi.Peng/cmc_25km_data ; + cmcgfile=CMC_glb_latlon.24x.24_${PDY}${cyc}_P ; + cmcgfile2=_NCEP.grib2 ; + + vit_incr=6 ; + fcstlen=120 ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + atcfnum=39 ; + atcfname="cmc " ; + atcfout="cmc" ; + atcffreq=600 ; + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + modtyp='global' ; + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=0 ; + model=15 ;; + + ens) set +x; echo " " ; + echo " ++ operational ensemble member ${pert} chosen"; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; +# ensdira=${ensdira:-${COMROOT}/gens/prod/gefs.${PDY}/$cyc/pgrb2ap5}; + ensdira=${ensdira:-${COMIN:?}/pgrb2ap5} ; + ensgfile=ge${pert}.t${cyc}z.pgrb2a.0p50.f ; + +# ensdirb=${ensdirb:-${COMROOT}/gens/prod/gefs.${PDY}/$cyc/pgrb2bp5}; + ensdirb=${ensdirb:-${COMIN:?}/pgrb2bp5} ; + ensgfileb=ge${pert}.t${cyc}z.pgrb2b.0p50.f ; + + vit_incr=6 + fcstlen=120 ; + fcsthrs=$(seq -w -s' ' 0 $vit_incr $fcstlen) ; + + atcfnum=91 ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + atcfname="a${pert_posneg}${pert_num}" ; + atcfout="a${pert_posneg}${pert_num}" ; + + atcffreq=600 ; + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=1 ; + modtyp='global' ; + model=10 ;; + +# g2_jpdtn sets the variable that will be used as "JPDTN" for +# the call to getgb2, if gribver=2. jpdtn=1 for ens data, +# jpdtn=0 for deterministic data. + + fens) set +x; echo " " ; + echo " ++ FNMOC ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; +# fensdir=${fensdir:-${DCOMROOT}/us007003/${PDY}/wgrbbul/fnmocens_gb2}; + fensdir=${fensdir:-${DCOM:?}} ; + + pert_num=` echo "${pert}" | cut -c2-3` ; + fensgfile=ENSEMBLE.MET.fcst_et0${pert_num}. ; + gensgfile=.${PDY}${cyc} ; + vit_incr=6 + fcstlen=120 ; + fcsthrs=$(seq -w -s' ' 0 $vit_incr $fcstlen) ; + + atcfnum=91 ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; +# pert_num=` echo "${pert}" | cut -c2-3` ; +# atcfname="f${pert_posneg}${pert_num}" ; +# atcfout="f${pert_posneg}${pert_num}" ; + atcfname="n${pert_posneg}${pert_num}" ; + atcfout="n${pert_posneg}${pert_num}" ; + + atcffreq=600 ; + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=1 ; + modtyp='global' ; + model=22 ;; + + cens) set +x; echo " " ; + echo " ++ Canadian ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; +# cceifile=does_not_exist ; +# ccedir=/dcom/us007003/${PDY}/wgrbbul/cmcens_gb2 ; +# ccegfile=${PDY}${cyc}_CMC_naefs_latlon1p0x1p0_P ; +# pert_num=` echo "${pert}" | cut -c2-3` ; +# dcegfile=_0${pert_num}.grib2 ; + +# ccedir=${ccedir:-${COMROOT}/gens/prod/cmce.${PDY}/${cyc}/pgrb2a}; +# ccedir=${ccedir:-${COMIN:?}/pgrb2a} ; +# ccegfile=cmc_ge${pert}.t${cyc}z.pgrb2af ; +# 2016121200_CMC_naefs_hr_latlon0p5x0p5_P210_010.grib2 ; +# ccedir=${ccedir:-/dcom/us007003/${PDY}/wgrbbul/cmcens_gb2} ; + pert_num=` echo "${pert}" | cut -c2-3` ; + ccegfile=${PDY}${cyc}_CMC_naefs_hr_latlon0p5x0p5_P ; + ccegfile2=_0${pert_num}.grib2 ; + + vit_incr=6 ; + fcstlen=120 ; +# fcsthrs=$(seq -f%02g -s' ' 0 $vit_incr $fcstlen) ; + fcsthrs=$(seq -w -s' ' 0 $vit_incr $fcstlen) ; + + atcfnum=91 ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + atcfname="c${pert_posneg}${pert_num}" ; + atcfout="c${pert_posneg}${pert_num}" ; + + atcffreq=600 ; + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=1 ; + modtyp='global' ; + model=16 ;; + +# g2_jpdtn sets the variable that will be used as "JPDTN" for +# the call to getgb2, if gribver=2. jpdtn=1 for ens data, +# jpdtn=0 for deterministic data. + + *) msg="FATAL ERROR: Model $cmodel is not recognized." ; + echo "$msg"; postmsg $jlogfile "$msg" ; + err_exit "UNKNOWN cmodel IN extrkr_gen_g2.sh";; + +esac + +if [ ${PHASEFLAG} = 'y' ]; then + +#J.Peng----2014-10-28--------- + if [ ${cmodel} = "gfs" ]; then + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 UGRD:200 VGRD:850 VGRD:700 VGRD:500 VGRD:200 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET HGT:900 HGT:850 HGT:800 HGT:750 HGT:700 HGT:650 HGT:600 HGT:550 HGT:500 HGT:450 HGT:400 HGT:350 HGT:300 TMP:500 TMP:450 TMP:400 TMP:350 TMP:300 RH:500" + + elif [ ${cmodel} = "ngps" ]; then + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 UGRD:200 VGRD:850 VGRD:700 VGRD:500 VGRD:200 SurfaceU SurfaceV PRMSL HGT:925 HGT:850 HGT:700 HGT:500 HGT:400 HGT:300 TMP:500 TMP:400 TMP:300 RH:500" + + elif [ ${cmodel} = "ens" ]; then +# wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 UGRD:200 VGRD:850 VGRD:700 VGRD:500 VGRD:200 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET HGT:925 HGT:850 HGT:700 HGT:500 HGT:400 HGT:300 TMP:500 TMP:400 TMP:300 RH:500" + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 UGRD:200 VGRD:850 VGRD:700 VGRD:500 VGRD:200 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET HGT:900 HGT:850 HGT:800 HGT:750 HGT:700 HGT:650 HGT:600 HGT:550 HGT:500 HGT:450 HGT:400 HGT:350 HGT:300 TMP:500 TMP:450 TMP:400 TMP:350 TMP:300 RH:500" + + elif [ ${cmodel} = "cens" ]; then + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 UGRD:200 VGRD:850 VGRD:700 VGRD:500 VGRD:200 SurfaceU SurfaceV PRMSL HGT:925 HGT:850 HGT:700 HGT:500 HGT:250 TMP:500 TMP:250 RH:500" + + elif [ ${cmodel} = "cmc" ]; then + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 UGRD:200 VGRD:850 VGRD:700 VGRD:500 VGRD:200 SurfaceU SurfaceV ABSV:850 ABSV:700 PRMSL HGT:925 HGT:850 HGT:700 HGT:500 HGT:250 TMP:500 TMP:250 SPFH:500" + + elif [ ${cmodel} = "fens" ]; then + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 UGRD:200 VGRD:850 VGRD:700 VGRD:500 VGRD:200 SurfaceU SurfaceV PRMSL HGT:925 HGT:850 HGT:700 HGT:500 HGT:250 TMP:500 TMP:250 RH:500" + + fi + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 U:200 V:850 V:700 V:500 V:200 10U:sfc 10V:sfc MSL:sfc GH:300 GH:400 GH:500 GH:925 T:300 T:400 T:500 R:500" + +else + wgrib_parmlist=" HGT:850 HGT:700 UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET " + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc" +fi + +#---------------------------------------------------------------# +# +# -------- TC Vitals processing -------- +# +# Check Steve Lord's operational tcvitals file to see if any +# vitals records were processed for this time by his system. +# If there were, then you'll find a file in /com/gfs/prod/gfs.yymmdd +# with the vitals in it. Also check the raw TC Vitals file in +# /com/arch/prod/syndat , since this may contain storms that Steve's +# system ignored (Steve's system will ignore all storms that are +# either over land or very close to land); We still want to track +# these inland storms, AS LONG AS THEY ARE NHC STORMS (don't +# bother trying to track inland storms that are outside of NHC's +# domain of responsibility -- we don't need that info). +# UPDATE 5/12/98 MARCHOK: The script is updated so that for the +# ensemble models, the gfs directory is checked for the error- +# checked vitals file, while for the regional models, the +# nam directory is checked for that file. +# UPDATE 3/27/09 MARCHOK: The SREF is run at off-synoptic times +# (03,09,15,21Z). There are no tcvitals issued at these offtimes, +# so the updating of the "old" tcvitals is critical for running +# the tracker on SREF. For updating the old tcvitals for SREF, +# we need to look 3h back, not 6h as for the other models that +# run at synoptic times. +#--------------------------------------------------------------# + +old_ymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +old_4ymd=` echo ${old_ymdh} | cut -c1-8` +old_ymd=` echo ${old_ymdh} | cut -c3-8` +old_hh=` echo ${old_ymdh} | cut -c9-10` +old_str="${old_ymd} ${old_hh}00" + +future_ymdh=` ${NDATE:?} ${vit_incr} ${PDY}${cyc}` +future_4ymd=` echo ${future_ymdh} | cut -c1-8` +future_ymd=` echo ${future_ymdh} | cut -c3-8` +future_hh=` echo ${future_ymdh} | cut -c9-10` +future_str="${future_ymd} ${future_hh}00" + +if [ ${modtyp} = 'global' ] +then +# synvitdir=${COMROOT}/gfs/prod/gfs.${PDY} + synvitdir=${COMINgfs:?}/${cyc} + synvitfile=gfs.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/gfs/prod/gfs.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd}/${old_hh} + synvitold_file=gfs.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/gfs/prod/gfs.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd}/${future_hh} + synvitfuture_file=gfs.t${future_hh}z.syndata.tcvitals.tm00 +else +# synvitdir=${COMROOT}/nam/prod/nam.${PDY} + synvitdir=${COMINnam:?} + synvitfile=nam.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/nam/prod/nam.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=nam.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/nam/prod/nam.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=nam.t${future_hh}z.syndata.tcvitals.tm00 +fi + +set +x +echo " " +echo " -----------------------------" +echo " " +echo " Now sorting and updating the TC Vitals file. Please wait...." +echo " " +set -x + +current_str="${symd} ${cyc}00" + +if [ -s ${synvitdir}/${synvitfile} -o\ + -s ${synvitold_dir}/${synvitold_file} -o\ + -s ${synvitfuture_dir}/${synvitfuture_file} ] +then + grep "${old_str}" ${synvitold_dir}/${synvitold_file} \ + >${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${synvitdir}/${synvitfile} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${future_str}" ${synvitfuture_dir}/${synvitfuture_file} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} +else + set +x + echo " " + echo " There is no (synthetic) TC vitals file for ${cyc}z in ${synvitdir}," + echo " nor is there a TC vitals file for ${old_hh}z in ${synvitold_dir}." + echo " nor is there a TC vitals file for ${future_hh}z in ${synvitfuture_dir}," + echo " Checking the raw TC Vitals file ....." + echo " " + set -x +fi + +# Take the vitals from Steve Lord's /com/gfs/prod tcvitals file, +# and cat them with the NHC-only vitals from the raw, original +# /com/arch/prod/synda_tcvitals file. Do this because the nwprod +# tcvitals file is the original tcvitals file, and Steve runs a +# program that ignores the vitals for a storm that's over land or +# even just too close to land, and for tracking purposes for the +# US regional models, we need these locations. Only include these +# "inland" storm vitals for NHC (we're not going to track inland +# storms that are outside of NHC's domain of responsibility -- we +# don't need that info). +# UPDATE 5/12/98 MARCHOK: awk logic is added to screen NHC +# vitals such as "91L NAMELESS" or "89E NAMELESS", since TPC +# does not want tracks for such storms. + +grep "${old_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${current_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${future_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-03---for genesis tc vitals-------------------- +#export COMINgenvit=/ensemble/save/Jiayi.Peng/genesis_vital_${syyyy} +#grep "${old_str}" ${COMINgenvit}/gen_tc.vitals.gfs.gfso.2012 | \ +# grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ +# >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +if [ ${cmodel} = 'ens' -o ${cmodel} = 'gfs' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.gfs.gfso.${syyyy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +elif [ ${cmodel} = 'cens' -o ${cmodel} = 'cmc' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.cmc.cmc.${syyyy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +elif [ ${cmodel} = 'fens' -o ${cmodel} = 'ngps' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.ngps.ngx.${syyyy} | \ +grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +elif [ ${cmodel} = 'eens' -o ${cmodel} = 'ecmwf' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.ecmwf.emx.${syyyy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +fi + +#PRODTEST# Use the next couple lines to test the tracker on the SP. +#PRODTEST# These next couple lines use data from a test TC Vitals file that +#PRODTEST# I generate. When you are ready to test this system, call me and +#PRODTEST# I'll create one for the current day, and then uncomment the next +#PRODTEST# couple lines in order to access the test vitals file. +# +#ttrkdir=/ensemble/save/wx20tm/trak/prod/data +#ttrkdir=/ensemble/save/wx20tm/trak/para/scripts +#grep "${current_str}" ${ttrkdir}/tcvit.01l >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + + +# IMPORTANT: When "cat-ing" these files, make sure that the vitals +# files from the "raw" TC vitals files are first in order and Steve's +# TC vitals files second. This is because Steve's vitals file has +# been error-checked, so if we have a duplicate tc vitals record in +# these 2 files (very likely), program supvit.x below will +# only take the last vitals record listed for a particular storm in +# the vitals file (all previous duplicates are ignored, and Steve's +# error-checked vitals records are kept). + +cat ${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} ${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} \ + >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# If we are doing the processing for the GFDL model, then we want +# to further cut down on which vitals we allow into this run of the +# tracker. The reason is that this program will be called from +# each individual run for a storm, so the grib files will be +# specific to each storm. So if 4 storms are being run at a +# particular cycle, then this script is run 4 separate times from +# within the GFDL_POST job. + +#if [ ${cmodel} = 'gfdl' ]; then +## grep -i ${stormid} ${COMIN}/${ATCFNAME}.vitals.${syy}${smm}${sdd}${shh} >${TRKDATA}/tmpvit +## mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#echo " " +#echo "!!! ERROR: TAKE THE COMMENTS OUT OF HERE BEFORE IMPLEMENTATION AAAA" +#echo "!!! AND TAKE OUT THESE NEXT 2 LINES AS WELL............................" +#echo " " +#grep -i ${stormid} ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/tmpvit +#mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#fi + +#--------------------------------------------------------------# +# Now run a fortran program that will read all the TC vitals +# records for the current dtg and the dtg from 6h ago, and +# sort out any duplicates. If the program finds a storm that +# was included in the vitals file 6h ago but not for the current +# dtg, this program updates the 6h-old first guess position +# and puts these updated records as well as the records from +# the current dtg into a temporary vitals file. It is this +# temporary vitals file that is then used as the input for the +# tracking program. +#--------------------------------------------------------------# + +oldymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +oldyy=`echo ${oldymdh} | cut -c3-4` +oldmm=`echo ${oldymdh} | cut -c5-6` +olddd=`echo ${oldymdh} | cut -c7-8` +oldhh=`echo ${oldymdh} | cut -c9-10` +oldymd=${oldyy}${oldmm}${olddd} + +futureymdh=` ${NDATE:?} 6 ${PDY}${cyc}` +futureyy=`echo ${futureymdh} | cut -c3-4` +futuremm=`echo ${futureymdh} | cut -c5-6` +futuredd=`echo ${futureymdh} | cut -c7-8` +futurehh=`echo ${futureymdh} | cut -c9-10` +futureymd=${futureyy}${futuremm}${futuredd} + +echo "&datenowin dnow%yy=${syy}, dnow%mm=${smm}," >${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&dateoldin dold%yy=${oldyy}, dold%mm=${oldmm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dold%dd=${olddd}, dold%hh=${oldhh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&datefuturein dfuture%yy=${futureyy}, dfuture%mm=${futuremm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dfuture%dd=${futuredd}, dfuture%hh=${futurehh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&hourinfo vit_hr_incr=${vit_incr}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period." + echo "!!! File ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} is empty." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi + +fi + +# For tcgen cases, filter to use only vitals from the ocean +# basin of interest.... +#J.Peng----2012-04-13------------------------------------- +#if [ ${trkrtype} = 'tcgen' ] +# then + +# if [ ${numvitrecs} -gt 0 ] +# then + +# fullvitfile=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# cp $fullvitfile ${TRKDATA}/vitals.all_basins.${atcfout}.${PDY}${cyc} +# basin=` echo $regtype | cut -c1-2` + +# if [ ${basin} = 'al' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "L") print $0}' | +# >${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'ep' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "E") print $0}' | +# >${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'wp' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "W") print $0}' | +# >${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi + +# cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# fi + +#fi +#J.Peng----2012-04-13------------------------------------- + +# - - - - - - - - - - - - - +# Before running the program to read, sort and update the vitals, +# first run the vitals through some awk logic, the purpose of +# which is to convert all the 2-digit years into 4-digit years. +# Beginning 4/21/99, NHC and JTWC will begin sending the vitals +# with 4-digit years, however it is unknown when other ensemble +# forecasting centers will begin using 4-digit years, thus we +# need the following logic to ensure that all the vitals going +# into supvit.f have uniform, 4-digit years in their records. +# +# 1/8/2000: sed code added by Tim Marchok due to the fact that +# some of the vitals were getting past the syndata/qctropcy +# error-checking with a colon in them; the colon appeared +# in the character immediately to the left of the date, which +# was messing up the "(length($4) == 8)" statement logic. +# - - - - - - - - - - - - - + +sed -e "s/\:/ /g" ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} > ${TRKDATA}/tempvit +mv ${TRKDATA}/tempvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +awk ' +{ + yycheck = substr($0,20,2) + if ((yycheck == 20 || yycheck == 19) && (length($4) == 8)) { + printf ("%s\n",$0) + } + else { + if (yycheck >= 0 && yycheck <= 50) { + printf ("%s20%s\n",substr($0,1,19),substr($0,20)) + } + else { + printf ("%s19%s\n",substr($0,1,19),substr($0,20)) + } + } +} ' ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 + +mv ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +#Added by J.Peng----2012-04-12---------------------------------------------- +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +#----------------------------------------------------------------- +if [ ${numvitrecs} -gt 0 ] +then + export pgm=supvit_g2 + . prep_step + + # Input file + export FORT31=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_g2 <${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + suvrcc=$? + + if [ ${suvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit.x, " + echo "!!! which is the program that updates the TC Vitals file." + echo "!!! Return code from supvit.x = ${suvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "ERROR RUNNING supvit_g2 IN extrkr_gen_g2 LINE $LINENO" + fi + +else +#Added by J.Peng----2012-04-12---------------------------------------------- + rm -f ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + touch ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#----------------------------------------------------------------- +# In this section, check to see if the user requested the use of +# operational TC vitals records for the initial time only. This +# option might be used for a retrospective medium range forecast +# in which the user wants to initialize with the storms that are +# currently there, but then let the model do its own thing for +# the next 10 or 14 days.... + + +if [ ${USE_OPER_VITALS} = 'INIT_ONLY' ]; then + + if [ ${init_flag} = 'yes' ]; then + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are currently at." + echo " " + set -x + else + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are now *PAST*." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + fi + +elif [ ${USE_OPER_VITALS} = 'NO' ]; then + + set +x + echo " " + echo "NOTE: User has requested that historical vitals not be used...." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#------------------------------------------------------------------# +# Now select all storms to be processed, that is, process every +# storm that's listed in the updated vitals file for the current +# forecast hour. If there are no storms for the current time, +# then exit. +#------------------------------------------------------------------# + +numvitrecs=`cat ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period " + echo "!!! in the UPDATED vitals file." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi +fi + +set +x +echo " " +echo " *--------------------------------*" +echo " | STORM SELECTION |" +echo " *--------------------------------*" +echo " " +set -x + +ict=1 +while [ $ict -le 15 ] +do + stormflag[${ict}]=3 + let ict=ict+1 +done + +dtg_current="${symd} ${cyc}00" +stormmax=` grep "${dtg_current}" ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +if [ ${stormmax} -gt 15 ] +then + stormmax=15 +fi + +sct=1 +while [ ${sct} -le ${stormmax} ] +do + stormflag[${sct}]=1 + let sct=sct+1 +done + + +#---------------------------------------------------------------# +# +# -------- "Genesis" Vitals processing -------- +# +# May 2006: This entire genesis tracking system is being +# upgraded to more comprehensively track and categorize storms. +# One thing that has been missing from the tracking system is +# the ability to keep track of storms from one analysis cycle +# to the next. That is, the current system has been very +# effective at tracking systems within a forecast, but we have +# no methods in place for keeping track of storms across +# difference initial times. For example, if we are running +# the tracker on today's 00z GFS analysis, we will get a +# position for various storms at the analysis time. But then +# if we go ahead and run again at 06z, we have no way of +# telling the tracker that we know about the 00z position of +# this storm. We now address that problem by creating +# "genesis" vitals, that is, when a storm is found at an +# analysis time, we not only produce "atcfunix" output to +# detail the track & intensity of a found storm, but we also +# produce a vitals record that will be used for the next +# run of the tracker script. These "genesis vitals" records +# will be of the format: +# +# YYYYMMDDHH_AAAH_LLLLX_TYP +# +# Where: +# +# YYYYMMDDHH = Date the storm was FIRST identified +# by the tracker. +# AAA = Abs(Latitude) * 10; integer value +# H = 'N' for norther hem, 'S' for southern hem +# LLLL = Abs(Longitude) * 10; integer value +# X = 'E' for eastern hem, 'W' for western hem +# TYP = Tropical cyclone storm id if this is a +# tropical cyclone (e.g., "12L", or "09W", etc). +# If this is one that the tracker instead "Found +# On the Fly (FOF)", we simply put those three +# "FOF" characters in there. +#J.Peng----2012-04-12-------- +genvitfile=${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} +touch ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + +if [ -f $genvitfile ]; then + d6ago_ymdh=` ${NDATE:?} -6 ${PDY}${cyc}` + d6ago_4ymd=` echo ${d6ago_ymdh} | cut -c1-8` + d6ago_ymd=` echo ${d6ago_ymdh} | cut -c3-8` + d6ago_hh=` echo ${d6ago_ymdh} | cut -c9-10` + d6ago_str="${d6ago_ymd} ${d6ago_hh}00" + + d6ahead_ymdh=` ${NDATE:?} 6 ${PDY}${cyc}` + d6ahead_4ymd=` echo ${d6ahead_ymdh} | cut -c1-8` + d6ahead_ymd=` echo ${d6ahead_ymdh} | cut -c3-8` + d6ahead_hh=` echo ${d6ahead_ymdh} | cut -c9-10` + d6ahead_str="${d6ahead_ymd} ${d6ahead_hh}00" + + set +x + echo " " + echo " d6ago_str= --->${d6ago_str}<---" + echo " current_str= --->${current_str}<---" + echo " d6ahead_str= --->${d6ahead_str}<---" + echo " " + #echo " Listing and contents of ${genvitfile} follow " + #echo " for the times 6h ago, current and 6h ahead:" + #echo " " + set -x + + #J.Peng----2012-04-11----- + ##ls -la ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + ##cat ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + #ls -la ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #cat ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + + grep "${d6ago_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${d6ahead_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + num_gen_vits=`cat ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} | wc -l` +else + echo "WARNING: Genesis Vitals from previous run(s) not found!" +fi + +echo "&datenowin dnow%yy=${syyyy}, dnow%mm=${smm}," >${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6agoin d6ago%yy=${syyyy6}, d6ago%mm=${smm6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ago%dd=${sdd6}, d6ago%hh=${shh6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6aheadin d6ahead%yy=${syyyyp6}, d6ahead%mm=${smmp6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ahead%dd=${sddp6}, d6ahead%hh=${shhp6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-04---do not need calculate this ---------- +num_gen_vits=0 + +if [ ${num_gen_vits} -gt 0 ] +then + export pgm=supvit_gen + . prep_step + + # Input file + export FORT31=${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_gen <${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + sgvrcc=$? + + if [ ${sgvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit_gen, " + echo "!!! which is the program that updates the genesis vitals file." + echo "!!! Return code from supvit_gen = ${sgvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "ERROR RUNNING SUPVIT_GEN IN extrkr_gen_g2.sh LINE $LINENO" + fi + +else + rm -f ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + touch ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +fi + + +#-----------------------------------------------------------------# +# +# ------ CUT APART INPUT GRIB FILES ------- +# +# For the selected model, cut apart the GRIB input files in order +# to pull out only the variables that we need for the tracker. +# Put these selected variables from all forecast hours into 1 big +# GRIB file that we'll use as input for the tracker. +# +# The wgrib utility (/nwprod/util/exec/wgrib) is used to cut out +# the needed parms for the GFS, MRF, GDAS, UKMET and NOGAPS files. +# The utility /nwprod/util/exec/copygb is used to interpolate the +# NGM (polar stereographic) and NAM (Lambert Conformal) data from +# their grids onto lat/lon grids. Note that while the lat/lon +# grid that I specify overlaps into areas that don't have any data +# on the original grid, Mark Iredell wrote the copygb software so +# that it will mask such "no-data" points with a bitmap (just be +# sure to check the lbms in your fortran program after getgb). +#-----------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------" +echo " NOW CUTTING APART INPUT GRIB FILES TO " +echo " CREATE 1 BIG GRIB INPUT FILE " +echo " -----------------------------------------" +echo " " +set -x + +regflag=`grep NHC ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +# ------------------------------ +# Process FNMOC Ensemble, if selected +# ------------------------------ + +if [ ${model} -eq 22 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ] + then + rm ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + fensfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + + let attempts=1 + while [ $attempts -le 40 ]; do + if [ -s $fensfile ]; then + break + else + sleep 60 + let attempts=attempts+1 + fi + done + if [ $attempts -gt 40 ] && [ ! -s $fensfile ]; then + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "$fensfile" >> ${DATA}/missing_fens.txt + exit + else + err_exit "$fensfile still not available after waiting 40 minutes" + fi + fi + + export pgm=wgrib2 + #${WGRIB2:?} -set_byte 1 11 1 -s $fensfile >fens.ix # set_byte resets local table to 1 to remove error messages + ${WGRIB2:?} -s $fensfile >fens.ix + if [ $err -ne 0 ]; then + err_exit "Error reading $fensfile" + fi + + for parm in ${wgrib_parmlist} + do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m " fens.ix | ${WGRIB2:?} -i $fensfile -append -grib \ + ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ;; + "SurfaceV") + grep "VGRD:10 m " fens.ix | ${WGRIB2:?} -i $fensfile -append -grib \ + ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ;; + *) + grep "${parm}" fens.ix | ${WGRIB2:?} -i $fensfile -append -grib \ + ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ;; + esac + done + unset pgm + done + + ${GRB2INDEX:?} ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} +# rm $tavefile $zfile + +#J.Peng----2014-10-15---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_g2.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2014-10-15---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ixfile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + +fi + +# ------------------------------ +# Process GFS, if selected +# ------------------------------ + +if [ ${model} -eq 1 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gfsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc} + fi + + rm ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f* + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gfsgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gfs wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gfsdir}/${gfsgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GFS File missing: ${gfsdir}/${gfsgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GFS FILE IN extrkr_gen_g2.sh: ${gfsdir}/${gfsgfile}${fhour}" + fi + + gfile=${gfsdir}/${gfsgfile}${fhour} + ${WGRIB2:?} -s $gfile >gfs.ix + export err=$?; err_chk + + for parm in ${wgrib_parmlist} + do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m " gfs.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} ;; + "SurfaceV") + grep "VGRD:10 m " gfs.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} ;; + *) + grep "${parm}" gfs.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} ;; + esac + done + + gfs_master_file=${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + gfs_cat_file=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfs_master_file} ${gfs_master_file}.ix + export err=$?; err_chk + + g1=${gfs_master_file} + x1=${gfs_master_file}.ix + cat ${gfs_master_file} >>${gfs_cat_file} + done + + ${GRB2INDEX:?} ${TRKDATA}/gfsgribfile.${PDY}${cyc} ${TRKDATA}/gfsixfile.${PDY}${cyc} + export err=$?; err_chk +# Now run through the work to get data for phase checking.... +# -------------------------------------------- + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk +# This next part is commented out for the GFS since we already +# have the necessary GFS height and temperature data at +# 50-mb intervals. However, we then still need to average the +# temperature data to get the 300-500 mb mean temperature, and +# that code is still un-commented below. +# gparm=7 +# namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z +# . prep_step +# echo "&timein ifcsthour=${fhour}," >${namelist} +# echo " iparm=${gparm}," >>${namelist} +# echo " gribver=${gribver}," >>${namelist} +# echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} +# echo " g2_model=${model}/" >>${namelist} +# ln -s -f ${gfile} fort.11 +# ln -s -f ${FIXens_tracker}/gfs_hgt_levs.txt fort.16 +# ln -s -f ${ifile} fort.31 +# ln -s -f ${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} fort.51 +# ${EXECens_tracker}/vint_g2.x <${namelist} +# rcc=$? + +# gparm=11 +# namelist=${TRKDATA}/vint_input.${PDY}${cyc}.t +# . prep_step +# echo "&timein ifcsthour=${fhour}," >${namelist} +# echo " iparm=${gparm}," >>${namelist} +# echo " gribver=${gribver}," >>${namelist} +# echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} +# echo " g2_model=${model}/" >>${namelist} +# ln -s -f ${gfile} fort.11 +# ln -s -f ${FIXens_tracker}/gfs_tmp_levs.txt fort.16 +# ln -s -f ${ifile} fort.31 +# ln -s -f ${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} fort.51 +# ${EXECens_tracker}/vint_g2.x <${namelist} +# rcc=$? + + gparm=11 +# ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ffile=${gfile} + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} +# export FORT92=${TRKDATA}/${cmodel}.tave92.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} +# zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} +# cat ${zfile} ${tavefile} >>${catfile} + cat ${tavefile} >>${catfile} + +#J.Peng----2012-04-18---added for U-shear----- + gparm=33 + ffile=${gfile} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_g2.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-04-18---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + + cat ${catfile} >>${gfile} + + fi + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gfsixfile.${PDY}${cyc} + +fi + +# -------------------------------------------------- +# Process NCEP Ensemble perturbation, if selected +# -------------------------------------------------- + +if [ ${model} -eq 10 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ensdira}/${ensgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GEFS ${PERT} File missing: ${ensdira}/${ensgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GEFS FILE IN TRACKER SCRIPT: ${ensdira}/${ensgfile}${fhour}" + fi + +# gfile=${ensdira}/${ensgfile}${fhour} + gfile=${TRKDATA}/${ensgfile}${fhour} + cat ${ensdira}/${ensgfile}${fhour} ${ensdirb}/${ensgfileb}${fhour} > $gfile + + ${WGRIB2:?} -s $gfile >ens.ix + export err=$?; err_chk + + for parm in ${wgrib_parmlist} + do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m " ens.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ;; + "SurfaceV") + grep "VGRD:10 m " ens.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ;; + *) + grep "${parm}" ens.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ;; + esac + done + done + + ${GRB2INDEX:?} ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/ens${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for pert= $pert and fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# gparm=7 +# namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z +# . prep_step +# echo "&timein ifcsthour=${fhour}," >${namelist} +# echo " iparm=${gparm}," >>${namelist} +# echo " gribver=${gribver}," >>${namelist} +# echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} +# echo " g2_model=${model}/" >>${namelist} +# ln -s -f ${gfile} fort.11 +# ln -s -f ${FIXens_tracker}/nce_hgt_levs.txt fort.16 +# ln -s -f ${ifile} fort.31 +# ln -s -f ${TRKDATA}/${cmodel}_${pert}.${PDY}${cyc}.z.f${fhour} fort.51 +# ${EXECens_tracker}/vint_g2.x <${namelist} +# rcc=$? + +# gparm=11 +# namelist=${TRKDATA}/vint_input.${PDY}${cyc}.t +# . prep_step +# echo "&timein ifcsthour=${fhour}," >${namelist} +# echo " iparm=${gparm}," >>${namelist} +# echo " gribver=${gribver}," >>${namelist} +# echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} +# echo " g2_model=${model}/" >>${namelist} +# ln -s -f ${gfile} fort.11 +# ln -s -f ${FIXens_tracker}/nce_tmp_levs.txt fort.16 +# ln -s -f ${ifile} fort.31 +# ln -s -f ${TRKDATA}/${cmodel}_${pert}.${PDY}${cyc}.t.f${fhour} fort.51 +# ${EXECens_tracker}/vint_g2.x <${namelist} +# rcc=$? + + gparm=11 +# ffile=${TRKDATA}/${cmodel}_${pert}.${PDY}${cyc}.t.f${fhour} + ffile=${gfile} +# ifile=${TRKDATA}/${cmodel}_${pert}.${PDY}${cyc}.t.f${fhour}.i + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} +# ${GRB2INDEX:?} ${ffile} ${ifile} +# export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} +# zfile=${TRKDATA}/${cmodel}_${pert}.${PDY}${cyc}.z.f${fhour} +# cat ${zfile} ${tavefile} >>${catfile} + cat ${tavefile} >>${catfile} +# rm $tavefile $zfile + +#J.Peng----2014-10-15---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_g2.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2014-10-15---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for pert= $pert and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + + gfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------ +# Process NAVGEM, if selected +# ------------------------------ + +if [ ${model} -eq 7 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} ] + then + set +x + echo " " + echo "FATAL ERROR: NAVGEM File missing ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" >> ${DATA}/missing_ngps.txt + exit + else + err_exit "MISSING NAVGEM FILE at $0:$LINENO" + fi + fi + + gfile=${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} + ${WGRIB2:?} -s $gfile >ngps.ix + export err=$?; err_chk + + for parm in ${wgrib_parmlist} + do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m above" ngps.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ;; + + "SurfaceV") + grep "VGRD:10 m above" ngps.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ;; + *) + grep "${parm}" ngps.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ;; + esac + done + done + +# Now run through the work to get data for phase checking.... + ${GRB2INDEX:?} ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ${TRKDATA}/ngpsixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_g2.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk + +fi + +# ------------------------------ +# Process CMC deterministic , if selected +# ------------------------------ + +if [ ${model} -eq 15 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/cmcgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/cmcgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} ] + then + set +x + echo " " + echo "FATAL ERROR: CMC File missing ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + fi + + gfile=${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} + ${WGRIB2:?} -s $gfile >cmc.ix + export err=$?; err_chk + + for parm in ${wgrib_parmlist} + do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m above" cmc.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cmcgribfile.${PDY}${cyc} ;; + + "SurfaceV") + grep "VGRD:10 m above" cmc.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cmcgribfile.${PDY}${cyc} ;; + *) + grep "${parm}" cmc.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cmcgribfile.${PDY}${cyc} ;; + esac + done + done + +# J.Peng--04-05-2017--changing grid from 180E to 0E------------ +# wgrib2 junk -small_grib 0:359.76 -90:90 junk2 +# cp ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/junk +# rm -f ${TRKDATA}/cmcgribfile.${PDY}${cyc} +# ${WGRIB2} ${TRKDATA}/junk -small_grib 0:359.76 -90:90 ${TRKDATA}/junk2 +# cp ${TRKDATA}/junk2 ${TRKDATA}/cmcgribfile.${PDY}${cyc} + +# Now run through the work to get data for phase checking.... + ${GRB2INDEX:?} ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/cmcixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_g2.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- +# +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + gparmq=7 + gparmt=11 + + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/rh_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparmq=${gparmq}," >>${namelist} + echo " iparmt=${gparmt}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT16=${FIXens_tracker}/cmc_rh_levs.txt + + # Output file + export FORT51=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/rhum_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to rhum_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -rhum_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + rhfile=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + cat ${rhfile} >>${catfile} +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk + +fi + +# ------------------------------------------------------ +# Process Canadian Ensemble perturbation, if selected +# ------------------------------------------------------ + +if [ ${model} -eq 16 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + +# if [ ! -s ${ccedir}/${ccegfile}${fhour} ] + if [ ! -s ${ccedir}/${ccegfile}${fhour}${ccegfile2} ] + then + set +x + echo " " +# echo "FATAL ERROR: CANADIAN ENSEMBLE ${PERT} missing: ${ccegfile}${fhour}" + echo "FATAL ERROR: CANADIAN ENSEMBLE ${PERT} missing: ${ccegfile}${fhour}${ccegfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x +# err_exit "MISSING CMC Ens FILE IN TRACKER SCRIPT: ${ccegfile}${fhour}" + err_exit "MISSING CMC Ens FILE IN TRACKER SCRIPT: ${ccegfile}${fhour}${ccegfile2}" + fi + +# gfile=${ccedir}/${ccegfile}${fhour} + gfile=${ccedir}/${ccegfile}${fhour}${ccegfile2} + ${WGRIB2:?} -s $gfile >ens.ix + export err=$?; err_chk + + for parm in ${wgrib_parmlist} + do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m " ens.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ;; + "SurfaceV") + grep "VGRD:10 m " ens.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ;; + *) + grep "${parm}" ens.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ;; + esac + done + done + + ${GRB2INDEX:?} ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk +# Process the cyclone phase variables, if requested + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}, iparm=${gparm}/" >${namelist} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} +# rm $tavefile $zfile + +#J.Peng----2014-10-22----added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_g2.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2014-10-22----added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + gribfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +fi + + +#------------------------------------------------------------------------# +# Now run the tracker # +#------------------------------------------------------------------------# +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW EXECUTING TRACKER......" +echo " -----------------------------------------------" +echo " " +set -x + +ist=1 +while [ $ist -le 15 ] +do + if [ ${stormflag[${ist}]} -ne 1 ] + then + set +x; echo "Storm number $ist NOT selected for processing"; set -x + else + set +x; echo "Storm number $ist IS selected for processing...."; set -x + fi + let ist=ist+1 +done + +if [ ! -s ${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} ] +then + for fhr in $fcsthrs; do + if [ $fhr -ne 99 ]; then + last_fcst_hour=$fhr + fi + done + echo ${last_fcst_hour} >${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} +fi + +namelist=${TRKDATA}/input.${atcfout}.${PDY}${cyc} +ATCFNAME=` echo "${atcfname}" | tr '[a-z]' '[A-Z]'` + +export atcfymdh=${scc}${syy}${smm}${sdd}${shh} + +contour_interval=100.0 +write_vit=y +want_oci=.TRUE. + +echo "&datein inp%bcc=${scc},inp%byy=${syy},inp%bmm=${smm}," >${namelist} +echo " inp%bdd=${sdd},inp%bhh=${shh},inp%model=${model}," >>${namelist} +echo " inp%modtyp='${modtyp}'," >>${namelist} +echo " inp%lt_units='${lead_time_units}'," >>${namelist} +echo " inp%file_seq='${file_sequence}'," >>${namelist} +echo " inp%nesttyp='${nest_type}'/" >>${namelist} +echo "&atcfinfo atcfnum=${atcfnum},atcfname='${ATCFNAME}'," >>${namelist} +echo " atcfymdh=${atcfymdh},atcffreq=${atcffreq}/" >>${namelist} +echo "&trackerinfo trkrinfo%westbd=${trkrwbd}," >>${namelist} +echo " trkrinfo%eastbd=${trkrebd}," >>${namelist} +echo " trkrinfo%northbd=${trkrnbd}," >>${namelist} +echo " trkrinfo%southbd=${trkrsbd}," >>${namelist} +echo " trkrinfo%type='${trkrtype}'," >>${namelist} +echo " trkrinfo%mslpthresh=${mslpthresh}," >>${namelist} +echo " trkrinfo%v850thresh=${v850thresh}," >>${namelist} +echo " trkrinfo%gridtype='${modtyp}'," >>${namelist} +echo " trkrinfo%contint=${contour_interval}," >>${namelist} +echo " trkrinfo%want_oci=${want_oci}," >>${namelist} +echo " trkrinfo%out_vit='${write_vit}'," >>${namelist} +echo " trkrinfo%gribver=${gribver}," >>${namelist} +echo " trkrinfo%g2_jpdtn=${g2_jpdtn}/" >>${namelist} +echo "&phaseinfo phaseflag='${PHASEFLAG}'," >>${namelist} +echo " phasescheme='${PHASE_SCHEME}'," >>${namelist} +echo " wcore_depth=${WCORE_DEPTH}/" >>${namelist} +echo "&structinfo structflag='${STRUCTFLAG}'," >>${namelist} +echo " ikeflag='${IKEFLAG}'/" >>${namelist} +echo "&fnameinfo gmodname='${atcfname}'," >>${namelist} +echo " rundescr='${rundescr}'," >>${namelist} +echo " atcfdescr='${atcfdescr}'/" >>${namelist} +echo "&verbose verb=3/" >>${namelist} +echo "&waitinfo use_waitfor='n'," >>${namelist} +echo " wait_min_age=10," >>${namelist} +echo " wait_min_size=100," >>${namelist} +echo " wait_max_wait=1800," >>${namelist} +echo " wait_sleeptime=5," >>${namelist} +echo " per_fcst_command=''/" >>${namelist} + +export pgm=gettrk_gen_g2 +. prep_step + +export FORT11=${gribfile} +export FORT12=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} +export FORT14=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +export FORT15=${FIXens_tracker}/${cmodel}.genesis_leadtimes_120 +export FORT31=${ixfile} + +if [ ${trkrtype} = 'tracker' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT61=${TRKDATA}/trak.${atcfout}.all.${stormenv}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${stormenv}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${stormenv}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${stormenv}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${stormenv}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${stormenv}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${stormenv}.${PDY}${cyc} + else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${PDY}${cyc} + fi +else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${regtype}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${regtype}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${regtype}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${regtype}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${regtype}.${PDY}${cyc} +fi + +if [ ${atcfname} = 'aear' ] +then + export FORT65=${TRKDATA}/trak.${atcfout}.initvitl.${PDY}${cyc} +fi + +if [ ${write_vit} = 'y' ] +then + export FORT67=${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} +fi + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${stormenv}.${PDY}${cyc} + else + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${PDY}${cyc} + fi +fi + +if [ ${STRUCTFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${stormenv}.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${stormenv}.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${stormenv}.${PDY}${cyc} + else + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${PDY}${cyc} + fi +fi + +if [ ${IKEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${stormenv}.${PDY}${cyc} + else + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${PDY}${cyc} + fi +fi + +if [ ${trkrtype} = 'midlat' -o ${trkrtype} = 'tcgen' ]; then + export FORT77=${TRKDATA}/trkrmask.${atcfout}.${regtype}.${PDY}${cyc} +fi + +msg="$pgm start for $atcfout at ${cyc}z" +postmsg "$jlogfile" "$msg" + +set +x +echo "+++ TIMING: BEFORE gettrk ---> `date`" +set -x + +ulimit -c unlimited + +${EXECens_tracker}/gettrk_gen_g2 <${namelist} +gettrk_rcc=$? + +set +x +echo "+++ TIMING: AFTER gettrk ---> `date`" +set -x + +#--------------------------------------------------------------# +# Now copy the output track files to different directories +#--------------------------------------------------------------# + +if [ ${gettrk_rcc} -eq 0 ]; then + set +x + echo " " + echo " -----------------------------------------------" + echo " NOW COPYING OUTPUT TRACK FILES TO COM " + echo " -----------------------------------------------" + echo " " + set -x + + if [ "$SENDCOM" = 'YES' ]; then + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} ${COMOUT}/ +# cp ${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} ${COMOUT}/ + + #if [ "$SENDDBN" = 'YES' ]; then + # $DBNROOT/bin/dbn_alert MODEL ENS_GENESIS $job ${COMOUT}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + #fi + fi + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" +else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running gettrk_gen_g2, " + echo "!!! which is the program that actually gets the track." + echo "!!! Return code from gettrk_gen_g2 = ${gettrk_rcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "ERROR RUNNING gettrk_gen_g2 IN 2nd step" +fi diff --git a/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_gfs.sh b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_gfs.sh new file mode 100755 index 0000000000..8d28aad3a6 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_gfs.sh @@ -0,0 +1,2775 @@ +#!/bin/ksh +export PS4=' + extrkr_gen_gfs.sh line $LINENO: ' + +set +x +############################################################################## +echo " " +echo "------------------------------------------------" +echo "xxxx - Track vortices in model GRIB2 output" +echo "J.Peng---10-07-2014-----------------------------" +echo " " +echo "Current time is: `date`" +echo " " +############################################################################## +set -x + +loopnum=$1 +cmodel=$2 +pert=$3 +TRKDATA=$4 + +export gribver=2 +#init_flag=$5 +# USE_OPER_VITALS=NO +# USE_OPER_VITALS=INIT_ONLY +USE_OPER_VITALS=YES + +############################################################################## +# +# FLOW OF CONTROL +# +# 1. Define data directories and file names for the input model +# 2. Process input starting date/cycle information +# 3. Update TC Vitals file and select storms to be processed +# 4. Cut apart input GRIB files to select only the needed parms and hours +# 5. Execute the tracker +# 6. Copy the output track files to various locations +# +############################################################################## +msg="has begun for ${cmodel} ${pert} at ${cyc}z" +postmsg "$jlogfile" "$msg" +######################################## + +# This script runs the hurricane tracker using operational GRIB model output. +# This script makes sure that the data files exist, it then pulls all of the +# needed data records out of the various GRIB forecast files and puts them +# into one, consolidated GRIB file, and then runs a program that reads the TC +# Vitals records for the input day and updates the TC Vitals (if necessary). +# It then runs gettrk, which actually does the tracking. +# +# Environmental variable inputs needed for this scripts: +# PDY -- The date for data being processed, in YYYYMMDD format +# cyc -- The numbers for the cycle for data being processed (00, 06, 12, 18) +# cmodel -- Model being processed (gfs, mrf, ukmet, ecmwf, nam, ngm, ngps, +# gdas, gfdl, ens (ncep ensemble)) +# envir -- 'prod' or 'test' +# SENDCOM -- 'YES' or 'NO' +# stormenv -- This is only needed by the tracker run for the GFDL model. +# 'stormenv' contains the name/id that is used in the input +# grib file names. +# pert -- This is only needed by the tracker run for the NCEP ensemble. +# 'pert' contains the ensemble member id (e.g., n2, p4, etc.) +# which is used as part of the grib file names. +# +# For testing script interactively in non-production set following vars: +# archsyndir - Directory with syndir scripts/exec/fix +# + +qid=$$ +#----------------------------------------------# +# Get input date information # +#----------------------------------------------# + +#export PDY=${PDY:-$1} +#export cyc=${cyc:-$2} +#export cmodel=${cmodel:-$3} +export jobid=${jobid:-testjob} +export SENDCOM=${SENDCOM:-NO} +export PHASEFLAG=y +export WCORE_DEPTH=1.0 +#export PHASE_SCHEME=vtt +#export PHASE_SCHEME=cps +export PHASE_SCHEME=both +export STRUCTFLAG=n +export IKEFLAG=n + +if [ ! -d $TRKDATA ] +then + mkdir -p $TRKDATA +fi +cd $TRKDATA + +if [ ${#PDY} -eq 0 -o ${#cyc} -eq 0 -o ${#cmodel} -eq 0 ] +then + set +x + echo " " + echo "FATAL ERROR: Something wrong with input parameters." + echo "PDY= ${PDY}, cyc= ${cyc}, cmodel= ${cmodel}" + set -x + err_exit "BAD INPUTS AT LINE $LINENO IN extrkr_gen_gfs.sh" +else + set +x + echo " " + echo " #-----------------------------------------------------------------#" + echo " At beginning of tracker script, the following imported variables " + echo " are defined: " + echo " PDY ................................... $PDY" + echo " cyc ................................... $cyc" + echo " cmodel ................................ $cmodel" + echo " jobid ................................. $jobid" + echo " envir ................................. $envir" + echo " SENDCOM ............................... $SENDCOM" + echo " " + set -x +fi + +scc=`echo ${PDY} | cut -c1-2` +syy=`echo ${PDY} | cut -c3-4` +smm=`echo ${PDY} | cut -c5-6` +sdd=`echo ${PDY} | cut -c7-8` +shh=${cyc} +symd=`echo ${PDY} | cut -c3-8` +syyyy=`echo ${PDY} | cut -c1-4` +CENT=`echo ${PDY} | cut -c1-2` + +#export archsyndir=${archsyndir:-${COMROOT}/arch/prod/syndat} +archsyndir=${archsyndir:-${COMINsyn:?}} + +#----------------------------------------------------------------# +# +# --- Define data directories and data file names --- +# +# Convert the input model to lowercase letters and check to see +# if it's a valid model, and assign a model ID number to it. +# This model ID number is passed into the Fortran program to +# let the program know what set of forecast hours to use in the +# ifhours array. Also, set the directories for the operational +# input GRIB data files and create templates for the file names. +# While only 1 of these sets of directories and file name +# templates is used during a particular run of this script, +# "gfsvitdir" is used every time, because that is the directory +# that contains the error-checked TC vitals file that Steve Lord +# produces, and so it is included after the case statement. +# +# NOTE: The varible PDY is now defined within the J-Jobs that +# call this script. Therefore there is no reason to do this +# here. +# +# NOTE: The script that processes the ECMWF data defines PDY as +# the current day, and in this script we need PDY to be +# yesterday's date (for the ecmwf ONLY). So instead, the ecmwf +# script will pass the variable PDYm1 to this script, and in the +# case statement below we change that to PDY. +# +# NOTE: Do NOT try to standardize this script by changing all of +# these various data directories' variable names to be all the +# same, such as "datadir". As you'll see in the data cutting +# part of this script below, different methods are used to cut +# apart different models, thus it is important to know the +# difference between them.... +#----------------------------------------------------------------# + +cmodel=`echo ${cmodel} | tr "[A-Z]" "[a-z]"` +#---- tracking variable list------------------------------------ +user_wants_to_track_zeta850='y' +user_wants_to_track_zeta700='y' +user_wants_to_track_wcirc850='y' +user_wants_to_track_wcirc700='y' +user_wants_to_track_gph850='y' +user_wants_to_track_gph700='y' +user_wants_to_track_mslp='y' +user_wants_to_track_wcircsfc='n' +user_wants_to_track_zetasfc='n' +user_wants_to_track_thick500850='n' +user_wants_to_track_thick200850='n' +user_wants_to_track_thick200500='n' + +case ${cmodel} in + + gfs) set +x; echo " " ; + echo " ++ operational FV3-GFS chosen" ; + echo " "; set -x ; + gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}/${COMPONENT}} ; + gfsgfile=gfs.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=1 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=15 ; + atcffreq=$((vit_incr*100)) ; + +# trkrwbd=0 ; +# trkrebd=360 ; +# trkrnbd=90 ; +# trkrsbd=-90 ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="gfso" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="gfso" ;; + +# g2_jpdtn sets the variable that will be used as "JPDTN" for +# the call to getgb2, if gribver=2. jpdtn=1 for ens data, +# jpdtn=0 for deterministic data. + + ngps) set +x; echo " " ; + echo " ++ operational NAVGEM chosen" ; + echo " "; set -x ; + ngpsdir=${ngpsdir:-${DCOM:?}} ; + ngpsgfile=US058GMET-OPSbd2.NAVGEM ; + ngemgfile=-${PDY}${cyc}-NOAA-halfdeg.gr2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=7 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=29 ; + atcffreq=$((vit_incr*100)) ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="ngx " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="ngx" ;; + + cmc) set +x; echo " " ; + echo " ++ operational CMC chosen" ; + echo " "; set -x ; + cmcdir=${cmcdir:-${DCOM:?}} ; + cmcgfile=CMC_glb_latlon.24x.24_${PDY}${cyc}_P ; + cmcgfile2=_NCEP.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=15 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=39 ; + atcffreq=$((vit_incr*100)) ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="cmc " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="cmc" ;; + + ens) set +x; echo " " ; + echo " ++ operational ensemble member ${pert} chosen"; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + + ensdira=${ensdira:-${COMINgefs:?}/pgrb2ap5} ; + ensgfilea=ge${pert}.t${cyc}z.pgrb2a.0p50.f ; + ensdirb=${ensdirb:-${COMINgefs:?}/pgrb2bp5} ; + ensgfileb=ge${pert}.t${cyc}z.pgrb2b.0p50.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=10 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="a${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="a${pert_posneg}${pert_num}" ;; + +# g2_jpdtn sets the variable that will be used as "JPDTN" for +# the call to getgb2, if gribver=2. jpdtn=1 for ens data, +# jpdtn=0 for deterministic data. + + fens) set +x; echo " " ; + echo " ++ FNMOC ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + + fensdir=${fensdir:-${DCOM:?}} ; + fensgfile=ENSEMBLE.MET.fcst_et0${pert_num}. ; + gensgfile=.${PDY}${cyc} ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=22 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + +# pert_posneg=` echo "${pert}" | cut -c1-1` ; +# pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="n${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="n${pert_posneg}${pert_num}" ;; + + cens) set +x; echo " " ; + echo " ++ Canadian ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + ccedir=${ccedir:-${DCOM:?}} ; + ccegfile=${PDY}${cyc}_CMC_naefs_hr_latlon0p5x0p5_P ; + ccegfile2=_0${pert_num}.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=16 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + +# pert_posneg=` echo "${pert}" | cut -c1-1` ; +# pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="c${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="c${pert_posneg}${pert_num}" ;; + + *) msg="FATAL ERROR: Model $cmodel is not recognized." ; + echo "$msg"; postmsg $jlogfile "$msg" ; + err_exit "UNKNOWN cmodel IN extrkr_gen_gfs.sh";; + +esac + +if [ ${PHASEFLAG} = 'y' ]; then + +#J.Peng----2014-10-28--------- + if [ ${cmodel} = "gfs" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300|RH:500)" + + elif [ ${cmodel} = "ngps" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:400|HGT:300|TMP:500|TMP:400|TMP:300|RH:500)" + + elif [ ${cmodel} = "ens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300|RH:500)" + + elif [ ${cmodel} = "cens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:300|TMP:500|TMP:250|RH:500)" + + elif [ ${cmodel} = "cmc" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250|SPFH:500)" + + elif [ ${cmodel} = "fens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250|RH:500)" + + fi + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 U:200 V:850 V:700 V:500 V:200 10U:sfc 10V:sfc MSL:sfc GH:300 GH:400 GH:500 GH:925 T:300 T:400 T:500 R:500" + +else + wgrib_parmlist=" HGT:850 HGT:700 UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET " + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc" +fi + +#---------------------------------------------------------------# +# +# -------- TC Vitals processing -------- +# +# Check Steve Lord's operational tcvitals file to see if any +# vitals records were processed for this time by his system. +# If there were, then you'll find a file in /com/gfs/prod/gfs.yymmdd +# with the vitals in it. Also check the raw TC Vitals file in +# /com/arch/prod/syndat , since this may contain storms that Steve's +# system ignored (Steve's system will ignore all storms that are +# either over land or very close to land); We still want to track +# these inland storms, AS LONG AS THEY ARE NHC STORMS (don't +# bother trying to track inland storms that are outside of NHC's +# domain of responsibility -- we don't need that info). +# UPDATE 5/12/98 MARCHOK: The script is updated so that for the +# ensemble models, the gfs directory is checked for the error- +# checked vitals file, while for the regional models, the +# nam directory is checked for that file. +# UPDATE 3/27/09 MARCHOK: The SREF is run at off-synoptic times +# (03,09,15,21Z). There are no tcvitals issued at these offtimes, +# so the updating of the "old" tcvitals is critical for running +# the tracker on SREF. For updating the old tcvitals for SREF, +# we need to look 3h back, not 6h as for the other models that +# run at synoptic times. +#--------------------------------------------------------------# + +old_ymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +old_4ymd=` echo ${old_ymdh} | cut -c1-8` +old_ymd=` echo ${old_ymdh} | cut -c3-8` +old_hh=` echo ${old_ymdh} | cut -c9-10` +old_str="${old_ymd} ${old_hh}00" + +future_ymdh=` ${NDATE:?} ${vit_incr} ${PDY}${cyc}` +future_4ymd=` echo ${future_ymdh} | cut -c1-8` +future_ymd=` echo ${future_ymdh} | cut -c3-8` +future_hh=` echo ${future_ymdh} | cut -c9-10` +future_str="${future_ymd} ${future_hh}00" + +if [ ${modtyp} = 'global' ] +then + synvitdir=${COMINgfs:?}/${cyc}/${COMPONENT} + synvitfile=gfs.t${cyc}z.syndata.tcvitals.tm00 + synvitold_dir=${synvitdir%.*}.${old_4ymd}/${old_hh}/${COMPONENT} + synvitold_file=gfs.t${old_hh}z.syndata.tcvitals.tm00 + synvitfuture_dir=${synvitdir%.*}.${future_4ymd}/${future_hh}/${COMPONENT} + synvitfuture_file=gfs.t${future_hh}z.syndata.tcvitals.tm00 +else +# synvitdir=${COMROOT}/nam/prod/nam.${PDY} + synvitdir=${COMINnam:?} + synvitfile=nam.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/nam/prod/nam.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=nam.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/nam/prod/nam.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=nam.t${future_hh}z.syndata.tcvitals.tm00 +fi + +set +x +echo " " +echo " -----------------------------" +echo " " +echo " Now sorting and updating the TC Vitals file. Please wait...." +echo " " +set -x + +current_str="${symd} ${cyc}00" + +if [ -s ${synvitdir}/${synvitfile} -o\ + -s ${synvitold_dir}/${synvitold_file} -o\ + -s ${synvitfuture_dir}/${synvitfuture_file} ] +then + grep "${old_str}" ${synvitold_dir}/${synvitold_file} \ + >${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${synvitdir}/${synvitfile} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${future_str}" ${synvitfuture_dir}/${synvitfuture_file} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} +else + set +x + echo " " + echo " There is no (synthetic) TC vitals file for ${cyc}z in ${synvitdir}," + echo " nor is there a TC vitals file for ${old_hh}z in ${synvitold_dir}." + echo " nor is there a TC vitals file for ${future_hh}z in ${synvitfuture_dir}," + echo " Checking the raw TC Vitals file ....." + echo " " + set -x +fi + +# Take the vitals from Steve Lord's /com/gfs/prod tcvitals file, +# and cat them with the NHC-only vitals from the raw, original +# /com/arch/prod/synda_tcvitals file. Do this because the nwprod +# tcvitals file is the original tcvitals file, and Steve runs a +# program that ignores the vitals for a storm that's over land or +# even just too close to land, and for tracking purposes for the +# US regional models, we need these locations. Only include these +# "inland" storm vitals for NHC (we're not going to track inland +# storms that are outside of NHC's domain of responsibility -- we +# don't need that info). +# UPDATE 5/12/98 MARCHOK: awk logic is added to screen NHC +# vitals such as "91L NAMELESS" or "89E NAMELESS", since TPC +# does not want tracks for such storms. + +grep "${old_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${current_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${future_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-03---for genesis tc vitals-------------------- +#export COMINgenvit=/ensemble/save/Jiayi.Peng/genesis_vital_${syyyy} +#grep "${old_str}" ${COMINgenvit}/gen_tc.vitals.gfs.gfso.2012 | \ +# grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ +# >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +if [ ${cmodel} = 'ens' -o ${cmodel} = 'gfs' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.gfs.gfso.${syyyy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +elif [ ${cmodel} = 'cens' -o ${cmodel} = 'cmc' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.cmc.cmc.${syyyy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +elif [ ${cmodel} = 'fens' -o ${cmodel} = 'ngps' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.ngps.ngx.${syyyy} | \ +grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +elif [ ${cmodel} = 'eens' -o ${cmodel} = 'ecmwf' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.ecmwf.emx.${syyyy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +fi + +#PRODTEST# Use the next couple lines to test the tracker on the SP. +#PRODTEST# These next couple lines use data from a test TC Vitals file that +#PRODTEST# I generate. When you are ready to test this system, call me and +#PRODTEST# I'll create one for the current day, and then uncomment the next +#PRODTEST# couple lines in order to access the test vitals file. +# +#ttrkdir=/ensemble/save/wx20tm/trak/prod/data +#ttrkdir=/ensemble/save/wx20tm/trak/para/scripts +#grep "${current_str}" ${ttrkdir}/tcvit.01l >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + + +# IMPORTANT: When "cat-ing" these files, make sure that the vitals +# files from the "raw" TC vitals files are first in order and Steve's +# TC vitals files second. This is because Steve's vitals file has +# been error-checked, so if we have a duplicate tc vitals record in +# these 2 files (very likely), program supvit.x below will +# only take the last vitals record listed for a particular storm in +# the vitals file (all previous duplicates are ignored, and Steve's +# error-checked vitals records are kept). + +cat ${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} ${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} \ + >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# If we are doing the processing for the GFDL model, then we want +# to further cut down on which vitals we allow into this run of the +# tracker. The reason is that this program will be called from +# each individual run for a storm, so the grib files will be +# specific to each storm. So if 4 storms are being run at a +# particular cycle, then this script is run 4 separate times from +# within the GFDL_POST job. + +#if [ ${cmodel} = 'gfdl' ]; then +## grep -i ${stormid} ${COMIN}/${ATCFNAME}.vitals.${syy}${smm}${sdd}${shh} >${TRKDATA}/tmpvit +## mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#echo " " +#echo "!!! ERROR: TAKE THE COMMENTS OUT OF HERE BEFORE IMPLEMENTATION AAAA" +#echo "!!! AND TAKE OUT THESE NEXT 2 LINES AS WELL............................" +#echo " " +#grep -i ${stormid} ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/tmpvit +#mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#fi + +#--------------------------------------------------------------# +# Now run a fortran program that will read all the TC vitals +# records for the current dtg and the dtg from 6h ago, and +# sort out any duplicates. If the program finds a storm that +# was included in the vitals file 6h ago but not for the current +# dtg, this program updates the 6h-old first guess position +# and puts these updated records as well as the records from +# the current dtg into a temporary vitals file. It is this +# temporary vitals file that is then used as the input for the +# tracking program. +#--------------------------------------------------------------# + +oldymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +oldyy=`echo ${oldymdh} | cut -c3-4` +oldmm=`echo ${oldymdh} | cut -c5-6` +olddd=`echo ${oldymdh} | cut -c7-8` +oldhh=`echo ${oldymdh} | cut -c9-10` +oldymd=${oldyy}${oldmm}${olddd} + +futureymdh=` ${NDATE:?} 6 ${PDY}${cyc}` +futureyy=`echo ${futureymdh} | cut -c3-4` +futuremm=`echo ${futureymdh} | cut -c5-6` +futuredd=`echo ${futureymdh} | cut -c7-8` +futurehh=`echo ${futureymdh} | cut -c9-10` +futureymd=${futureyy}${futuremm}${futuredd} + +echo "&datenowin dnow%yy=${syy}, dnow%mm=${smm}," >${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&dateoldin dold%yy=${oldyy}, dold%mm=${oldmm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dold%dd=${olddd}, dold%hh=${oldhh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&datefuturein dfuture%yy=${futureyy}, dfuture%mm=${futuremm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dfuture%dd=${futuredd}, dfuture%hh=${futurehh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&hourinfo vit_hr_incr=${vit_incr}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period." + echo "!!! File ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} is empty." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi + +fi + +# For tcgen cases, filter to use only vitals from the ocean +# basin of interest.... +#J.Peng----2012-04-13------------------------------------- +#if [ ${trkrtype} = 'tcgen' ] +# then + +# if [ ${numvitrecs} -gt 0 ] +# then + +# fullvitfile=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# cp $fullvitfile ${TRKDATA}/vitals.all_basins.${atcfout}.${PDY}${cyc} +# basin=` echo $regtype | cut -c1-2` + +# if [ ${basin} = 'al' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "L") print $0}' | +# >${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'ep' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "E") print $0}' | +# >${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'wp' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "W") print $0}' | +# >${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi + +# cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# fi + +#fi +#J.Peng----2012-04-13------------------------------------- + +# - - - - - - - - - - - - - +# Before running the program to read, sort and update the vitals, +# first run the vitals through some awk logic, the purpose of +# which is to convert all the 2-digit years into 4-digit years. +# Beginning 4/21/99, NHC and JTWC will begin sending the vitals +# with 4-digit years, however it is unknown when other ensemble +# forecasting centers will begin using 4-digit years, thus we +# need the following logic to ensure that all the vitals going +# into supvit.f have uniform, 4-digit years in their records. +# +# 1/8/2000: sed code added by Tim Marchok due to the fact that +# some of the vitals were getting past the syndata/qctropcy +# error-checking with a colon in them; the colon appeared +# in the character immediately to the left of the date, which +# was messing up the "(length($4) == 8)" statement logic. +# - - - - - - - - - - - - - + +sed -e "s/\:/ /g" ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} > ${TRKDATA}/tempvit +mv ${TRKDATA}/tempvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +awk ' +{ + yycheck = substr($0,20,2) + if ((yycheck == 20 || yycheck == 19) && (length($4) == 8)) { + printf ("%s\n",$0) + } + else { + if (yycheck >= 0 && yycheck <= 50) { + printf ("%s20%s\n",substr($0,1,19),substr($0,20)) + } + else { + printf ("%s19%s\n",substr($0,1,19),substr($0,20)) + } + } +} ' ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 + +mv ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +#Added by J.Peng----2012-04-12---------------------------------------------- +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +#----------------------------------------------------------------- +if [ ${numvitrecs} -gt 0 ] +then + export pgm=supvit_g2 + . prep_step + + # Input file + export FORT31=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_g2 <${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + suvrcc=$? + + if [ ${suvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit.x, " + echo "!!! which is the program that updates the TC Vitals file." + echo "!!! Return code from supvit.x = ${suvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "ERROR RUNNING supvit_g2 IN extrkr_gen_gfs LINE $LINENO" + fi + +else +#Added by J.Peng----2012-04-12---------------------------------------------- + rm -f ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + touch ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#----------------------------------------------------------------- +# In this section, check to see if the user requested the use of +# operational TC vitals records for the initial time only. This +# option might be used for a retrospective medium range forecast +# in which the user wants to initialize with the storms that are +# currently there, but then let the model do its own thing for +# the next 10 or 14 days.... + + +if [ ${USE_OPER_VITALS} = 'INIT_ONLY' ]; then + + if [ ${init_flag} = 'yes' ]; then + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are currently at." + echo " " + set -x + else + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are now *PAST*." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + fi + +elif [ ${USE_OPER_VITALS} = 'NO' ]; then + + set +x + echo " " + echo "NOTE: User has requested that historical vitals not be used...." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#------------------------------------------------------------------# +# Now select all storms to be processed, that is, process every +# storm that's listed in the updated vitals file for the current +# forecast hour. If there are no storms for the current time, +# then exit. +#------------------------------------------------------------------# + +numvitrecs=`cat ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period " + echo "!!! in the UPDATED vitals file." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi +fi + +set +x +echo " " +echo " *--------------------------------*" +echo " | STORM SELECTION |" +echo " *--------------------------------*" +echo " " +set -x + +ict=1 +while [ $ict -le 15 ] +do + stormflag[${ict}]=3 + let ict=ict+1 +done + +dtg_current="${symd} ${cyc}00" +stormmax=` grep "${dtg_current}" ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +if [ ${stormmax} -gt 15 ] +then + stormmax=15 +fi + +sct=1 +while [ ${sct} -le ${stormmax} ] +do + stormflag[${sct}]=1 + let sct=sct+1 +done + + +#---------------------------------------------------------------# +# +# -------- "Genesis" Vitals processing -------- +# +# May 2006: This entire genesis tracking system is being +# upgraded to more comprehensively track and categorize storms. +# One thing that has been missing from the tracking system is +# the ability to keep track of storms from one analysis cycle +# to the next. That is, the current system has been very +# effective at tracking systems within a forecast, but we have +# no methods in place for keeping track of storms across +# difference initial times. For example, if we are running +# the tracker on today's 00z GFS analysis, we will get a +# position for various storms at the analysis time. But then +# if we go ahead and run again at 06z, we have no way of +# telling the tracker that we know about the 00z position of +# this storm. We now address that problem by creating +# "genesis" vitals, that is, when a storm is found at an +# analysis time, we not only produce "atcfunix" output to +# detail the track & intensity of a found storm, but we also +# produce a vitals record that will be used for the next +# run of the tracker script. These "genesis vitals" records +# will be of the format: +# +# YYYYMMDDHH_AAAH_LLLLX_TYP +# +# Where: +# +# YYYYMMDDHH = Date the storm was FIRST identified +# by the tracker. +# AAA = Abs(Latitude) * 10; integer value +# H = 'N' for norther hem, 'S' for southern hem +# LLLL = Abs(Longitude) * 10; integer value +# X = 'E' for eastern hem, 'W' for western hem +# TYP = Tropical cyclone storm id if this is a +# tropical cyclone (e.g., "12L", or "09W", etc). +# If this is one that the tracker instead "Found +# On the Fly (FOF)", we simply put those three +# "FOF" characters in there. +#J.Peng----2012-04-12-------- +genvitfile=${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} +touch ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + +if [ -f $genvitfile ]; then + d6ago_ymdh=` ${NDATE:?} -6 ${PDY}${cyc}` + d6ago_4ymd=` echo ${d6ago_ymdh} | cut -c1-8` + d6ago_ymd=` echo ${d6ago_ymdh} | cut -c3-8` + d6ago_hh=` echo ${d6ago_ymdh} | cut -c9-10` + d6ago_str="${d6ago_ymd} ${d6ago_hh}00" + + d6ahead_ymdh=` ${NDATE:?} 6 ${PDY}${cyc}` + d6ahead_4ymd=` echo ${d6ahead_ymdh} | cut -c1-8` + d6ahead_ymd=` echo ${d6ahead_ymdh} | cut -c3-8` + d6ahead_hh=` echo ${d6ahead_ymdh} | cut -c9-10` + d6ahead_str="${d6ahead_ymd} ${d6ahead_hh}00" + + set +x + echo " " + echo " d6ago_str= --->${d6ago_str}<---" + echo " current_str= --->${current_str}<---" + echo " d6ahead_str= --->${d6ahead_str}<---" + echo " " + #echo " Listing and contents of ${genvitfile} follow " + #echo " for the times 6h ago, current and 6h ahead:" + #echo " " + set -x + + #J.Peng----2012-04-11----- + ##ls -la ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + ##cat ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + #ls -la ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #cat ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + + grep "${d6ago_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${d6ahead_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + num_gen_vits=`cat ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} | wc -l` +else + echo "WARNING: Genesis Vitals from previous run(s) not found!" +fi + +echo "&datenowin dnow%yy=${syyyy}, dnow%mm=${smm}," >${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6agoin d6ago%yy=${syyyy6}, d6ago%mm=${smm6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ago%dd=${sdd6}, d6ago%hh=${shh6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6aheadin d6ahead%yy=${syyyyp6}, d6ahead%mm=${smmp6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ahead%dd=${sddp6}, d6ahead%hh=${shhp6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-04---do not need calculate this ---------- +num_gen_vits=0 + +if [ ${num_gen_vits} -gt 0 ] +then + export pgm=supvit_gen + . prep_step + + # Input file + export FORT31=${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_gen <${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + sgvrcc=$? + + if [ ${sgvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit_gen, " + echo "!!! which is the program that updates the genesis vitals file." + echo "!!! Return code from supvit_gen = ${sgvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "ERROR RUNNING SUPVIT_GEN IN extrkr_gen_gfs.sh LINE $LINENO" + fi + +else + rm -f ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + touch ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +fi + + +#-----------------------------------------------------------------# +# +# ------ CUT APART INPUT GRIB FILES ------- +# +# For the selected model, cut apart the GRIB input files in order +# to pull out only the variables that we need for the tracker. +# Put these selected variables from all forecast hours into 1 big +# GRIB file that we'll use as input for the tracker. +# +# The wgrib utility (/nwprod/util/exec/wgrib) is used to cut out +# the needed parms for the GFS, MRF, GDAS, UKMET and NOGAPS files. +# The utility /nwprod/util/exec/copygb is used to interpolate the +# NGM (polar stereographic) and NAM (Lambert Conformal) data from +# their grids onto lat/lon grids. Note that while the lat/lon +# grid that I specify overlaps into areas that don't have any data +# on the original grid, Mark Iredell wrote the copygb software so +# that it will mask such "no-data" points with a bitmap (just be +# sure to check the lbms in your fortran program after getgb). +#-----------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------" +echo " NOW CUTTING APART INPUT GRIB FILES TO " +echo " CREATE 1 BIG GRIB INPUT FILE " +echo " -----------------------------------------" +echo " " +set -x + +regflag=`grep NHC ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +# ------------------------------ +# Process FNMOC Ensemble, if selected +# ------------------------------ + +if [ ${model} -eq 22 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ] + then + rm ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + fensfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + + let attempts=1 + while [ $attempts -le 40 ]; do + if [ -s $fensfile ]; then + break + else + sleep 60 + let attempts=attempts+1 + fi + done + if [ $attempts -gt 40 ] && [ ! -s $fensfile ]; then + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "$fensfile" >> ${DATA}/missing_fens.txt + exit + else + err_exit "$fensfile still not available after waiting 40 minutes" + fi + fi + + #${WGRIB2:?} -set_byte 1 11 1 -s $fensfile >fens.ix # set_byte resets local table to 1 to remove error messages + gfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} +# rm $tavefile $zfile + +#J.Peng----2014-10-15---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2014-10-15---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ixfile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + +fi + +# ------------------------------ +# Process GFS, if selected +# ------------------------------ + +if [ ${model} -eq 1 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gfsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc} + fi + + rm ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f* + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gfsgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gfs wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gfsdir}/${gfsgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GFS File missing: ${gfsdir}/${gfsgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GFS FILE IN extrkr_gen_gfs.sh: ${gfsdir}/${gfsgfile}${fhour}" + fi + + gfile=${gfsdir}/${gfsgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + + gfs_master_file=${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + gfs_cat_file=${TRKDATA}/gfsgribfile.${PDY}${cyc} + cat ${gfs_master_file} >>${gfs_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gfsgribfile.${PDY}${cyc} ${TRKDATA}/gfsixfile.${PDY}${cyc} + export err=$?; err_chk + +# Now run through the work to get data for phase checking.... +# -------------------------------------------- + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "BEGIN: 300-500hPa T-mean and 200-850hPa U-shear" + echo " for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + +#J.Peng----2012-04-18---added for U-shear----- + gparm=33 + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-04-18---added for U-shear----- + + set +x + echo " " + echo "END: 300-500hPa T-mean and 200-850hPa U-shear" + echo " for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + cat ${catfile} >>${ffile} + + fi + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gfsixfile.${PDY}${cyc} + +fi + +# -------------------------------------------------- +# Process NCEP Ensemble perturbation, if selected +# -------------------------------------------------- + +if [ ${model} -eq 10 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ensdira}/${ensgfilea}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GEFS ${PERT} File missing: ${ensdira}/${ensgfilea}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GEFS FILE IN TRACKER SCRIPT: ${ensdira}/${ensgfilea}${fhour}" + fi + + gfile=${TRKDATA}/${ensgfilea}${fhour} + cat ${ensdira}/${ensgfilea}${fhour} ${ensdirb}/${ensgfileb}${fhour} > $gfile + + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} + cat ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} >> ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/ens${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in T-mean/U-shear for pert= $pert and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + +#J.Peng----2014-10-15---added for U-shear----- + ffile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + + gparm=33 + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2014-10-15---added for U-shear----- + + set +x + echo " " + echo "Date in T-mean/U-shear for pert= $pert and fhour= $fhour before = `date`" + echo " " + set -x + + done + fi + fi + + gfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------ +# Process NAVGEM, if selected +# ------------------------------ + +if [ ${model} -eq 7 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} ] + then + set +x + echo " " + echo "FATAL ERROR: NAVGEM File missing ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" >> ${DATA}/missing_ngps.txt + exit + else + err_exit "MISSING NAVGEM FILE at $0:$LINENO" + fi + fi + + gfile=${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + + done + +# Now run through the work to get data for phase checking.... + ${GRB2INDEX:?} ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ${TRKDATA}/ngpsixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk + +fi + +# ------------------------------ +# Process CMC deterministic , if selected +# ------------------------------ + +if [ ${model} -eq 15 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/cmcgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/cmcgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} ] + then + set +x + echo " " + echo "FATAL ERROR: CMC File missing ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + fi + + gfile=${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + done + +# J.Peng--04-05-2017--changing grid from 180E to 0E------------ +# wgrib2 junk -small_grib 0:359.76 -90:90 junk2 +# J.Peng--10-15-2019----uncomment the next 4 lines -------- + cp ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/junk + rm -f ${TRKDATA}/cmcgribfile.${PDY}${cyc} + ${WGRIB2} ${TRKDATA}/junk -small_grib 0:359.76 -90:90 ${TRKDATA}/junk2 + cp ${TRKDATA}/junk2 ${TRKDATA}/cmcgribfile.${PDY}${cyc} + +# Now run through the work to get data for phase checking.... + ${GRB2INDEX:?} ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/cmcixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- +# +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + gparmq=7 + gparmt=11 + + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/rh_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparmq=${gparmq}," >>${namelist} + echo " iparmt=${gparmt}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT16=${FIXens_tracker}/cmc_rh_levs.txt + + # Output file + export FORT51=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/rhum_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to rhum_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -rhum_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + rhfile=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + cat ${rhfile} >>${catfile} +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk + +fi + +# ------------------------------------------------------ +# Process Canadian Ensemble perturbation, if selected +# ------------------------------------------------------ + +if [ ${model} -eq 16 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ccedir}/${ccegfile}${fhour}${ccegfile2} ] + then + set +x + echo " " + echo "FATAL ERROR: CANADIAN ENSEMBLE ${PERT} missing: ${ccegfile}${fhour}${ccegfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING CMC Ens FILE IN TRACKER SCRIPT: ${ccegfile}${fhour}${ccegfile2}" + fi + + gfile=${ccedir}/${ccegfile}${fhour}${ccegfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk +# Process the cyclone phase variables, if requested + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}, iparm=${gparm}/" >${namelist} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} +# rm $tavefile $zfile + +#J.Peng----2014-10-22----added for U-shear----- + ffile=${gfile} + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + + gparm=33 + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2014-10-22----added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + gribfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +fi + + +#------------------------------------------------------------------------# +# Now run the tracker # +#------------------------------------------------------------------------# +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW EXECUTING TRACKER......" +echo " -----------------------------------------------" +echo " " +set -x + +ist=1 +while [ $ist -le 15 ] +do + if [ ${stormflag[${ist}]} -ne 1 ] + then + set +x; echo "Storm number $ist NOT selected for processing"; set -x + else + set +x; echo "Storm number $ist IS selected for processing...."; set -x + fi + let ist=ist+1 +done + +if [ ! -s ${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} ] +then + for fhr in $fcsthrs; do + if [ $fhr -ne 99 ]; then + last_fcst_hour=$fhr + fi + done + echo ${last_fcst_hour} >${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} +fi + +namelist=${TRKDATA}/input.${atcfout}.${PDY}${cyc} +ATCFNAME=` echo "${atcfname}" | tr '[a-z]' '[A-Z]'` + +export atcfymdh=${scc}${syy}${smm}${sdd}${shh} + +#------07-26-2019----creating the fix-file for GFS tracking ----- +if [ ${model} -eq 1 ]; then +. prep_step + +# Input files +namelist_2_fix=${TRKDATA}/namelist_2_fix_input.${PDY}${cyc} +echo "&timein maxhrs=${fcstlen}," >${namelist_2_fix} +echo " dthrs=${vit_incr}/" >>${namelist_2_fix} + +# Output file +export FORT10=${TRKDATA}/${cmodel}.genesis_leadtimes_120 + +${EXECens_tracker}/leadtime <${namelist_2_fix} +rcc=$? + +if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to leadtime at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "leadtime - ERROR AT extrkr_gen_gfs.sh LINE $LINENO" +fi + +fi +#------07-26-2019----creating the fix-file for GFS tracking ----- + +#contour_interval=100.0 +#write_vit=y +#want_oci=.TRUE. + +echo "&datein inp%bcc=${scc},inp%byy=${syy},inp%bmm=${smm}," >${namelist} +echo " inp%bdd=${sdd},inp%bhh=${shh},inp%model=${model}," >>${namelist} +echo " inp%modtyp='${modtyp}'," >>${namelist} +echo " inp%lt_units='${lead_time_units}'," >>${namelist} +echo " inp%file_seq='${file_sequence}'," >>${namelist} +echo " inp%nesttyp='${nest_type}'/" >>${namelist} + +echo "&atcfinfo atcfnum=${atcfnum},atcfname='${ATCFNAME}'," >>${namelist} +echo " atcfymdh=${atcfymdh},atcffreq=${atcffreq}/" >>${namelist} + +echo "&trackerinfo trkrinfo%westbd=${trkrwbd}," >>${namelist} +echo " trkrinfo%eastbd=${trkrebd}," >>${namelist} +echo " trkrinfo%northbd=${trkrnbd}," >>${namelist} +echo " trkrinfo%southbd=${trkrsbd}," >>${namelist} +echo " trkrinfo%type='${trkrtype}'," >>${namelist} +echo " trkrinfo%mslpthresh=${mslpthresh}," >>${namelist} +echo " trkrinfo%use_backup_mslp_grad_check='${use_backup_mslp_grad_check}'," >>${namelist} +echo " trkrinfo%max_mslp_850=${max_mslp_850}," >>${namelist} +echo " trkrinfo%v850thresh=${v850thresh}," >>${namelist} +echo " trkrinfo%use_backup_850_vt_check='${use_backup_850_vt_check}'," >>${namelist} +echo " trkrinfo%gridtype='${modtyp}'," >>${namelist} +echo " trkrinfo%enable_timing=1," >>${namelist} +echo " trkrinfo%contint=${contour_interval}," >>${namelist} +echo " trkrinfo%want_oci=${want_oci}," >>${namelist} +echo " trkrinfo%out_vit='${write_vit}'," >>${namelist} +echo " trkrinfo%use_land_mask='${use_land_mask}'," >>${namelist} +echo " trkrinfo%inp_data_type='${inp_data_type}'," >>${namelist} +echo " trkrinfo%gribver=${gribver}," >>${namelist} +echo " trkrinfo%g2_jpdtn=${g2_jpdtn}," >>${namelist} +echo " trkrinfo%g2_mslp_parm_id=${g2_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_mslp_parm_id=${g1_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_typ=${g1_sfcwind_lev_typ}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_val=${g1_sfcwind_lev_val}/" >>${namelist} + +echo "&phaseinfo phaseflag='${PHASEFLAG}'," >>${namelist} +echo " phasescheme='${PHASE_SCHEME}'," >>${namelist} +echo " wcore_depth=${WCORE_DEPTH}/" >>${namelist} + +echo "&structinfo structflag='${STRUCTFLAG}'," >>${namelist} +echo " ikeflag='${IKEFLAG}'/" >>${namelist} + +echo "&fnameinfo gmodname='${atcfname}'," >>${namelist} +echo " rundescr='${rundescr}'," >>${namelist} +echo " atcfdescr='${atcfdescr}'/" >>${namelist} + +echo "&waitinfo use_waitfor='n'," >>${namelist} +echo " wait_min_age=10," >>${namelist} +echo " wait_min_size=100," >>${namelist} +echo " wait_max_wait=1800," >>${namelist} +echo " wait_sleeptime=5," >>${namelist} +echo " per_fcst_command=''/" >>${namelist} + +echo "&netcdflist netcdfinfo%num_netcdf_vars=${ncdf_num_netcdf_vars}," >>${namelist} +echo " netcdfinfo%netcdf_filename='${netcdffile}'," >>${namelist} +echo " netcdfinfo%rv850name='${ncdf_rv850name}'," >>${namelist} +echo " netcdfinfo%rv700name='${ncdf_rv700name}'," >>${namelist} +echo " netcdfinfo%u850name='${ncdf_u850name}'," >>${namelist} +echo " netcdfinfo%v850name='${ncdf_v850name}'," >>${namelist} +echo " netcdfinfo%u700name='${ncdf_u700name}'," >>${namelist} +echo " netcdfinfo%v700name='${ncdf_v700name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%mslpname='${ncdf_mslpname}'," >>${namelist} +echo " netcdfinfo%usfcname='${ncdf_usfcname}'," >>${namelist} +echo " netcdfinfo%vsfcname='${ncdf_vsfcname}'," >>${namelist} +echo " netcdfinfo%u500name='${ncdf_u500name}'," >>${namelist} +echo " netcdfinfo%v500name='${ncdf_v500name}'," >>${namelist} +echo " netcdfinfo%tmean_300_500_name='${ncdf_tmean_300_500_name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z200name='${ncdf_z200name}'," >>${namelist} +echo " netcdfinfo%lmaskname='${ncdf_lmaskname}'," >>${namelist} +echo " netcdfinfo%z900name='${ncdf_z900name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z800name='${ncdf_z800name}'," >>${namelist} +echo " netcdfinfo%z750name='${ncdf_z750name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%z650name='${ncdf_z650name}'," >>${namelist} +echo " netcdfinfo%z600name='${ncdf_z600name}'," >>${namelist} +echo " netcdfinfo%z550name='${ncdf_z550name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z450name='${ncdf_z450name}'," >>${namelist} +echo " netcdfinfo%z400name='${ncdf_z400name}'," >>${namelist} +echo " netcdfinfo%z350name='${ncdf_z350name}'," >>${namelist} +echo " netcdfinfo%z300name='${ncdf_z300name}'," >>${namelist} +echo " netcdfinfo%time_name='${ncdf_time_name}'," >>${namelist} +echo " netcdfinfo%lon_name='${ncdf_lon_name}'," >>${namelist} +echo " netcdfinfo%lat_name='${ncdf_lat_name}'," >>${namelist} +echo " netcdfinfo%time_units='${ncdf_time_units}'/" >>${namelist} + +echo "&parmpreflist user_wants_to_track_zeta850='${user_wants_to_track_zeta850}'," >>${namelist} +echo " user_wants_to_track_zeta700='${user_wants_to_track_zeta700}'," >>${namelist} +echo " user_wants_to_track_wcirc850='${user_wants_to_track_wcirc850}'," >>${namelist} +echo " user_wants_to_track_wcirc700='${user_wants_to_track_wcirc700}'," >>${namelist} +echo " user_wants_to_track_gph850='${user_wants_to_track_gph850}'," >>${namelist} +echo " user_wants_to_track_gph700='${user_wants_to_track_gph700}'," >>${namelist} +echo " user_wants_to_track_mslp='${user_wants_to_track_mslp}'," >>${namelist} +echo " user_wants_to_track_wcircsfc='${user_wants_to_track_wcircsfc}'," >>${namelist} +echo " user_wants_to_track_zetasfc='${user_wants_to_track_zetasfc}'," >>${namelist} +echo " user_wants_to_track_thick500850='${user_wants_to_track_thick500850}'," >>${namelist} +echo " user_wants_to_track_thick200500='${user_wants_to_track_thick200500}'," >>${namelist} +echo " user_wants_to_track_thick200850='${user_wants_to_track_thick200850}'/" >>${namelist} + +echo "&verbose verb=3,verb_g2=0/" >>${namelist} + +export pgm=gettrk_gen_gfs +. prep_step + +export FORT11=${gribfile} +export FORT12=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} +export FORT14=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +if [ ${model} -eq 1 ]; then + export FORT15=${TRKDATA}/${cmodel}.genesis_leadtimes_120 +else + export FORT15=${FIXens_tracker}/${cmodel}.genesis_leadtimes_120 +fi + +export FORT31=${ixfile} + +if [ ${trkrtype} = 'tracker' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT61=${TRKDATA}/trak.${atcfout}.all.${stormenv}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${stormenv}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${stormenv}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${stormenv}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${stormenv}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${stormenv}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${stormenv}.${PDY}${cyc} + else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${PDY}${cyc} + fi +else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${regtype}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${regtype}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${regtype}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${regtype}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${regtype}.${PDY}${cyc} +fi + +if [ ${atcfname} = 'aear' ] +then + export FORT65=${TRKDATA}/trak.${atcfout}.initvitl.${PDY}${cyc} +fi + +if [ ${write_vit} = 'y' ] +then + export FORT67=${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} +fi + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${stormenv}.${PDY}${cyc} + else + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${PDY}${cyc} + fi +fi + +if [ ${STRUCTFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${stormenv}.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${stormenv}.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${stormenv}.${PDY}${cyc} + else + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${PDY}${cyc} + fi +fi + +if [ ${IKEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${stormenv}.${PDY}${cyc} + else + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${PDY}${cyc} + fi +fi + +if [ ${trkrtype} = 'midlat' -o ${trkrtype} = 'tcgen' ]; then + export FORT77=${TRKDATA}/trkrmask.${atcfout}.${regtype}.${PDY}${cyc} +fi + +msg="$pgm start for $atcfout at ${cyc}z" +postmsg "$jlogfile" "$msg" + +set +x +echo "+++ TIMING: BEFORE gettrk ---> `date`" +set -x + +ulimit -c unlimited + +${EXECens_tracker}/gettrk_gen_gfs <${namelist} +gettrk_rcc=$? + +set +x +echo "+++ TIMING: AFTER gettrk ---> `date`" +set -x + +#--------------------------------------------------------------# +# Now copy the output track files to different directories +#--------------------------------------------------------------# + +if [ ${gettrk_rcc} -eq 0 ]; then + set +x + echo " " + echo " -----------------------------------------------" + echo " NOW COPYING OUTPUT TRACK FILES TO COM " + echo " -----------------------------------------------" + echo " " + set -x + + if [ "$SENDCOM" = 'YES' ]; then + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} ${COMOUT}/ +# cp ${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} ${COMOUT}/ + + #if [ "$SENDDBN" = 'YES' ]; then + # $DBNROOT/bin/dbn_alert MODEL ENS_GENESIS $job ${COMOUT}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + #fi + fi + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" +else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running gettrk_gen_gfs, " + echo "!!! which is the program that actually gets the track." + echo "!!! Return code from gettrk_gen_gfs = ${gettrk_rcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "ERROR RUNNING gettrk_gen_gfs IN 2nd step" +fi diff --git a/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_gfs.sh_06032020 b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_gfs.sh_06032020 new file mode 100755 index 0000000000..34a4ec9909 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_gfs.sh_06032020 @@ -0,0 +1,2778 @@ +#!/bin/ksh +export PS4=' + extrkr_gen_gfs.sh line $LINENO: ' + +set +x +############################################################################## +echo " " +echo "------------------------------------------------" +echo "xxxx - Track vortices in model GRIB2 output" +echo "J.Peng---10-07-2014-----------------------------" +echo " " +echo "Current time is: `date`" +echo " " +############################################################################## +set -x + +loopnum=$1 +cmodel=$2 +pert=$3 +TRKDATA=$4 + +export gribver=2 +#init_flag=$5 +# USE_OPER_VITALS=NO +# USE_OPER_VITALS=INIT_ONLY +USE_OPER_VITALS=YES + +############################################################################## +# +# FLOW OF CONTROL +# +# 1. Define data directories and file names for the input model +# 2. Process input starting date/cycle information +# 3. Update TC Vitals file and select storms to be processed +# 4. Cut apart input GRIB files to select only the needed parms and hours +# 5. Execute the tracker +# 6. Copy the output track files to various locations +# +############################################################################## +msg="has begun for ${cmodel} ${pert} at ${cyc}z" +postmsg "$jlogfile" "$msg" +######################################## + +# This script runs the hurricane tracker using operational GRIB model output. +# This script makes sure that the data files exist, it then pulls all of the +# needed data records out of the various GRIB forecast files and puts them +# into one, consolidated GRIB file, and then runs a program that reads the TC +# Vitals records for the input day and updates the TC Vitals (if necessary). +# It then runs gettrk, which actually does the tracking. +# +# Environmental variable inputs needed for this scripts: +# PDY -- The date for data being processed, in YYYYMMDD format +# cyc -- The numbers for the cycle for data being processed (00, 06, 12, 18) +# cmodel -- Model being processed (gfs, mrf, ukmet, ecmwf, nam, ngm, ngps, +# gdas, gfdl, ens (ncep ensemble)) +# envir -- 'prod' or 'test' +# SENDCOM -- 'YES' or 'NO' +# stormenv -- This is only needed by the tracker run for the GFDL model. +# 'stormenv' contains the name/id that is used in the input +# grib file names. +# pert -- This is only needed by the tracker run for the NCEP ensemble. +# 'pert' contains the ensemble member id (e.g., n2, p4, etc.) +# which is used as part of the grib file names. +# +# For testing script interactively in non-production set following vars: +# archsyndir - Directory with syndir scripts/exec/fix +# + +qid=$$ +#----------------------------------------------# +# Get input date information # +#----------------------------------------------# + +#export PDY=${PDY:-$1} +#export cyc=${cyc:-$2} +#export cmodel=${cmodel:-$3} +export jobid=${jobid:-testjob} +export SENDCOM=${SENDCOM:-NO} +export PHASEFLAG=y +export WCORE_DEPTH=1.0 +#export PHASE_SCHEME=vtt +#export PHASE_SCHEME=cps +export PHASE_SCHEME=both +export STRUCTFLAG=n +export IKEFLAG=n + +if [ ! -d $TRKDATA ] +then + mkdir -p $TRKDATA +fi +cd $TRKDATA + +if [ ${#PDY} -eq 0 -o ${#cyc} -eq 0 -o ${#cmodel} -eq 0 ] +then + set +x + echo " " + echo "FATAL ERROR: Something wrong with input parameters." + echo "PDY= ${PDY}, cyc= ${cyc}, cmodel= ${cmodel}" + set -x + err_exit "BAD INPUTS AT LINE $LINENO IN extrkr_gen_gfs.sh" +else + set +x + echo " " + echo " #-----------------------------------------------------------------#" + echo " At beginning of tracker script, the following imported variables " + echo " are defined: " + echo " PDY ................................... $PDY" + echo " cyc ................................... $cyc" + echo " cmodel ................................ $cmodel" + echo " jobid ................................. $jobid" + echo " envir ................................. $envir" + echo " SENDCOM ............................... $SENDCOM" + echo " " + set -x +fi + +scc=`echo ${PDY} | cut -c1-2` +syy=`echo ${PDY} | cut -c3-4` +smm=`echo ${PDY} | cut -c5-6` +sdd=`echo ${PDY} | cut -c7-8` +shh=${cyc} +symd=`echo ${PDY} | cut -c3-8` +syyyy=`echo ${PDY} | cut -c1-4` +CENT=`echo ${PDY} | cut -c1-2` + +#export archsyndir=${archsyndir:-${COMROOT}/arch/prod/syndat} +archsyndir=${archsyndir:-${COMINsyn:?}} + +#----------------------------------------------------------------# +# +# --- Define data directories and data file names --- +# +# Convert the input model to lowercase letters and check to see +# if it's a valid model, and assign a model ID number to it. +# This model ID number is passed into the Fortran program to +# let the program know what set of forecast hours to use in the +# ifhours array. Also, set the directories for the operational +# input GRIB data files and create templates for the file names. +# While only 1 of these sets of directories and file name +# templates is used during a particular run of this script, +# "gfsvitdir" is used every time, because that is the directory +# that contains the error-checked TC vitals file that Steve Lord +# produces, and so it is included after the case statement. +# +# NOTE: The varible PDY is now defined within the J-Jobs that +# call this script. Therefore there is no reason to do this +# here. +# +# NOTE: The script that processes the ECMWF data defines PDY as +# the current day, and in this script we need PDY to be +# yesterday's date (for the ecmwf ONLY). So instead, the ecmwf +# script will pass the variable PDYm1 to this script, and in the +# case statement below we change that to PDY. +# +# NOTE: Do NOT try to standardize this script by changing all of +# these various data directories' variable names to be all the +# same, such as "datadir". As you'll see in the data cutting +# part of this script below, different methods are used to cut +# apart different models, thus it is important to know the +# difference between them.... +#----------------------------------------------------------------# + +cmodel=`echo ${cmodel} | tr "[A-Z]" "[a-z]"` +#---- tracking variable list------------------------------------ +user_wants_to_track_zeta850='y' +user_wants_to_track_zeta700='y' +user_wants_to_track_wcirc850='y' +user_wants_to_track_wcirc700='y' +user_wants_to_track_gph850='y' +user_wants_to_track_gph700='y' +user_wants_to_track_mslp='y' +user_wants_to_track_wcircsfc='n' +user_wants_to_track_zetasfc='n' +user_wants_to_track_thick500850='n' +user_wants_to_track_thick200850='n' +user_wants_to_track_thick200500='n' + +case ${cmodel} in + + gfs) set +x; echo " " ; + echo " ++ operational FV3-GFS chosen" ; + echo " "; set -x ; + gfsdir=${gfsdir:-${COMINgfs:?}} ; + gfsgfile=gfs.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=1 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=15 ; + atcffreq=$((vit_incr*100)) ; + +# trkrwbd=0 ; +# trkrebd=360 ; +# trkrnbd=90 ; +# trkrsbd=-90 ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="gfso" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="gfso" ;; + +# g2_jpdtn sets the variable that will be used as "JPDTN" for +# the call to getgb2, if gribver=2. jpdtn=1 for ens data, +# jpdtn=0 for deterministic data. + + ngps) set +x; echo " " ; + echo " ++ operational NAVGEM chosen" ; + echo " "; set -x ; + ngpsdir=${ngpsdir:-${DCOM:?}} ; + ngpsgfile=US058GMET-OPSbd2.NAVGEM ; + ngemgfile=-${PDY}${cyc}-NOAA-halfdeg.gr2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=7 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=29 ; + atcffreq=$((vit_incr*100)) ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="ngx " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="ngx" ;; + + cmc) set +x; echo " " ; + echo " ++ operational CMC chosen" ; + echo " "; set -x ; + cmcdir=${cmcdir:-${DCOM:?}} ; + cmcgfile=CMC_glb_latlon.24x.24_${PDY}${cyc}_P ; + cmcgfile2=_NCEP.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=15 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=39 ; + atcffreq=$((vit_incr*100)) ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="cmc " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="cmc" ;; + + ens) set +x; echo " " ; + echo " ++ operational ensemble member ${pert} chosen"; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + + ensdira=${ensdira:-${COMINgefs:?}/pgrb2ap5} ; + ensgfilea=ge${pert}.t${cyc}z.pgrb2a.0p50.f ; + ensdirb=${ensdirb:-${COMINgefs:?}/pgrb2bp5} ; + ensgfileb=ge${pert}.t${cyc}z.pgrb2b.0p50.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=10 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="a${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="a${pert_posneg}${pert_num}" ;; + +# g2_jpdtn sets the variable that will be used as "JPDTN" for +# the call to getgb2, if gribver=2. jpdtn=1 for ens data, +# jpdtn=0 for deterministic data. + + fens) set +x; echo " " ; + echo " ++ FNMOC ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + + fensdir=${fensdir:-${DCOM:?}} ; + fensgfile=ENSEMBLE.MET.fcst_et0${pert_num}. ; + gensgfile=.${PDY}${cyc} ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=22 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + +# pert_posneg=` echo "${pert}" | cut -c1-1` ; +# pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="n${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="n${pert_posneg}${pert_num}" ;; + + cens) set +x; echo " " ; + echo " ++ Canadian ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + ccedir=${ccedir:-${DCOM:?}} ; + ccegfile=${PDY}${cyc}_CMC_naefs_hr_latlon0p5x0p5_P ; + ccegfile2=_0${pert_num}.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=16 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + +# pert_posneg=` echo "${pert}" | cut -c1-1` ; +# pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="c${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="c${pert_posneg}${pert_num}" ;; + + *) msg="FATAL ERROR: Model $cmodel is not recognized." ; + echo "$msg"; postmsg $jlogfile "$msg" ; + err_exit "UNKNOWN cmodel IN extrkr_gen_gfs.sh";; + +esac + +if [ ${PHASEFLAG} = 'y' ]; then + +#J.Peng----2014-10-28--------- + if [ ${cmodel} = "gfs" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300|RH:500)" + + elif [ ${cmodel} = "ngps" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:400|HGT:300|TMP:500|TMP:400|TMP:300|RH:500)" + + elif [ ${cmodel} = "ens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300|RH:500)" + + elif [ ${cmodel} = "cens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:300|TMP:500|TMP:250|RH:500)" + + elif [ ${cmodel} = "cmc" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250|SPFH:500)" + + elif [ ${cmodel} = "fens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250|RH:500)" + + fi + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 U:200 V:850 V:700 V:500 V:200 10U:sfc 10V:sfc MSL:sfc GH:300 GH:400 GH:500 GH:925 T:300 T:400 T:500 R:500" + +else + wgrib_parmlist=" HGT:850 HGT:700 UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET " + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc" +fi + +#---------------------------------------------------------------# +# +# -------- TC Vitals processing -------- +# +# Check Steve Lord's operational tcvitals file to see if any +# vitals records were processed for this time by his system. +# If there were, then you'll find a file in /com/gfs/prod/gfs.yymmdd +# with the vitals in it. Also check the raw TC Vitals file in +# /com/arch/prod/syndat , since this may contain storms that Steve's +# system ignored (Steve's system will ignore all storms that are +# either over land or very close to land); We still want to track +# these inland storms, AS LONG AS THEY ARE NHC STORMS (don't +# bother trying to track inland storms that are outside of NHC's +# domain of responsibility -- we don't need that info). +# UPDATE 5/12/98 MARCHOK: The script is updated so that for the +# ensemble models, the gfs directory is checked for the error- +# checked vitals file, while for the regional models, the +# nam directory is checked for that file. +# UPDATE 3/27/09 MARCHOK: The SREF is run at off-synoptic times +# (03,09,15,21Z). There are no tcvitals issued at these offtimes, +# so the updating of the "old" tcvitals is critical for running +# the tracker on SREF. For updating the old tcvitals for SREF, +# we need to look 3h back, not 6h as for the other models that +# run at synoptic times. +#--------------------------------------------------------------# + +old_ymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +old_4ymd=` echo ${old_ymdh} | cut -c1-8` +old_ymd=` echo ${old_ymdh} | cut -c3-8` +old_hh=` echo ${old_ymdh} | cut -c9-10` +old_str="${old_ymd} ${old_hh}00" + +future_ymdh=` ${NDATE:?} ${vit_incr} ${PDY}${cyc}` +future_4ymd=` echo ${future_ymdh} | cut -c1-8` +future_ymd=` echo ${future_ymdh} | cut -c3-8` +future_hh=` echo ${future_ymdh} | cut -c9-10` +future_str="${future_ymd} ${future_hh}00" + +if [ ${modtyp} = 'global' ] +then +# synvitdir=${COMROOT}/gfs/prod/gfs.${PDY} + synvitdir=${COMINgfs:?} + synvitfile=gfs.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/gfs/prod/gfs.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=gfs.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/gfs/prod/gfs.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=gfs.t${future_hh}z.syndata.tcvitals.tm00 +else +# synvitdir=${COMROOT}/nam/prod/nam.${PDY} + synvitdir=${COMINnam:?} + synvitfile=nam.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/nam/prod/nam.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=nam.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/nam/prod/nam.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=nam.t${future_hh}z.syndata.tcvitals.tm00 +fi + +set +x +echo " " +echo " -----------------------------" +echo " " +echo " Now sorting and updating the TC Vitals file. Please wait...." +echo " " +set -x + +current_str="${symd} ${cyc}00" + +if [ -s ${synvitdir}/${synvitfile} -o\ + -s ${synvitold_dir}/${synvitold_file} -o\ + -s ${synvitfuture_dir}/${synvitfuture_file} ] +then + grep "${old_str}" ${synvitold_dir}/${synvitold_file} \ + >${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${synvitdir}/${synvitfile} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${future_str}" ${synvitfuture_dir}/${synvitfuture_file} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} +else + set +x + echo " " + echo " There is no (synthetic) TC vitals file for ${cyc}z in ${synvitdir}," + echo " nor is there a TC vitals file for ${old_hh}z in ${synvitold_dir}." + echo " nor is there a TC vitals file for ${future_hh}z in ${synvitfuture_dir}," + echo " Checking the raw TC Vitals file ....." + echo " " + set -x +fi + +# Take the vitals from Steve Lord's /com/gfs/prod tcvitals file, +# and cat them with the NHC-only vitals from the raw, original +# /com/arch/prod/synda_tcvitals file. Do this because the nwprod +# tcvitals file is the original tcvitals file, and Steve runs a +# program that ignores the vitals for a storm that's over land or +# even just too close to land, and for tracking purposes for the +# US regional models, we need these locations. Only include these +# "inland" storm vitals for NHC (we're not going to track inland +# storms that are outside of NHC's domain of responsibility -- we +# don't need that info). +# UPDATE 5/12/98 MARCHOK: awk logic is added to screen NHC +# vitals such as "91L NAMELESS" or "89E NAMELESS", since TPC +# does not want tracks for such storms. + +grep "${old_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${current_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${future_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-03---for genesis tc vitals-------------------- +#export COMINgenvit=/ensemble/save/Jiayi.Peng/genesis_vital_${syyyy} +#grep "${old_str}" ${COMINgenvit}/gen_tc.vitals.gfs.gfso.2012 | \ +# grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ +# >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +if [ ${cmodel} = 'ens' -o ${cmodel} = 'gfs' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.gfs.gfso.${syyyy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +elif [ ${cmodel} = 'cens' -o ${cmodel} = 'cmc' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.cmc.cmc.${syyyy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +elif [ ${cmodel} = 'fens' -o ${cmodel} = 'ngps' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.ngps.ngx.${syyyy} | \ +grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +elif [ ${cmodel} = 'eens' -o ${cmodel} = 'ecmwf' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.ecmwf.emx.${syyyy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +fi + +#PRODTEST# Use the next couple lines to test the tracker on the SP. +#PRODTEST# These next couple lines use data from a test TC Vitals file that +#PRODTEST# I generate. When you are ready to test this system, call me and +#PRODTEST# I'll create one for the current day, and then uncomment the next +#PRODTEST# couple lines in order to access the test vitals file. +# +#ttrkdir=/ensemble/save/wx20tm/trak/prod/data +#ttrkdir=/ensemble/save/wx20tm/trak/para/scripts +#grep "${current_str}" ${ttrkdir}/tcvit.01l >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + + +# IMPORTANT: When "cat-ing" these files, make sure that the vitals +# files from the "raw" TC vitals files are first in order and Steve's +# TC vitals files second. This is because Steve's vitals file has +# been error-checked, so if we have a duplicate tc vitals record in +# these 2 files (very likely), program supvit.x below will +# only take the last vitals record listed for a particular storm in +# the vitals file (all previous duplicates are ignored, and Steve's +# error-checked vitals records are kept). + +cat ${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} ${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} \ + >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# If we are doing the processing for the GFDL model, then we want +# to further cut down on which vitals we allow into this run of the +# tracker. The reason is that this program will be called from +# each individual run for a storm, so the grib files will be +# specific to each storm. So if 4 storms are being run at a +# particular cycle, then this script is run 4 separate times from +# within the GFDL_POST job. + +#if [ ${cmodel} = 'gfdl' ]; then +## grep -i ${stormid} ${COMIN}/${ATCFNAME}.vitals.${syy}${smm}${sdd}${shh} >${TRKDATA}/tmpvit +## mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#echo " " +#echo "!!! ERROR: TAKE THE COMMENTS OUT OF HERE BEFORE IMPLEMENTATION AAAA" +#echo "!!! AND TAKE OUT THESE NEXT 2 LINES AS WELL............................" +#echo " " +#grep -i ${stormid} ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/tmpvit +#mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#fi + +#--------------------------------------------------------------# +# Now run a fortran program that will read all the TC vitals +# records for the current dtg and the dtg from 6h ago, and +# sort out any duplicates. If the program finds a storm that +# was included in the vitals file 6h ago but not for the current +# dtg, this program updates the 6h-old first guess position +# and puts these updated records as well as the records from +# the current dtg into a temporary vitals file. It is this +# temporary vitals file that is then used as the input for the +# tracking program. +#--------------------------------------------------------------# + +oldymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +oldyy=`echo ${oldymdh} | cut -c3-4` +oldmm=`echo ${oldymdh} | cut -c5-6` +olddd=`echo ${oldymdh} | cut -c7-8` +oldhh=`echo ${oldymdh} | cut -c9-10` +oldymd=${oldyy}${oldmm}${olddd} + +futureymdh=` ${NDATE:?} 6 ${PDY}${cyc}` +futureyy=`echo ${futureymdh} | cut -c3-4` +futuremm=`echo ${futureymdh} | cut -c5-6` +futuredd=`echo ${futureymdh} | cut -c7-8` +futurehh=`echo ${futureymdh} | cut -c9-10` +futureymd=${futureyy}${futuremm}${futuredd} + +echo "&datenowin dnow%yy=${syy}, dnow%mm=${smm}," >${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&dateoldin dold%yy=${oldyy}, dold%mm=${oldmm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dold%dd=${olddd}, dold%hh=${oldhh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&datefuturein dfuture%yy=${futureyy}, dfuture%mm=${futuremm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dfuture%dd=${futuredd}, dfuture%hh=${futurehh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&hourinfo vit_hr_incr=${vit_incr}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period." + echo "!!! File ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} is empty." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi + +fi + +# For tcgen cases, filter to use only vitals from the ocean +# basin of interest.... +#J.Peng----2012-04-13------------------------------------- +#if [ ${trkrtype} = 'tcgen' ] +# then + +# if [ ${numvitrecs} -gt 0 ] +# then + +# fullvitfile=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# cp $fullvitfile ${TRKDATA}/vitals.all_basins.${atcfout}.${PDY}${cyc} +# basin=` echo $regtype | cut -c1-2` + +# if [ ${basin} = 'al' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "L") print $0}' | +# >${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'ep' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "E") print $0}' | +# >${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'wp' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "W") print $0}' | +# >${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi + +# cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# fi + +#fi +#J.Peng----2012-04-13------------------------------------- + +# - - - - - - - - - - - - - +# Before running the program to read, sort and update the vitals, +# first run the vitals through some awk logic, the purpose of +# which is to convert all the 2-digit years into 4-digit years. +# Beginning 4/21/99, NHC and JTWC will begin sending the vitals +# with 4-digit years, however it is unknown when other ensemble +# forecasting centers will begin using 4-digit years, thus we +# need the following logic to ensure that all the vitals going +# into supvit.f have uniform, 4-digit years in their records. +# +# 1/8/2000: sed code added by Tim Marchok due to the fact that +# some of the vitals were getting past the syndata/qctropcy +# error-checking with a colon in them; the colon appeared +# in the character immediately to the left of the date, which +# was messing up the "(length($4) == 8)" statement logic. +# - - - - - - - - - - - - - + +sed -e "s/\:/ /g" ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} > ${TRKDATA}/tempvit +mv ${TRKDATA}/tempvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +awk ' +{ + yycheck = substr($0,20,2) + if ((yycheck == 20 || yycheck == 19) && (length($4) == 8)) { + printf ("%s\n",$0) + } + else { + if (yycheck >= 0 && yycheck <= 50) { + printf ("%s20%s\n",substr($0,1,19),substr($0,20)) + } + else { + printf ("%s19%s\n",substr($0,1,19),substr($0,20)) + } + } +} ' ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 + +mv ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +#Added by J.Peng----2012-04-12---------------------------------------------- +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +#----------------------------------------------------------------- +if [ ${numvitrecs} -gt 0 ] +then + export pgm=supvit_g2 + . prep_step + + # Input file + export FORT31=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_g2 <${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + suvrcc=$? + + if [ ${suvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit.x, " + echo "!!! which is the program that updates the TC Vitals file." + echo "!!! Return code from supvit.x = ${suvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "ERROR RUNNING supvit_g2 IN extrkr_gen_gfs LINE $LINENO" + fi + +else +#Added by J.Peng----2012-04-12---------------------------------------------- + rm -f ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + touch ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#----------------------------------------------------------------- +# In this section, check to see if the user requested the use of +# operational TC vitals records for the initial time only. This +# option might be used for a retrospective medium range forecast +# in which the user wants to initialize with the storms that are +# currently there, but then let the model do its own thing for +# the next 10 or 14 days.... + + +if [ ${USE_OPER_VITALS} = 'INIT_ONLY' ]; then + + if [ ${init_flag} = 'yes' ]; then + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are currently at." + echo " " + set -x + else + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are now *PAST*." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + fi + +elif [ ${USE_OPER_VITALS} = 'NO' ]; then + + set +x + echo " " + echo "NOTE: User has requested that historical vitals not be used...." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#------------------------------------------------------------------# +# Now select all storms to be processed, that is, process every +# storm that's listed in the updated vitals file for the current +# forecast hour. If there are no storms for the current time, +# then exit. +#------------------------------------------------------------------# + +numvitrecs=`cat ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period " + echo "!!! in the UPDATED vitals file." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi +fi + +set +x +echo " " +echo " *--------------------------------*" +echo " | STORM SELECTION |" +echo " *--------------------------------*" +echo " " +set -x + +ict=1 +while [ $ict -le 15 ] +do + stormflag[${ict}]=3 + let ict=ict+1 +done + +dtg_current="${symd} ${cyc}00" +stormmax=` grep "${dtg_current}" ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +if [ ${stormmax} -gt 15 ] +then + stormmax=15 +fi + +sct=1 +while [ ${sct} -le ${stormmax} ] +do + stormflag[${sct}]=1 + let sct=sct+1 +done + + +#---------------------------------------------------------------# +# +# -------- "Genesis" Vitals processing -------- +# +# May 2006: This entire genesis tracking system is being +# upgraded to more comprehensively track and categorize storms. +# One thing that has been missing from the tracking system is +# the ability to keep track of storms from one analysis cycle +# to the next. That is, the current system has been very +# effective at tracking systems within a forecast, but we have +# no methods in place for keeping track of storms across +# difference initial times. For example, if we are running +# the tracker on today's 00z GFS analysis, we will get a +# position for various storms at the analysis time. But then +# if we go ahead and run again at 06z, we have no way of +# telling the tracker that we know about the 00z position of +# this storm. We now address that problem by creating +# "genesis" vitals, that is, when a storm is found at an +# analysis time, we not only produce "atcfunix" output to +# detail the track & intensity of a found storm, but we also +# produce a vitals record that will be used for the next +# run of the tracker script. These "genesis vitals" records +# will be of the format: +# +# YYYYMMDDHH_AAAH_LLLLX_TYP +# +# Where: +# +# YYYYMMDDHH = Date the storm was FIRST identified +# by the tracker. +# AAA = Abs(Latitude) * 10; integer value +# H = 'N' for norther hem, 'S' for southern hem +# LLLL = Abs(Longitude) * 10; integer value +# X = 'E' for eastern hem, 'W' for western hem +# TYP = Tropical cyclone storm id if this is a +# tropical cyclone (e.g., "12L", or "09W", etc). +# If this is one that the tracker instead "Found +# On the Fly (FOF)", we simply put those three +# "FOF" characters in there. +#J.Peng----2012-04-12-------- +genvitfile=${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} +touch ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + +if [ -f $genvitfile ]; then + d6ago_ymdh=` ${NDATE:?} -6 ${PDY}${cyc}` + d6ago_4ymd=` echo ${d6ago_ymdh} | cut -c1-8` + d6ago_ymd=` echo ${d6ago_ymdh} | cut -c3-8` + d6ago_hh=` echo ${d6ago_ymdh} | cut -c9-10` + d6ago_str="${d6ago_ymd} ${d6ago_hh}00" + + d6ahead_ymdh=` ${NDATE:?} 6 ${PDY}${cyc}` + d6ahead_4ymd=` echo ${d6ahead_ymdh} | cut -c1-8` + d6ahead_ymd=` echo ${d6ahead_ymdh} | cut -c3-8` + d6ahead_hh=` echo ${d6ahead_ymdh} | cut -c9-10` + d6ahead_str="${d6ahead_ymd} ${d6ahead_hh}00" + + set +x + echo " " + echo " d6ago_str= --->${d6ago_str}<---" + echo " current_str= --->${current_str}<---" + echo " d6ahead_str= --->${d6ahead_str}<---" + echo " " + #echo " Listing and contents of ${genvitfile} follow " + #echo " for the times 6h ago, current and 6h ahead:" + #echo " " + set -x + + #J.Peng----2012-04-11----- + ##ls -la ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + ##cat ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + #ls -la ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #cat ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + + grep "${d6ago_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${d6ahead_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + num_gen_vits=`cat ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} | wc -l` +else + echo "WARNING: Genesis Vitals from previous run(s) not found!" +fi + +echo "&datenowin dnow%yy=${syyyy}, dnow%mm=${smm}," >${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6agoin d6ago%yy=${syyyy6}, d6ago%mm=${smm6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ago%dd=${sdd6}, d6ago%hh=${shh6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6aheadin d6ahead%yy=${syyyyp6}, d6ahead%mm=${smmp6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ahead%dd=${sddp6}, d6ahead%hh=${shhp6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-04---do not need calculate this ---------- +num_gen_vits=0 + +if [ ${num_gen_vits} -gt 0 ] +then + export pgm=supvit_gen + . prep_step + + # Input file + export FORT31=${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_gen <${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + sgvrcc=$? + + if [ ${sgvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit_gen, " + echo "!!! which is the program that updates the genesis vitals file." + echo "!!! Return code from supvit_gen = ${sgvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "ERROR RUNNING SUPVIT_GEN IN extrkr_gen_gfs.sh LINE $LINENO" + fi + +else + rm -f ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + touch ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +fi + + +#-----------------------------------------------------------------# +# +# ------ CUT APART INPUT GRIB FILES ------- +# +# For the selected model, cut apart the GRIB input files in order +# to pull out only the variables that we need for the tracker. +# Put these selected variables from all forecast hours into 1 big +# GRIB file that we'll use as input for the tracker. +# +# The wgrib utility (/nwprod/util/exec/wgrib) is used to cut out +# the needed parms for the GFS, MRF, GDAS, UKMET and NOGAPS files. +# The utility /nwprod/util/exec/copygb is used to interpolate the +# NGM (polar stereographic) and NAM (Lambert Conformal) data from +# their grids onto lat/lon grids. Note that while the lat/lon +# grid that I specify overlaps into areas that don't have any data +# on the original grid, Mark Iredell wrote the copygb software so +# that it will mask such "no-data" points with a bitmap (just be +# sure to check the lbms in your fortran program after getgb). +#-----------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------" +echo " NOW CUTTING APART INPUT GRIB FILES TO " +echo " CREATE 1 BIG GRIB INPUT FILE " +echo " -----------------------------------------" +echo " " +set -x + +regflag=`grep NHC ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +# ------------------------------ +# Process FNMOC Ensemble, if selected +# ------------------------------ + +if [ ${model} -eq 22 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ] + then + rm ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + fensfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + + let attempts=1 + while [ $attempts -le 40 ]; do + if [ -s $fensfile ]; then + break + else + sleep 60 + let attempts=attempts+1 + fi + done + if [ $attempts -gt 40 ] && [ ! -s $fensfile ]; then + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "$fensfile" >> ${DATA}/missing_fens.txt + exit + else + err_exit "$fensfile still not available after waiting 40 minutes" + fi + fi + + #${WGRIB2:?} -set_byte 1 11 1 -s $fensfile >fens.ix # set_byte resets local table to 1 to remove error messages + gfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} +# rm $tavefile $zfile + +#J.Peng----2014-10-15---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2014-10-15---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ixfile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + +fi + +# ------------------------------ +# Process GFS, if selected +# ------------------------------ + +if [ ${model} -eq 1 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gfsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc} + fi + + rm ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f* + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gfsgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gfs wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gfsdir}/${gfsgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GFS File missing: ${gfsdir}/${gfsgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GFS FILE IN extrkr_gen_gfs.sh: ${gfsdir}/${gfsgfile}${fhour}" + fi + + gfile=${gfsdir}/${gfsgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + + gfs_master_file=${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + gfs_cat_file=${TRKDATA}/gfsgribfile.${PDY}${cyc} + cat ${gfs_master_file} >>${gfs_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gfsgribfile.${PDY}${cyc} ${TRKDATA}/gfsixfile.${PDY}${cyc} + export err=$?; err_chk + +# Now run through the work to get data for phase checking.... +# -------------------------------------------- + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "BEGIN: 300-500hPa T-mean and 200-850hPa U-shear" + echo " for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + +#J.Peng----2012-04-18---added for U-shear----- + gparm=33 + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-04-18---added for U-shear----- + + set +x + echo " " + echo "END: 300-500hPa T-mean and 200-850hPa U-shear" + echo " for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + cat ${catfile} >>${ffile} + + fi + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gfsixfile.${PDY}${cyc} + +fi + +# -------------------------------------------------- +# Process NCEP Ensemble perturbation, if selected +# -------------------------------------------------- + +if [ ${model} -eq 10 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ensdira}/${ensgfilea}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GEFS ${PERT} File missing: ${ensdira}/${ensgfilea}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GEFS FILE IN TRACKER SCRIPT: ${ensdira}/${ensgfilea}${fhour}" + fi + + gfile=${TRKDATA}/${ensgfilea}${fhour} + cat ${ensdira}/${ensgfilea}${fhour} ${ensdirb}/${ensgfileb}${fhour} > $gfile + + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} + cat ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} >> ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/ens${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in T-mean/U-shear for pert= $pert and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + +#J.Peng----2014-10-15---added for U-shear----- + ffile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + + gparm=33 + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2014-10-15---added for U-shear----- + + set +x + echo " " + echo "Date in T-mean/U-shear for pert= $pert and fhour= $fhour before = `date`" + echo " " + set -x + + done + fi + fi + + gfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------ +# Process NAVGEM, if selected +# ------------------------------ + +if [ ${model} -eq 7 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} ] + then + set +x + echo " " + echo "FATAL ERROR: NAVGEM File missing ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" >> ${DATA}/missing_ngps.txt + exit + else + err_exit "MISSING NAVGEM FILE at $0:$LINENO" + fi + fi + + gfile=${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + + done + +# Now run through the work to get data for phase checking.... + ${GRB2INDEX:?} ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ${TRKDATA}/ngpsixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk + +fi + +# ------------------------------ +# Process CMC deterministic , if selected +# ------------------------------ + +if [ ${model} -eq 15 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/cmcgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/cmcgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} ] + then + set +x + echo " " + echo "FATAL ERROR: CMC File missing ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + fi + + gfile=${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + done + +# J.Peng--04-05-2017--changing grid from 180E to 0E------------ +# wgrib2 junk -small_grib 0:359.76 -90:90 junk2 +# J.Peng--10-15-2019----uncomment the next 4 lines -------- + cp ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/junk + rm -f ${TRKDATA}/cmcgribfile.${PDY}${cyc} + ${WGRIB2} ${TRKDATA}/junk -small_grib 0:359.76 -90:90 ${TRKDATA}/junk2 + cp ${TRKDATA}/junk2 ${TRKDATA}/cmcgribfile.${PDY}${cyc} + +# Now run through the work to get data for phase checking.... + ${GRB2INDEX:?} ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/cmcixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- +# +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + gparmq=7 + gparmt=11 + + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/rh_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparmq=${gparmq}," >>${namelist} + echo " iparmt=${gparmt}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT16=${FIXens_tracker}/cmc_rh_levs.txt + + # Output file + export FORT51=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/rhum_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to rhum_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -rhum_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + rhfile=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + cat ${rhfile} >>${catfile} +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk + +fi + +# ------------------------------------------------------ +# Process Canadian Ensemble perturbation, if selected +# ------------------------------------------------------ + +if [ ${model} -eq 16 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ccedir}/${ccegfile}${fhour}${ccegfile2} ] + then + set +x + echo " " + echo "FATAL ERROR: CANADIAN ENSEMBLE ${PERT} missing: ${ccegfile}${fhour}${ccegfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING CMC Ens FILE IN TRACKER SCRIPT: ${ccegfile}${fhour}${ccegfile2}" + fi + + gfile=${ccedir}/${ccegfile}${fhour}${ccegfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk +# Process the cyclone phase variables, if requested + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}, iparm=${gparm}/" >${namelist} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} +# rm $tavefile $zfile + +#J.Peng----2014-10-22----added for U-shear----- + ffile=${gfile} + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + + gparm=33 + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2014-10-22----added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + gribfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +fi + + +#------------------------------------------------------------------------# +# Now run the tracker # +#------------------------------------------------------------------------# +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW EXECUTING TRACKER......" +echo " -----------------------------------------------" +echo " " +set -x + +ist=1 +while [ $ist -le 15 ] +do + if [ ${stormflag[${ist}]} -ne 1 ] + then + set +x; echo "Storm number $ist NOT selected for processing"; set -x + else + set +x; echo "Storm number $ist IS selected for processing...."; set -x + fi + let ist=ist+1 +done + +if [ ! -s ${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} ] +then + for fhr in $fcsthrs; do + if [ $fhr -ne 99 ]; then + last_fcst_hour=$fhr + fi + done + echo ${last_fcst_hour} >${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} +fi + +namelist=${TRKDATA}/input.${atcfout}.${PDY}${cyc} +ATCFNAME=` echo "${atcfname}" | tr '[a-z]' '[A-Z]'` + +export atcfymdh=${scc}${syy}${smm}${sdd}${shh} + +#------07-26-2019----creating the fix-file for GFS tracking ----- +if [ ${model} -eq 1 ]; then +. prep_step + +# Input files +namelist_2_fix=${TRKDATA}/namelist_2_fix_input.${PDY}${cyc} +echo "&timein maxhrs=${fcstlen}," >${namelist_2_fix} +echo " dthrs=${vit_incr}/" >>${namelist_2_fix} + +# Output file +export FORT10=${TRKDATA}/${cmodel}.genesis_leadtimes_120 + +${EXECens_tracker}/leadtime <${namelist_2_fix} +rcc=$? + +if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to leadtime at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "leadtime - ERROR AT extrkr_gen_gfs.sh LINE $LINENO" +fi + +fi +#------07-26-2019----creating the fix-file for GFS tracking ----- + +#contour_interval=100.0 +#write_vit=y +#want_oci=.TRUE. + +echo "&datein inp%bcc=${scc},inp%byy=${syy},inp%bmm=${smm}," >${namelist} +echo " inp%bdd=${sdd},inp%bhh=${shh},inp%model=${model}," >>${namelist} +echo " inp%modtyp='${modtyp}'," >>${namelist} +echo " inp%lt_units='${lead_time_units}'," >>${namelist} +echo " inp%file_seq='${file_sequence}'," >>${namelist} +echo " inp%nesttyp='${nest_type}'/" >>${namelist} + +echo "&atcfinfo atcfnum=${atcfnum},atcfname='${ATCFNAME}'," >>${namelist} +echo " atcfymdh=${atcfymdh},atcffreq=${atcffreq}/" >>${namelist} + +echo "&trackerinfo trkrinfo%westbd=${trkrwbd}," >>${namelist} +echo " trkrinfo%eastbd=${trkrebd}," >>${namelist} +echo " trkrinfo%northbd=${trkrnbd}," >>${namelist} +echo " trkrinfo%southbd=${trkrsbd}," >>${namelist} +echo " trkrinfo%type='${trkrtype}'," >>${namelist} +echo " trkrinfo%mslpthresh=${mslpthresh}," >>${namelist} +echo " trkrinfo%use_backup_mslp_grad_check='${use_backup_mslp_grad_check}'," >>${namelist} +echo " trkrinfo%max_mslp_850=${max_mslp_850}," >>${namelist} +echo " trkrinfo%v850thresh=${v850thresh}," >>${namelist} +echo " trkrinfo%use_backup_850_vt_check='${use_backup_850_vt_check}'," >>${namelist} +echo " trkrinfo%gridtype='${modtyp}'," >>${namelist} +echo " trkrinfo%enable_timing=1," >>${namelist} +echo " trkrinfo%contint=${contour_interval}," >>${namelist} +echo " trkrinfo%want_oci=${want_oci}," >>${namelist} +echo " trkrinfo%out_vit='${write_vit}'," >>${namelist} +echo " trkrinfo%use_land_mask='${use_land_mask}'," >>${namelist} +echo " trkrinfo%inp_data_type='${inp_data_type}'," >>${namelist} +echo " trkrinfo%gribver=${gribver}," >>${namelist} +echo " trkrinfo%g2_jpdtn=${g2_jpdtn}," >>${namelist} +echo " trkrinfo%g2_mslp_parm_id=${g2_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_mslp_parm_id=${g1_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_typ=${g1_sfcwind_lev_typ}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_val=${g1_sfcwind_lev_val}/" >>${namelist} + +echo "&phaseinfo phaseflag='${PHASEFLAG}'," >>${namelist} +echo " phasescheme='${PHASE_SCHEME}'," >>${namelist} +echo " wcore_depth=${WCORE_DEPTH}/" >>${namelist} + +echo "&structinfo structflag='${STRUCTFLAG}'," >>${namelist} +echo " ikeflag='${IKEFLAG}'/" >>${namelist} + +echo "&fnameinfo gmodname='${atcfname}'," >>${namelist} +echo " rundescr='${rundescr}'," >>${namelist} +echo " atcfdescr='${atcfdescr}'/" >>${namelist} + +echo "&waitinfo use_waitfor='n'," >>${namelist} +echo " wait_min_age=10," >>${namelist} +echo " wait_min_size=100," >>${namelist} +echo " wait_max_wait=1800," >>${namelist} +echo " wait_sleeptime=5," >>${namelist} +echo " per_fcst_command=''/" >>${namelist} + +echo "&netcdflist netcdfinfo%num_netcdf_vars=${ncdf_num_netcdf_vars}," >>${namelist} +echo " netcdfinfo%netcdf_filename='${netcdffile}'," >>${namelist} +echo " netcdfinfo%rv850name='${ncdf_rv850name}'," >>${namelist} +echo " netcdfinfo%rv700name='${ncdf_rv700name}'," >>${namelist} +echo " netcdfinfo%u850name='${ncdf_u850name}'," >>${namelist} +echo " netcdfinfo%v850name='${ncdf_v850name}'," >>${namelist} +echo " netcdfinfo%u700name='${ncdf_u700name}'," >>${namelist} +echo " netcdfinfo%v700name='${ncdf_v700name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%mslpname='${ncdf_mslpname}'," >>${namelist} +echo " netcdfinfo%usfcname='${ncdf_usfcname}'," >>${namelist} +echo " netcdfinfo%vsfcname='${ncdf_vsfcname}'," >>${namelist} +echo " netcdfinfo%u500name='${ncdf_u500name}'," >>${namelist} +echo " netcdfinfo%v500name='${ncdf_v500name}'," >>${namelist} +echo " netcdfinfo%tmean_300_500_name='${ncdf_tmean_300_500_name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z200name='${ncdf_z200name}'," >>${namelist} +echo " netcdfinfo%lmaskname='${ncdf_lmaskname}'," >>${namelist} +echo " netcdfinfo%z900name='${ncdf_z900name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z800name='${ncdf_z800name}'," >>${namelist} +echo " netcdfinfo%z750name='${ncdf_z750name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%z650name='${ncdf_z650name}'," >>${namelist} +echo " netcdfinfo%z600name='${ncdf_z600name}'," >>${namelist} +echo " netcdfinfo%z550name='${ncdf_z550name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z450name='${ncdf_z450name}'," >>${namelist} +echo " netcdfinfo%z400name='${ncdf_z400name}'," >>${namelist} +echo " netcdfinfo%z350name='${ncdf_z350name}'," >>${namelist} +echo " netcdfinfo%z300name='${ncdf_z300name}'," >>${namelist} +echo " netcdfinfo%time_name='${ncdf_time_name}'," >>${namelist} +echo " netcdfinfo%lon_name='${ncdf_lon_name}'," >>${namelist} +echo " netcdfinfo%lat_name='${ncdf_lat_name}'," >>${namelist} +echo " netcdfinfo%time_units='${ncdf_time_units}'/" >>${namelist} + +echo "&parmpreflist user_wants_to_track_zeta850='${user_wants_to_track_zeta850}'," >>${namelist} +echo " user_wants_to_track_zeta700='${user_wants_to_track_zeta700}'," >>${namelist} +echo " user_wants_to_track_wcirc850='${user_wants_to_track_wcirc850}'," >>${namelist} +echo " user_wants_to_track_wcirc700='${user_wants_to_track_wcirc700}'," >>${namelist} +echo " user_wants_to_track_gph850='${user_wants_to_track_gph850}'," >>${namelist} +echo " user_wants_to_track_gph700='${user_wants_to_track_gph700}'," >>${namelist} +echo " user_wants_to_track_mslp='${user_wants_to_track_mslp}'," >>${namelist} +echo " user_wants_to_track_wcircsfc='${user_wants_to_track_wcircsfc}'," >>${namelist} +echo " user_wants_to_track_zetasfc='${user_wants_to_track_zetasfc}'," >>${namelist} +echo " user_wants_to_track_thick500850='${user_wants_to_track_thick500850}'," >>${namelist} +echo " user_wants_to_track_thick200500='${user_wants_to_track_thick200500}'," >>${namelist} +echo " user_wants_to_track_thick200850='${user_wants_to_track_thick200850}'/" >>${namelist} + +echo "&verbose verb=3,verb_g2=0/" >>${namelist} + +export pgm=gettrk_gen_gfs +. prep_step + +export FORT11=${gribfile} +export FORT12=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} +export FORT14=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +if [ ${model} -eq 1 ]; then + export FORT15=${TRKDATA}/${cmodel}.genesis_leadtimes_120 +else + export FORT15=${FIXens_tracker}/${cmodel}.genesis_leadtimes_120 +fi + +export FORT31=${ixfile} + +if [ ${trkrtype} = 'tracker' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT61=${TRKDATA}/trak.${atcfout}.all.${stormenv}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${stormenv}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${stormenv}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${stormenv}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${stormenv}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${stormenv}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${stormenv}.${PDY}${cyc} + else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${PDY}${cyc} + fi +else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${regtype}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${regtype}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${regtype}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${regtype}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${regtype}.${PDY}${cyc} +fi + +if [ ${atcfname} = 'aear' ] +then + export FORT65=${TRKDATA}/trak.${atcfout}.initvitl.${PDY}${cyc} +fi + +if [ ${write_vit} = 'y' ] +then + export FORT67=${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} +fi + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${stormenv}.${PDY}${cyc} + else + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${PDY}${cyc} + fi +fi + +if [ ${STRUCTFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${stormenv}.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${stormenv}.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${stormenv}.${PDY}${cyc} + else + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${PDY}${cyc} + fi +fi + +if [ ${IKEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${stormenv}.${PDY}${cyc} + else + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${PDY}${cyc} + fi +fi + +if [ ${trkrtype} = 'midlat' -o ${trkrtype} = 'tcgen' ]; then + export FORT77=${TRKDATA}/trkrmask.${atcfout}.${regtype}.${PDY}${cyc} +fi + +msg="$pgm start for $atcfout at ${cyc}z" +postmsg "$jlogfile" "$msg" + +set +x +echo "+++ TIMING: BEFORE gettrk ---> `date`" +set -x + +ulimit -c unlimited + +${EXECens_tracker}/gettrk_gen_gfs <${namelist} +gettrk_rcc=$? + +set +x +echo "+++ TIMING: AFTER gettrk ---> `date`" +set -x + +#--------------------------------------------------------------# +# Now copy the output track files to different directories +#--------------------------------------------------------------# + +if [ ${gettrk_rcc} -eq 0 ]; then + set +x + echo " " + echo " -----------------------------------------------" + echo " NOW COPYING OUTPUT TRACK FILES TO COM " + echo " -----------------------------------------------" + echo " " + set -x + + if [ "$SENDCOM" = 'YES' ]; then + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} ${COMOUT}/ +# cp ${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} ${COMOUT}/ + + #if [ "$SENDDBN" = 'YES' ]; then + # $DBNROOT/bin/dbn_alert MODEL ENS_GENESIS $job ${COMOUT}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + #fi + fi + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" +else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running gettrk_gen_gfs, " + echo "!!! which is the program that actually gets the track." + echo "!!! Return code from gettrk_gen_gfs = ${gettrk_rcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "ERROR RUNNING gettrk_gen_gfs IN 2nd step" +fi diff --git a/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_gfs.sh_10152019 b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_gfs.sh_10152019 new file mode 100755 index 0000000000..fc6a65cc0d --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_gfs.sh_10152019 @@ -0,0 +1,2777 @@ +#!/bin/ksh +export PS4=' + extrkr_gen_gfs.sh line $LINENO: ' + +set +x +############################################################################## +echo " " +echo "------------------------------------------------" +echo "xxxx - Track vortices in model GRIB2 output" +echo "J.Peng---10-07-2014-----------------------------" +echo " " +echo "Current time is: `date`" +echo " " +############################################################################## +set -x + +loopnum=$1 +cmodel=$2 +pert=$3 +TRKDATA=$4 + +export gribver=2 +#init_flag=$5 +# USE_OPER_VITALS=NO +# USE_OPER_VITALS=INIT_ONLY +USE_OPER_VITALS=YES + +############################################################################## +# +# FLOW OF CONTROL +# +# 1. Define data directories and file names for the input model +# 2. Process input starting date/cycle information +# 3. Update TC Vitals file and select storms to be processed +# 4. Cut apart input GRIB files to select only the needed parms and hours +# 5. Execute the tracker +# 6. Copy the output track files to various locations +# +############################################################################## +msg="has begun for ${cmodel} ${pert} at ${cyc}z" +postmsg "$jlogfile" "$msg" +######################################## + +# This script runs the hurricane tracker using operational GRIB model output. +# This script makes sure that the data files exist, it then pulls all of the +# needed data records out of the various GRIB forecast files and puts them +# into one, consolidated GRIB file, and then runs a program that reads the TC +# Vitals records for the input day and updates the TC Vitals (if necessary). +# It then runs gettrk, which actually does the tracking. +# +# Environmental variable inputs needed for this scripts: +# PDY -- The date for data being processed, in YYYYMMDD format +# cyc -- The numbers for the cycle for data being processed (00, 06, 12, 18) +# cmodel -- Model being processed (gfs, mrf, ukmet, ecmwf, nam, ngm, ngps, +# gdas, gfdl, ens (ncep ensemble)) +# envir -- 'prod' or 'test' +# SENDCOM -- 'YES' or 'NO' +# stormenv -- This is only needed by the tracker run for the GFDL model. +# 'stormenv' contains the name/id that is used in the input +# grib file names. +# pert -- This is only needed by the tracker run for the NCEP ensemble. +# 'pert' contains the ensemble member id (e.g., n2, p4, etc.) +# which is used as part of the grib file names. +# +# For testing script interactively in non-production set following vars: +# archsyndir - Directory with syndir scripts/exec/fix +# + +qid=$$ +#----------------------------------------------# +# Get input date information # +#----------------------------------------------# + +#export PDY=${PDY:-$1} +#export cyc=${cyc:-$2} +#export cmodel=${cmodel:-$3} +export jobid=${jobid:-testjob} +export SENDCOM=${SENDCOM:-NO} +export PHASEFLAG=y +export WCORE_DEPTH=1.0 +#export PHASE_SCHEME=vtt +#export PHASE_SCHEME=cps +export PHASE_SCHEME=both +export STRUCTFLAG=n +export IKEFLAG=n + +if [ ! -d $TRKDATA ] +then + mkdir -p $TRKDATA +fi +cd $TRKDATA + +if [ ${#PDY} -eq 0 -o ${#cyc} -eq 0 -o ${#cmodel} -eq 0 ] +then + set +x + echo " " + echo "FATAL ERROR: Something wrong with input parameters." + echo "PDY= ${PDY}, cyc= ${cyc}, cmodel= ${cmodel}" + set -x + err_exit "BAD INPUTS AT LINE $LINENO IN extrkr_gen_gfs.sh" +else + set +x + echo " " + echo " #-----------------------------------------------------------------#" + echo " At beginning of tracker script, the following imported variables " + echo " are defined: " + echo " PDY ................................... $PDY" + echo " cyc ................................... $cyc" + echo " cmodel ................................ $cmodel" + echo " jobid ................................. $jobid" + echo " envir ................................. $envir" + echo " SENDCOM ............................... $SENDCOM" + echo " " + set -x +fi + +scc=`echo ${PDY} | cut -c1-2` +syy=`echo ${PDY} | cut -c3-4` +smm=`echo ${PDY} | cut -c5-6` +sdd=`echo ${PDY} | cut -c7-8` +shh=${cyc} +symd=`echo ${PDY} | cut -c3-8` +syyyy=`echo ${PDY} | cut -c1-4` +CENT=`echo ${PDY} | cut -c1-2` + +#export archsyndir=${archsyndir:-${COMROOT}/arch/prod/syndat} +archsyndir=${archsyndir:-${COMINsyn:?}} + +#----------------------------------------------------------------# +# +# --- Define data directories and data file names --- +# +# Convert the input model to lowercase letters and check to see +# if it's a valid model, and assign a model ID number to it. +# This model ID number is passed into the Fortran program to +# let the program know what set of forecast hours to use in the +# ifhours array. Also, set the directories for the operational +# input GRIB data files and create templates for the file names. +# While only 1 of these sets of directories and file name +# templates is used during a particular run of this script, +# "gfsvitdir" is used every time, because that is the directory +# that contains the error-checked TC vitals file that Steve Lord +# produces, and so it is included after the case statement. +# +# NOTE: The varible PDY is now defined within the J-Jobs that +# call this script. Therefore there is no reason to do this +# here. +# +# NOTE: The script that processes the ECMWF data defines PDY as +# the current day, and in this script we need PDY to be +# yesterday's date (for the ecmwf ONLY). So instead, the ecmwf +# script will pass the variable PDYm1 to this script, and in the +# case statement below we change that to PDY. +# +# NOTE: Do NOT try to standardize this script by changing all of +# these various data directories' variable names to be all the +# same, such as "datadir". As you'll see in the data cutting +# part of this script below, different methods are used to cut +# apart different models, thus it is important to know the +# difference between them.... +#----------------------------------------------------------------# + +cmodel=`echo ${cmodel} | tr "[A-Z]" "[a-z]"` +#---- tracking variable list------------------------------------ +user_wants_to_track_zeta850='y' +user_wants_to_track_zeta700='y' +user_wants_to_track_wcirc850='y' +user_wants_to_track_wcirc700='y' +user_wants_to_track_gph850='y' +user_wants_to_track_gph700='y' +user_wants_to_track_mslp='y' +user_wants_to_track_wcircsfc='n' +user_wants_to_track_zetasfc='n' +user_wants_to_track_thick500850='n' +user_wants_to_track_thick200850='n' +user_wants_to_track_thick200500='n' + +case ${cmodel} in + + gfs) set +x; echo " " ; + echo " ++ operational FV3-GFS chosen" ; + echo " "; set -x ; + gfsdir=${gfsdir:-${COMINgfs:?}} ; + gfsgfile=gfs.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=1 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=15 ; + atcffreq=$((vit_incr*100)) ; + +# trkrwbd=0 ; +# trkrebd=360 ; +# trkrnbd=90 ; +# trkrsbd=-90 ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="gfso" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="gfso" ;; + +# g2_jpdtn sets the variable that will be used as "JPDTN" for +# the call to getgb2, if gribver=2. jpdtn=1 for ens data, +# jpdtn=0 for deterministic data. + + ngps) set +x; echo " " ; + echo " ++ operational NAVGEM chosen" ; + echo " "; set -x ; + ngpsdir=${ngpsdir:-${DCOM:?}} ; + ngpsgfile=US058GMET-OPSbd2.NAVGEM ; + ngemgfile=-${PDY}${cyc}-NOAA-halfdeg.gr2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=7 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=29 ; + atcffreq=$((vit_incr*100)) ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="ngx " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="ngx" ;; + + cmc) set +x; echo " " ; + echo " ++ operational CMC chosen" ; + echo " "; set -x ; + cmcdir=${cmcdir:-${DCOM:?}} ; + cmcgfile=CMC_glb_latlon.24x.24_${PDY}${cyc}_P ; + cmcgfile2=_NCEP.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=15 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=39 ; + atcffreq=$((vit_incr*100)) ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="cmc " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="cmc" ;; + + ens) set +x; echo " " ; + echo " ++ operational ensemble member ${pert} chosen"; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + + ensdira=${ensdira:-${COMINgefs:?}/pgrb2ap5} ; + ensgfilea=ge${pert}.t${cyc}z.pgrb2a.0p50.f ; + ensdirb=${ensdirb:-${COMINgefs:?}/pgrb2bp5} ; + ensgfileb=ge${pert}.t${cyc}z.pgrb2b.0p50.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=10 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="a${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="a${pert_posneg}${pert_num}" ;; + +# g2_jpdtn sets the variable that will be used as "JPDTN" for +# the call to getgb2, if gribver=2. jpdtn=1 for ens data, +# jpdtn=0 for deterministic data. + + fens) set +x; echo " " ; + echo " ++ FNMOC ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + + fensdir=${fensdir:-${DCOM:?}} ; + fensgfile=ENSEMBLE.MET.fcst_et0${pert_num}. ; + gensgfile=.${PDY}${cyc} ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-120} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=22 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + +# pert_posneg=` echo "${pert}" | cut -c1-1` ; +# pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="n${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="n${pert_posneg}${pert_num}" ;; + + cens) set +x; echo " " ; + echo " ++ Canadian ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + ccedir=${ccedir:-${DCOM:?}} ; + ccegfile=${PDY}${cyc}_CMC_naefs_hr_latlon0p5x0p5_P ; + ccegfile2=_0${pert_num}.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=16 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + +# pert_posneg=` echo "${pert}" | cut -c1-1` ; +# pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="c${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="c${pert_posneg}${pert_num}" ;; + + *) msg="FATAL ERROR: Model $cmodel is not recognized." ; + echo "$msg"; postmsg $jlogfile "$msg" ; + err_exit "UNKNOWN cmodel IN extrkr_gen_gfs.sh";; + +esac + +if [ ${PHASEFLAG} = 'y' ]; then + +#J.Peng----2014-10-28--------- + if [ ${cmodel} = "gfs" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300|RH:500)" + + elif [ ${cmodel} = "ngps" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:400|HGT:300|TMP:500|TMP:400|TMP:300|RH:500)" + + elif [ ${cmodel} = "ens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300|RH:500)" + + elif [ ${cmodel} = "cens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:300|TMP:500|TMP:250|RH:500)" + + elif [ ${cmodel} = "cmc" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250|SPFH:500)" + + elif [ ${cmodel} = "fens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250|RH:500)" + + fi + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 U:200 V:850 V:700 V:500 V:200 10U:sfc 10V:sfc MSL:sfc GH:300 GH:400 GH:500 GH:925 T:300 T:400 T:500 R:500" + +else + wgrib_parmlist=" HGT:850 HGT:700 UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET " + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc" +fi + +#---------------------------------------------------------------# +# +# -------- TC Vitals processing -------- +# +# Check Steve Lord's operational tcvitals file to see if any +# vitals records were processed for this time by his system. +# If there were, then you'll find a file in /com/gfs/prod/gfs.yymmdd +# with the vitals in it. Also check the raw TC Vitals file in +# /com/arch/prod/syndat , since this may contain storms that Steve's +# system ignored (Steve's system will ignore all storms that are +# either over land or very close to land); We still want to track +# these inland storms, AS LONG AS THEY ARE NHC STORMS (don't +# bother trying to track inland storms that are outside of NHC's +# domain of responsibility -- we don't need that info). +# UPDATE 5/12/98 MARCHOK: The script is updated so that for the +# ensemble models, the gfs directory is checked for the error- +# checked vitals file, while for the regional models, the +# nam directory is checked for that file. +# UPDATE 3/27/09 MARCHOK: The SREF is run at off-synoptic times +# (03,09,15,21Z). There are no tcvitals issued at these offtimes, +# so the updating of the "old" tcvitals is critical for running +# the tracker on SREF. For updating the old tcvitals for SREF, +# we need to look 3h back, not 6h as for the other models that +# run at synoptic times. +#--------------------------------------------------------------# + +old_ymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +old_4ymd=` echo ${old_ymdh} | cut -c1-8` +old_ymd=` echo ${old_ymdh} | cut -c3-8` +old_hh=` echo ${old_ymdh} | cut -c9-10` +old_str="${old_ymd} ${old_hh}00" + +future_ymdh=` ${NDATE:?} ${vit_incr} ${PDY}${cyc}` +future_4ymd=` echo ${future_ymdh} | cut -c1-8` +future_ymd=` echo ${future_ymdh} | cut -c3-8` +future_hh=` echo ${future_ymdh} | cut -c9-10` +future_str="${future_ymd} ${future_hh}00" + +if [ ${modtyp} = 'global' ] +then +# synvitdir=${COMROOT}/gfs/prod/gfs.${PDY} + synvitdir=${COMINgfs:?} + synvitfile=gfs.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/gfs/prod/gfs.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=gfs.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/gfs/prod/gfs.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=gfs.t${future_hh}z.syndata.tcvitals.tm00 +else +# synvitdir=${COMROOT}/nam/prod/nam.${PDY} + synvitdir=${COMINnam:?} + synvitfile=nam.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/nam/prod/nam.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=nam.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/nam/prod/nam.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=nam.t${future_hh}z.syndata.tcvitals.tm00 +fi + +set +x +echo " " +echo " -----------------------------" +echo " " +echo " Now sorting and updating the TC Vitals file. Please wait...." +echo " " +set -x + +current_str="${symd} ${cyc}00" + +if [ -s ${synvitdir}/${synvitfile} -o\ + -s ${synvitold_dir}/${synvitold_file} -o\ + -s ${synvitfuture_dir}/${synvitfuture_file} ] +then + grep "${old_str}" ${synvitold_dir}/${synvitold_file} \ + >${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${synvitdir}/${synvitfile} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${future_str}" ${synvitfuture_dir}/${synvitfuture_file} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} +else + set +x + echo " " + echo " There is no (synthetic) TC vitals file for ${cyc}z in ${synvitdir}," + echo " nor is there a TC vitals file for ${old_hh}z in ${synvitold_dir}." + echo " nor is there a TC vitals file for ${future_hh}z in ${synvitfuture_dir}," + echo " Checking the raw TC Vitals file ....." + echo " " + set -x +fi + +# Take the vitals from Steve Lord's /com/gfs/prod tcvitals file, +# and cat them with the NHC-only vitals from the raw, original +# /com/arch/prod/synda_tcvitals file. Do this because the nwprod +# tcvitals file is the original tcvitals file, and Steve runs a +# program that ignores the vitals for a storm that's over land or +# even just too close to land, and for tracking purposes for the +# US regional models, we need these locations. Only include these +# "inland" storm vitals for NHC (we're not going to track inland +# storms that are outside of NHC's domain of responsibility -- we +# don't need that info). +# UPDATE 5/12/98 MARCHOK: awk logic is added to screen NHC +# vitals such as "91L NAMELESS" or "89E NAMELESS", since TPC +# does not want tracks for such storms. + +grep "${old_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${current_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${future_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-03---for genesis tc vitals-------------------- +#export COMINgenvit=/ensemble/save/Jiayi.Peng/genesis_vital_${syyyy} +#grep "${old_str}" ${COMINgenvit}/gen_tc.vitals.gfs.gfso.2012 | \ +# grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ +# >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +if [ ${cmodel} = 'ens' -o ${cmodel} = 'gfs' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.gfs.gfso.${syyyy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +elif [ ${cmodel} = 'cens' -o ${cmodel} = 'cmc' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.cmc.cmc.${syyyy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +elif [ ${cmodel} = 'fens' -o ${cmodel} = 'ngps' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.ngps.ngx.${syyyy} | \ +grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +elif [ ${cmodel} = 'eens' -o ${cmodel} = 'ecmwf' ]; then +grep "${current_str}" ${COMINgenvit}/all.vitals.ecmwf.emx.${syyyy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +fi + +#PRODTEST# Use the next couple lines to test the tracker on the SP. +#PRODTEST# These next couple lines use data from a test TC Vitals file that +#PRODTEST# I generate. When you are ready to test this system, call me and +#PRODTEST# I'll create one for the current day, and then uncomment the next +#PRODTEST# couple lines in order to access the test vitals file. +# +#ttrkdir=/ensemble/save/wx20tm/trak/prod/data +#ttrkdir=/ensemble/save/wx20tm/trak/para/scripts +#grep "${current_str}" ${ttrkdir}/tcvit.01l >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + + +# IMPORTANT: When "cat-ing" these files, make sure that the vitals +# files from the "raw" TC vitals files are first in order and Steve's +# TC vitals files second. This is because Steve's vitals file has +# been error-checked, so if we have a duplicate tc vitals record in +# these 2 files (very likely), program supvit.x below will +# only take the last vitals record listed for a particular storm in +# the vitals file (all previous duplicates are ignored, and Steve's +# error-checked vitals records are kept). + +cat ${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} ${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} \ + >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# If we are doing the processing for the GFDL model, then we want +# to further cut down on which vitals we allow into this run of the +# tracker. The reason is that this program will be called from +# each individual run for a storm, so the grib files will be +# specific to each storm. So if 4 storms are being run at a +# particular cycle, then this script is run 4 separate times from +# within the GFDL_POST job. + +#if [ ${cmodel} = 'gfdl' ]; then +## grep -i ${stormid} ${COMIN}/${ATCFNAME}.vitals.${syy}${smm}${sdd}${shh} >${TRKDATA}/tmpvit +## mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#echo " " +#echo "!!! ERROR: TAKE THE COMMENTS OUT OF HERE BEFORE IMPLEMENTATION AAAA" +#echo "!!! AND TAKE OUT THESE NEXT 2 LINES AS WELL............................" +#echo " " +#grep -i ${stormid} ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/tmpvit +#mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#fi + +#--------------------------------------------------------------# +# Now run a fortran program that will read all the TC vitals +# records for the current dtg and the dtg from 6h ago, and +# sort out any duplicates. If the program finds a storm that +# was included in the vitals file 6h ago but not for the current +# dtg, this program updates the 6h-old first guess position +# and puts these updated records as well as the records from +# the current dtg into a temporary vitals file. It is this +# temporary vitals file that is then used as the input for the +# tracking program. +#--------------------------------------------------------------# + +oldymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +oldyy=`echo ${oldymdh} | cut -c3-4` +oldmm=`echo ${oldymdh} | cut -c5-6` +olddd=`echo ${oldymdh} | cut -c7-8` +oldhh=`echo ${oldymdh} | cut -c9-10` +oldymd=${oldyy}${oldmm}${olddd} + +futureymdh=` ${NDATE:?} 6 ${PDY}${cyc}` +futureyy=`echo ${futureymdh} | cut -c3-4` +futuremm=`echo ${futureymdh} | cut -c5-6` +futuredd=`echo ${futureymdh} | cut -c7-8` +futurehh=`echo ${futureymdh} | cut -c9-10` +futureymd=${futureyy}${futuremm}${futuredd} + +echo "&datenowin dnow%yy=${syy}, dnow%mm=${smm}," >${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&dateoldin dold%yy=${oldyy}, dold%mm=${oldmm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dold%dd=${olddd}, dold%hh=${oldhh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&datefuturein dfuture%yy=${futureyy}, dfuture%mm=${futuremm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dfuture%dd=${futuredd}, dfuture%hh=${futurehh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&hourinfo vit_hr_incr=${vit_incr}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period." + echo "!!! File ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} is empty." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi + +fi + +# For tcgen cases, filter to use only vitals from the ocean +# basin of interest.... +#J.Peng----2012-04-13------------------------------------- +#if [ ${trkrtype} = 'tcgen' ] +# then + +# if [ ${numvitrecs} -gt 0 ] +# then + +# fullvitfile=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# cp $fullvitfile ${TRKDATA}/vitals.all_basins.${atcfout}.${PDY}${cyc} +# basin=` echo $regtype | cut -c1-2` + +# if [ ${basin} = 'al' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "L") print $0}' | +# >${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'ep' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "E") print $0}' | +# >${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'wp' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "W") print $0}' | +# >${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi + +# cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# fi + +#fi +#J.Peng----2012-04-13------------------------------------- + +# - - - - - - - - - - - - - +# Before running the program to read, sort and update the vitals, +# first run the vitals through some awk logic, the purpose of +# which is to convert all the 2-digit years into 4-digit years. +# Beginning 4/21/99, NHC and JTWC will begin sending the vitals +# with 4-digit years, however it is unknown when other ensemble +# forecasting centers will begin using 4-digit years, thus we +# need the following logic to ensure that all the vitals going +# into supvit.f have uniform, 4-digit years in their records. +# +# 1/8/2000: sed code added by Tim Marchok due to the fact that +# some of the vitals were getting past the syndata/qctropcy +# error-checking with a colon in them; the colon appeared +# in the character immediately to the left of the date, which +# was messing up the "(length($4) == 8)" statement logic. +# - - - - - - - - - - - - - + +sed -e "s/\:/ /g" ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} > ${TRKDATA}/tempvit +mv ${TRKDATA}/tempvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +awk ' +{ + yycheck = substr($0,20,2) + if ((yycheck == 20 || yycheck == 19) && (length($4) == 8)) { + printf ("%s\n",$0) + } + else { + if (yycheck >= 0 && yycheck <= 50) { + printf ("%s20%s\n",substr($0,1,19),substr($0,20)) + } + else { + printf ("%s19%s\n",substr($0,1,19),substr($0,20)) + } + } +} ' ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 + +mv ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +#Added by J.Peng----2012-04-12---------------------------------------------- +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +#----------------------------------------------------------------- +if [ ${numvitrecs} -gt 0 ] +then + export pgm=supvit_g2 + . prep_step + + # Input file + export FORT31=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_g2 <${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + suvrcc=$? + + if [ ${suvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit.x, " + echo "!!! which is the program that updates the TC Vitals file." + echo "!!! Return code from supvit.x = ${suvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "ERROR RUNNING supvit_g2 IN extrkr_gen_gfs LINE $LINENO" + fi + +else +#Added by J.Peng----2012-04-12---------------------------------------------- + rm -f ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + touch ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#----------------------------------------------------------------- +# In this section, check to see if the user requested the use of +# operational TC vitals records for the initial time only. This +# option might be used for a retrospective medium range forecast +# in which the user wants to initialize with the storms that are +# currently there, but then let the model do its own thing for +# the next 10 or 14 days.... + + +if [ ${USE_OPER_VITALS} = 'INIT_ONLY' ]; then + + if [ ${init_flag} = 'yes' ]; then + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are currently at." + echo " " + set -x + else + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are now *PAST*." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + fi + +elif [ ${USE_OPER_VITALS} = 'NO' ]; then + + set +x + echo " " + echo "NOTE: User has requested that historical vitals not be used...." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#------------------------------------------------------------------# +# Now select all storms to be processed, that is, process every +# storm that's listed in the updated vitals file for the current +# forecast hour. If there are no storms for the current time, +# then exit. +#------------------------------------------------------------------# + +numvitrecs=`cat ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period " + echo "!!! in the UPDATED vitals file." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi +fi + +set +x +echo " " +echo " *--------------------------------*" +echo " | STORM SELECTION |" +echo " *--------------------------------*" +echo " " +set -x + +ict=1 +while [ $ict -le 15 ] +do + stormflag[${ict}]=3 + let ict=ict+1 +done + +dtg_current="${symd} ${cyc}00" +stormmax=` grep "${dtg_current}" ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +if [ ${stormmax} -gt 15 ] +then + stormmax=15 +fi + +sct=1 +while [ ${sct} -le ${stormmax} ] +do + stormflag[${sct}]=1 + let sct=sct+1 +done + + +#---------------------------------------------------------------# +# +# -------- "Genesis" Vitals processing -------- +# +# May 2006: This entire genesis tracking system is being +# upgraded to more comprehensively track and categorize storms. +# One thing that has been missing from the tracking system is +# the ability to keep track of storms from one analysis cycle +# to the next. That is, the current system has been very +# effective at tracking systems within a forecast, but we have +# no methods in place for keeping track of storms across +# difference initial times. For example, if we are running +# the tracker on today's 00z GFS analysis, we will get a +# position for various storms at the analysis time. But then +# if we go ahead and run again at 06z, we have no way of +# telling the tracker that we know about the 00z position of +# this storm. We now address that problem by creating +# "genesis" vitals, that is, when a storm is found at an +# analysis time, we not only produce "atcfunix" output to +# detail the track & intensity of a found storm, but we also +# produce a vitals record that will be used for the next +# run of the tracker script. These "genesis vitals" records +# will be of the format: +# +# YYYYMMDDHH_AAAH_LLLLX_TYP +# +# Where: +# +# YYYYMMDDHH = Date the storm was FIRST identified +# by the tracker. +# AAA = Abs(Latitude) * 10; integer value +# H = 'N' for norther hem, 'S' for southern hem +# LLLL = Abs(Longitude) * 10; integer value +# X = 'E' for eastern hem, 'W' for western hem +# TYP = Tropical cyclone storm id if this is a +# tropical cyclone (e.g., "12L", or "09W", etc). +# If this is one that the tracker instead "Found +# On the Fly (FOF)", we simply put those three +# "FOF" characters in there. +#J.Peng----2012-04-12-------- +genvitfile=${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} +touch ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + +if [ -f $genvitfile ]; then + d6ago_ymdh=` ${NDATE:?} -6 ${PDY}${cyc}` + d6ago_4ymd=` echo ${d6ago_ymdh} | cut -c1-8` + d6ago_ymd=` echo ${d6ago_ymdh} | cut -c3-8` + d6ago_hh=` echo ${d6ago_ymdh} | cut -c9-10` + d6ago_str="${d6ago_ymd} ${d6ago_hh}00" + + d6ahead_ymdh=` ${NDATE:?} 6 ${PDY}${cyc}` + d6ahead_4ymd=` echo ${d6ahead_ymdh} | cut -c1-8` + d6ahead_ymd=` echo ${d6ahead_ymdh} | cut -c3-8` + d6ahead_hh=` echo ${d6ahead_ymdh} | cut -c9-10` + d6ahead_str="${d6ahead_ymd} ${d6ahead_hh}00" + + set +x + echo " " + echo " d6ago_str= --->${d6ago_str}<---" + echo " current_str= --->${current_str}<---" + echo " d6ahead_str= --->${d6ahead_str}<---" + echo " " + #echo " Listing and contents of ${genvitfile} follow " + #echo " for the times 6h ago, current and 6h ahead:" + #echo " " + set -x + + #J.Peng----2012-04-11----- + ##ls -la ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + ##cat ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + #ls -la ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #cat ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + + grep "${d6ago_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${d6ahead_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + num_gen_vits=`cat ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} | wc -l` +else + echo "WARNING: Genesis Vitals from previous run(s) not found!" +fi + +echo "&datenowin dnow%yy=${syyyy}, dnow%mm=${smm}," >${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6agoin d6ago%yy=${syyyy6}, d6ago%mm=${smm6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ago%dd=${sdd6}, d6ago%hh=${shh6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6aheadin d6ahead%yy=${syyyyp6}, d6ahead%mm=${smmp6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ahead%dd=${sddp6}, d6ahead%hh=${shhp6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-04---do not need calculate this ---------- +num_gen_vits=0 + +if [ ${num_gen_vits} -gt 0 ] +then + export pgm=supvit_gen + . prep_step + + # Input file + export FORT31=${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_gen <${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + sgvrcc=$? + + if [ ${sgvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit_gen, " + echo "!!! which is the program that updates the genesis vitals file." + echo "!!! Return code from supvit_gen = ${sgvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "ERROR RUNNING SUPVIT_GEN IN extrkr_gen_gfs.sh LINE $LINENO" + fi + +else + rm -f ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + touch ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +fi + + +#-----------------------------------------------------------------# +# +# ------ CUT APART INPUT GRIB FILES ------- +# +# For the selected model, cut apart the GRIB input files in order +# to pull out only the variables that we need for the tracker. +# Put these selected variables from all forecast hours into 1 big +# GRIB file that we'll use as input for the tracker. +# +# The wgrib utility (/nwprod/util/exec/wgrib) is used to cut out +# the needed parms for the GFS, MRF, GDAS, UKMET and NOGAPS files. +# The utility /nwprod/util/exec/copygb is used to interpolate the +# NGM (polar stereographic) and NAM (Lambert Conformal) data from +# their grids onto lat/lon grids. Note that while the lat/lon +# grid that I specify overlaps into areas that don't have any data +# on the original grid, Mark Iredell wrote the copygb software so +# that it will mask such "no-data" points with a bitmap (just be +# sure to check the lbms in your fortran program after getgb). +#-----------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------" +echo " NOW CUTTING APART INPUT GRIB FILES TO " +echo " CREATE 1 BIG GRIB INPUT FILE " +echo " -----------------------------------------" +echo " " +set -x + +regflag=`grep NHC ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +# ------------------------------ +# Process FNMOC Ensemble, if selected +# ------------------------------ + +if [ ${model} -eq 22 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ] + then + rm ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + fensfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + + let attempts=1 + while [ $attempts -le 40 ]; do + if [ -s $fensfile ]; then + break + else + sleep 60 + let attempts=attempts+1 + fi + done + if [ $attempts -gt 40 ] && [ ! -s $fensfile ]; then + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "$fensfile" >> ${DATA}/missing_fens.txt + exit + else + err_exit "$fensfile still not available after waiting 40 minutes" + fi + fi + + #${WGRIB2:?} -set_byte 1 11 1 -s $fensfile >fens.ix # set_byte resets local table to 1 to remove error messages + gfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} +# rm $tavefile $zfile + +#J.Peng----2014-10-15---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2014-10-15---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ixfile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + +fi + +# ------------------------------ +# Process GFS, if selected +# ------------------------------ + +if [ ${model} -eq 1 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gfsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc} + fi + + rm ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f* + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gfsgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gfs wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gfsdir}/${gfsgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GFS File missing: ${gfsdir}/${gfsgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GFS FILE IN extrkr_gen_gfs.sh: ${gfsdir}/${gfsgfile}${fhour}" + fi + + gfile=${gfsdir}/${gfsgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + + gfs_master_file=${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + gfs_cat_file=${TRKDATA}/gfsgribfile.${PDY}${cyc} + cat ${gfs_master_file} >>${gfs_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gfsgribfile.${PDY}${cyc} ${TRKDATA}/gfsixfile.${PDY}${cyc} + export err=$?; err_chk + +# Now run through the work to get data for phase checking.... +# -------------------------------------------- + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "BEGIN: 300-500hPa T-mean and 200-850hPa U-shear" + echo " for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + +#J.Peng----2012-04-18---added for U-shear----- + gparm=33 + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-04-18---added for U-shear----- + + set +x + echo " " + echo "END: 300-500hPa T-mean and 200-850hPa U-shear" + echo " for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + cat ${catfile} >>${ffile} + + fi + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gfsixfile.${PDY}${cyc} + +fi + +# -------------------------------------------------- +# Process NCEP Ensemble perturbation, if selected +# -------------------------------------------------- + +if [ ${model} -eq 10 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ensdira}/${ensgfilea}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GEFS ${PERT} File missing: ${ensdira}/${ensgfilea}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GEFS FILE IN TRACKER SCRIPT: ${ensdira}/${ensgfilea}${fhour}" + fi + + gfile=${TRKDATA}/${ensgfilea}${fhour} + cat ${ensdira}/${ensgfilea}${fhour} ${ensdirb}/${ensgfileb}${fhour} > $gfile + + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} + cat ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} >> ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/ens${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in T-mean/U-shear for pert= $pert and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + +#J.Peng----2014-10-15---added for U-shear----- + ffile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + + gparm=33 + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2014-10-15---added for U-shear----- + + set +x + echo " " + echo "Date in T-mean/U-shear for pert= $pert and fhour= $fhour before = `date`" + echo " " + set -x + + done + fi + fi + + gfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------ +# Process NAVGEM, if selected +# ------------------------------ + +if [ ${model} -eq 7 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} ] + then + set +x + echo " " + echo "FATAL ERROR: NAVGEM File missing ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" >> ${DATA}/missing_ngps.txt + exit + else + err_exit "MISSING NAVGEM FILE at $0:$LINENO" + fi + fi + + gfile=${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + + done + +# Now run through the work to get data for phase checking.... + ${GRB2INDEX:?} ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ${TRKDATA}/ngpsixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk + +fi + +# ------------------------------ +# Process CMC deterministic , if selected +# ------------------------------ + +if [ ${model} -eq 15 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/cmcgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/cmcgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} ] + then + set +x + echo " " + echo "FATAL ERROR: CMC File missing ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + fi + + gfile=${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + done + +# J.Peng--04-05-2017--changing grid from 180E to 0E------------ +# wgrib2 junk -small_grib 0:359.76 -90:90 junk2 +# cp ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/junk +# rm -f ${TRKDATA}/cmcgribfile.${PDY}${cyc} +# ${WGRIB2} ${TRKDATA}/junk -small_grib 0:359.76 -90:90 ${TRKDATA}/junk2 +# cp ${TRKDATA}/junk2 ${TRKDATA}/cmcgribfile.${PDY}${cyc} + +# Now run through the work to get data for phase checking.... + ${GRB2INDEX:?} ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/cmcixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- +# +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + gparmq=7 + gparmt=11 + + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/rh_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparmq=${gparmq}," >>${namelist} + echo " iparmt=${gparmt}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT16=${FIXens_tracker}/cmc_rh_levs.txt + + # Output file + export FORT51=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/rhum_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to rhum_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -rhum_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + rhfile=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + cat ${rhfile} >>${catfile} +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk + +fi + +# ------------------------------------------------------ +# Process Canadian Ensemble perturbation, if selected +# ------------------------------------------------------ + +if [ ${model} -eq 16 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ccedir}/${ccegfile}${fhour}${ccegfile2} ] + then + set +x + echo " " + echo "FATAL ERROR: CANADIAN ENSEMBLE ${PERT} missing: ${ccegfile}${fhour}${ccegfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING CMC Ens FILE IN TRACKER SCRIPT: ${ccegfile}${fhour}${ccegfile2}" + fi + + gfile=${ccedir}/${ccegfile}${fhour}${ccegfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk +# Process the cyclone phase variables, if requested + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}, iparm=${gparm}/" >${namelist} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gen_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} +# rm $tavefile $zfile + +#J.Peng----2014-10-22----added for U-shear----- + ffile=${gfile} + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + + gparm=33 + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "ushear_g2.x- ERROR AT extrkr_gen_gfs.sh LINE $LINENO" + fi + + ushearfile=${TRKDATA}/${cmodel}_${pert}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2014-10-22----added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + gribfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +fi + + +#------------------------------------------------------------------------# +# Now run the tracker # +#------------------------------------------------------------------------# +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW EXECUTING TRACKER......" +echo " -----------------------------------------------" +echo " " +set -x + +ist=1 +while [ $ist -le 15 ] +do + if [ ${stormflag[${ist}]} -ne 1 ] + then + set +x; echo "Storm number $ist NOT selected for processing"; set -x + else + set +x; echo "Storm number $ist IS selected for processing...."; set -x + fi + let ist=ist+1 +done + +if [ ! -s ${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} ] +then + for fhr in $fcsthrs; do + if [ $fhr -ne 99 ]; then + last_fcst_hour=$fhr + fi + done + echo ${last_fcst_hour} >${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} +fi + +namelist=${TRKDATA}/input.${atcfout}.${PDY}${cyc} +ATCFNAME=` echo "${atcfname}" | tr '[a-z]' '[A-Z]'` + +export atcfymdh=${scc}${syy}${smm}${sdd}${shh} + +#------07-26-2019----creating the fix-file for GFS tracking ----- +if [ ${model} -eq 1 ]; then +. prep_step + +# Input files +namelist_2_fix=${TRKDATA}/namelist_2_fix_input.${PDY}${cyc} +echo "&timein maxhrs=${fcstlen}," >${namelist_2_fix} +echo " dthrs=${vit_incr}/" >>${namelist_2_fix} + +# Output file +export FORT10=${TRKDATA}/${cmodel}.genesis_leadtimes_120 + +${EXECens_tracker}/leadtime <${namelist_2_fix} +rcc=$? + +if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to leadtime at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "leadtime - ERROR AT extrkr_gen_gfs.sh LINE $LINENO" +fi + +fi +#------07-26-2019----creating the fix-file for GFS tracking ----- + +#contour_interval=100.0 +#write_vit=y +#want_oci=.TRUE. + +echo "&datein inp%bcc=${scc},inp%byy=${syy},inp%bmm=${smm}," >${namelist} +echo " inp%bdd=${sdd},inp%bhh=${shh},inp%model=${model}," >>${namelist} +echo " inp%modtyp='${modtyp}'," >>${namelist} +echo " inp%lt_units='${lead_time_units}'," >>${namelist} +echo " inp%file_seq='${file_sequence}'," >>${namelist} +echo " inp%nesttyp='${nest_type}'/" >>${namelist} + +echo "&atcfinfo atcfnum=${atcfnum},atcfname='${ATCFNAME}'," >>${namelist} +echo " atcfymdh=${atcfymdh},atcffreq=${atcffreq}/" >>${namelist} + +echo "&trackerinfo trkrinfo%westbd=${trkrwbd}," >>${namelist} +echo " trkrinfo%eastbd=${trkrebd}," >>${namelist} +echo " trkrinfo%northbd=${trkrnbd}," >>${namelist} +echo " trkrinfo%southbd=${trkrsbd}," >>${namelist} +echo " trkrinfo%type='${trkrtype}'," >>${namelist} +echo " trkrinfo%mslpthresh=${mslpthresh}," >>${namelist} +echo " trkrinfo%use_backup_mslp_grad_check='${use_backup_mslp_grad_check}'," >>${namelist} +echo " trkrinfo%max_mslp_850=${max_mslp_850}," >>${namelist} +echo " trkrinfo%v850thresh=${v850thresh}," >>${namelist} +echo " trkrinfo%use_backup_850_vt_check='${use_backup_850_vt_check}'," >>${namelist} +echo " trkrinfo%gridtype='${modtyp}'," >>${namelist} +echo " trkrinfo%enable_timing=1," >>${namelist} +echo " trkrinfo%contint=${contour_interval}," >>${namelist} +echo " trkrinfo%want_oci=${want_oci}," >>${namelist} +echo " trkrinfo%out_vit='${write_vit}'," >>${namelist} +echo " trkrinfo%use_land_mask='${use_land_mask}'," >>${namelist} +echo " trkrinfo%inp_data_type='${inp_data_type}'," >>${namelist} +echo " trkrinfo%gribver=${gribver}," >>${namelist} +echo " trkrinfo%g2_jpdtn=${g2_jpdtn}," >>${namelist} +echo " trkrinfo%g2_mslp_parm_id=${g2_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_mslp_parm_id=${g1_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_typ=${g1_sfcwind_lev_typ}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_val=${g1_sfcwind_lev_val}/" >>${namelist} + +echo "&phaseinfo phaseflag='${PHASEFLAG}'," >>${namelist} +echo " phasescheme='${PHASE_SCHEME}'," >>${namelist} +echo " wcore_depth=${WCORE_DEPTH}/" >>${namelist} + +echo "&structinfo structflag='${STRUCTFLAG}'," >>${namelist} +echo " ikeflag='${IKEFLAG}'/" >>${namelist} + +echo "&fnameinfo gmodname='${atcfname}'," >>${namelist} +echo " rundescr='${rundescr}'," >>${namelist} +echo " atcfdescr='${atcfdescr}'/" >>${namelist} + +echo "&waitinfo use_waitfor='n'," >>${namelist} +echo " wait_min_age=10," >>${namelist} +echo " wait_min_size=100," >>${namelist} +echo " wait_max_wait=1800," >>${namelist} +echo " wait_sleeptime=5," >>${namelist} +echo " per_fcst_command=''/" >>${namelist} + +echo "&netcdflist netcdfinfo%num_netcdf_vars=${ncdf_num_netcdf_vars}," >>${namelist} +echo " netcdfinfo%netcdf_filename='${netcdffile}'," >>${namelist} +echo " netcdfinfo%rv850name='${ncdf_rv850name}'," >>${namelist} +echo " netcdfinfo%rv700name='${ncdf_rv700name}'," >>${namelist} +echo " netcdfinfo%u850name='${ncdf_u850name}'," >>${namelist} +echo " netcdfinfo%v850name='${ncdf_v850name}'," >>${namelist} +echo " netcdfinfo%u700name='${ncdf_u700name}'," >>${namelist} +echo " netcdfinfo%v700name='${ncdf_v700name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%mslpname='${ncdf_mslpname}'," >>${namelist} +echo " netcdfinfo%usfcname='${ncdf_usfcname}'," >>${namelist} +echo " netcdfinfo%vsfcname='${ncdf_vsfcname}'," >>${namelist} +echo " netcdfinfo%u500name='${ncdf_u500name}'," >>${namelist} +echo " netcdfinfo%v500name='${ncdf_v500name}'," >>${namelist} +echo " netcdfinfo%tmean_300_500_name='${ncdf_tmean_300_500_name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z200name='${ncdf_z200name}'," >>${namelist} +echo " netcdfinfo%lmaskname='${ncdf_lmaskname}'," >>${namelist} +echo " netcdfinfo%z900name='${ncdf_z900name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z800name='${ncdf_z800name}'," >>${namelist} +echo " netcdfinfo%z750name='${ncdf_z750name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%z650name='${ncdf_z650name}'," >>${namelist} +echo " netcdfinfo%z600name='${ncdf_z600name}'," >>${namelist} +echo " netcdfinfo%z550name='${ncdf_z550name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z450name='${ncdf_z450name}'," >>${namelist} +echo " netcdfinfo%z400name='${ncdf_z400name}'," >>${namelist} +echo " netcdfinfo%z350name='${ncdf_z350name}'," >>${namelist} +echo " netcdfinfo%z300name='${ncdf_z300name}'," >>${namelist} +echo " netcdfinfo%time_name='${ncdf_time_name}'," >>${namelist} +echo " netcdfinfo%lon_name='${ncdf_lon_name}'," >>${namelist} +echo " netcdfinfo%lat_name='${ncdf_lat_name}'," >>${namelist} +echo " netcdfinfo%time_units='${ncdf_time_units}'/" >>${namelist} + +echo "&parmpreflist user_wants_to_track_zeta850='${user_wants_to_track_zeta850}'," >>${namelist} +echo " user_wants_to_track_zeta700='${user_wants_to_track_zeta700}'," >>${namelist} +echo " user_wants_to_track_wcirc850='${user_wants_to_track_wcirc850}'," >>${namelist} +echo " user_wants_to_track_wcirc700='${user_wants_to_track_wcirc700}'," >>${namelist} +echo " user_wants_to_track_gph850='${user_wants_to_track_gph850}'," >>${namelist} +echo " user_wants_to_track_gph700='${user_wants_to_track_gph700}'," >>${namelist} +echo " user_wants_to_track_mslp='${user_wants_to_track_mslp}'," >>${namelist} +echo " user_wants_to_track_wcircsfc='${user_wants_to_track_wcircsfc}'," >>${namelist} +echo " user_wants_to_track_zetasfc='${user_wants_to_track_zetasfc}'," >>${namelist} +echo " user_wants_to_track_thick500850='${user_wants_to_track_thick500850}'," >>${namelist} +echo " user_wants_to_track_thick200500='${user_wants_to_track_thick200500}'," >>${namelist} +echo " user_wants_to_track_thick200850='${user_wants_to_track_thick200850}'/" >>${namelist} + +echo "&verbose verb=3,verb_g2=0/" >>${namelist} + +export pgm=gettrk_gen_gfs +. prep_step + +export FORT11=${gribfile} +export FORT12=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} +export FORT14=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +if [ ${model} -eq 1 ]; then + export FORT15=${TRKDATA}/${cmodel}.genesis_leadtimes_120 +else + export FORT15=${FIXens_tracker}/${cmodel}.genesis_leadtimes_120 +fi + +export FORT31=${ixfile} + +if [ ${trkrtype} = 'tracker' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT61=${TRKDATA}/trak.${atcfout}.all.${stormenv}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${stormenv}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${stormenv}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${stormenv}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${stormenv}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${stormenv}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${stormenv}.${PDY}${cyc} + else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${PDY}${cyc} + fi +else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${regtype}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${regtype}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${regtype}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${regtype}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${regtype}.${PDY}${cyc} +fi + +if [ ${atcfname} = 'aear' ] +then + export FORT65=${TRKDATA}/trak.${atcfout}.initvitl.${PDY}${cyc} +fi + +if [ ${write_vit} = 'y' ] +then + export FORT67=${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} +fi + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${stormenv}.${PDY}${cyc} + else + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${PDY}${cyc} + fi +fi + +if [ ${STRUCTFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${stormenv}.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${stormenv}.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${stormenv}.${PDY}${cyc} + else + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${PDY}${cyc} + fi +fi + +if [ ${IKEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${stormenv}.${PDY}${cyc} + else + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${PDY}${cyc} + fi +fi + +if [ ${trkrtype} = 'midlat' -o ${trkrtype} = 'tcgen' ]; then + export FORT77=${TRKDATA}/trkrmask.${atcfout}.${regtype}.${PDY}${cyc} +fi + +msg="$pgm start for $atcfout at ${cyc}z" +postmsg "$jlogfile" "$msg" + +set +x +echo "+++ TIMING: BEFORE gettrk ---> `date`" +set -x + +ulimit -c unlimited + +${EXECens_tracker}/gettrk_gen_gfs <${namelist} +gettrk_rcc=$? + +set +x +echo "+++ TIMING: AFTER gettrk ---> `date`" +set -x + +#--------------------------------------------------------------# +# Now copy the output track files to different directories +#--------------------------------------------------------------# + +if [ ${gettrk_rcc} -eq 0 ]; then + set +x + echo " " + echo " -----------------------------------------------" + echo " NOW COPYING OUTPUT TRACK FILES TO COM " + echo " -----------------------------------------------" + echo " " + set -x + + if [ "$SENDCOM" = 'YES' ]; then + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} ${COMOUT}/ +# cp ${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} ${COMOUT}/ + + #if [ "$SENDDBN" = 'YES' ]; then + # $DBNROOT/bin/dbn_alert MODEL ENS_GENESIS $job ${COMOUT}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + #fi + fi + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" +else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running gettrk_gen_gfs, " + echo "!!! which is the program that actually gets the track." + echo "!!! Return code from gettrk_gen_gfs = ${gettrk_rcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "ERROR RUNNING gettrk_gen_gfs IN 2nd step" +fi diff --git a/sorc/ens_tracker.v1.1.15.2/ush/extrkr_gen_gfs.sh b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_gfs.sh_20200806 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/extrkr_gen_gfs.sh rename to sorc/ens_tracker.v1.1.15.4/ush/extrkr_gen_gfs.sh_20200806 diff --git a/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh new file mode 100755 index 0000000000..abf5bc34b1 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh @@ -0,0 +1,2659 @@ +#!/bin/ksh +export PS4=' + extrkr_gfs.sh line $LINENO: ' + +set +x +############################################################################## +echo " " +echo "------------------------------------------------" +echo " TC Track in model GRIB2 output " +echo " Models:GFS, GEFS, CENS, FENS, NAVGEM " +echo "------------J.Peng Jan.21, 2015 ----------------" +echo "Current time is: `date`" +echo " " +############################################################################## +set -x + +loopnum=$1 +cmodel=$2 +ymdh=$3 +pert=$4 +TRKDATA=$5 + +USE_OPER_VITALS=YES +# USE_OPER_VITALS=NO +# USE_OPER_VITALS=INIT_ONLY + +############################################################################## +# +# FLOW OF CONTROL +# +# 1. Define data directories and file names for the input model +# 2. Process input starting date/cycle information +# 3. Update TC Vitals file and select storms to be processed +# 4. Cut apart input GRIB files to select only the needed parms and hours +# 5. Execute the tracker +# 6. Copy the output track files to various locations +# +############################################################################## +msg="has begun for ${cmodel} ${pert} at ${cyc}z" +postmsg "$jlogfile" "$msg" + +# This script runs the hurricane tracker using operational GRIB model output. +# This script makes sure that the data files exist, it then pulls all of the +# needed data records out of the various GRIB forecast files and puts them +# into one, consolidated GRIB file, and then runs a program that reads the TC +# Vitals records for the input day and updates the TC Vitals (if necessary). +# It then runs gettrk, which actually does the tracking. +# +# Environmental variable inputs needed for this scripts: +# PDY -- The date for data being processed, in YYYYMMDD format +# cyc -- The numbers for the cycle for data being processed (00, 06, 12, 18) +# cmodel -- Model being processed (gfs, mrf, ukmet, ecmwf, nam, ngm, ngps, +# gdas, gfdl, ens (ncep ensemble)) +# envir -- 'prod' or 'test' +# SENDCOM -- 'YES' or 'NO' +# stormenv -- This is only needed by the tracker run for the GFDL model. +# 'stormenv' contains the name/id that is used in the input +# grib file names. +# pert -- This is only needed by the tracker run for the NCEP ensemble. +# 'pert' contains the ensemble member id (e.g., n2, p4, etc.) +# which is used as part of the grib file names. +# +# For testing script interactively in non-production set following vars: +# gltrkdir - Directory for output tracks +# archsyndir - Directory with syndir scripts/exec/fix +# + +qid=$$ +#----------------------------------------------# +# Get input date information # +#----------------------------------------------# + +export jobid=${jobid:-testjob} +export SENDCOM=${SENDCOM:-NO} + +# Define tracker working directory for this ensemble member + +if [ ! -d $TRKDATA ] +then + mkdir -p $TRKDATA +fi +cd $TRKDATA + +if [ ${#PDY} -eq 0 -o ${#cyc} -eq 0 -o ${#cmodel} -eq 0 ] +then + set +x + echo " " + echo "FATAL ERROR: Something wrong with input parameters." + echo "PDY= ${PDY}, cyc= ${cyc}, cmodel= ${cmodel}" + set -x + err_exit "FAILED ${jobid} -- BAD INPUTS AT LINE $LINENO IN TRACKER SCRIPT - ABNORMAL EXIT" +else + set +x + echo " " + echo " #-----------------------------------------------------------------#" + echo " At beginning of tracker script, the following imported variables " + echo " are defined: " + echo " PDY ................................... $PDY" + echo " cyc ................................... $cyc" + echo " cmodel ................................ $cmodel" + echo " jobid ................................. $jobid" + echo " envir ................................. $envir" + echo " SENDCOM ............................... $SENDCOM" + echo " " + set -x +fi + +scc=`echo ${PDY} | cut -c1-2` +syy=`echo ${PDY} | cut -c3-4` +smm=`echo ${PDY} | cut -c5-6` +sdd=`echo ${PDY} | cut -c7-8` +shh=${cyc} +symd=`echo ${PDY} | cut -c3-8` +syyyy=`echo ${PDY} | cut -c1-4` +CENT=`echo ${PDY} | cut -c1-2` + +#------J.Peng----01-21-2015--------- +archsyndir=${archsyndir:-${COMINsyn:?}} +gltrkdir=${gltrkdir:-${COMOUThur:?}} + +#----------------------------------------------------------------# +# +# --- Define data directories and data file names --- +# +# Convert the input model to lowercase letters and check to see +# if it's a valid model, and assign a model ID number to it. +# This model ID number is passed into the Fortran program to +# let the program know what set of forecast hours to use in the +# ifhours array. Also, set the directories for the operational +# input GRIB data files and create templates for the file names. +# +#----------------------------------------------------------------# + +cmodel=`echo ${cmodel} | tr "[A-Z]" "[a-z]"` + +#---- tracking variable list------------------------------------ +user_wants_to_track_zeta850='y' +user_wants_to_track_zeta700='y' +user_wants_to_track_wcirc850='y' +user_wants_to_track_wcirc700='y' +user_wants_to_track_gph850='y' +user_wants_to_track_gph700='y' +user_wants_to_track_mslp='y' +user_wants_to_track_wcircsfc='n' +user_wants_to_track_zetasfc='n' +user_wants_to_track_thick500850='n' +user_wants_to_track_thick200850='n' +user_wants_to_track_thick200500='n' + +case ${cmodel} in + gdas) set +x; echo " " ; + echo " ++ operational FV3-GDAS chosen" ; + echo " "; set -x ; +# gdasdir=${gdasdir:-${COMINgdas:?}/${cyc}} ; + gdasdir=${gdasdir:-${COMINgdas:?}/${cyc}/${COMPONENT}} ; + gdasgfile=gdas.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-3} ; + fcstlen=${FHMAX_CYCLONE:-9} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=8 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=72 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + +# PHASEFLAG='n' ; + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="gdas" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="gdas" ;; + + gfs) set +x; echo " " ; + echo " ++ operational FV3-GFS chosen" ; + echo " "; set -x ; +# gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}} ; + gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}/${COMPONENT}} ; + gfsgfile=gfs.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=1 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=15 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + +# PHASEFLAG='n' ; + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="avno" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="avno" ;; + + ens) set +x; echo " " ; + echo " ++ operational ensemble member ${pert} chosen"; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + + ensdira=${ensdira:-${COMINgefs:?}/pgrb2ap5} ; + ensgfilea=ge${pert}.t${cyc}z.pgrb2a.0p50.f ; + ensdirb=${ensdirb:-${COMINgefs:?}/pgrb2bp5} ; + ensgfileb=ge${pert}.t${cyc}z.pgrb2b.0p50.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=10 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="a${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="a${pert_posneg}${pert_num}" ;; + + cmc) set +x; echo " " ; + echo " ++ operational CMC chosen" ; + echo " "; set -x ; + cmcdir=${cmcdir:-${DCOM:?}} ; + cmcgfile=CMC_glb_latlon.24x.24_${PDY}${cyc}_P ; + cmcgfile2=_NCEP.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=15 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=39 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="cmc " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="cmc" ;; + + cens) set +x; echo " " ; + echo " ++ Canadian ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + ccedir=${ccedir:-${DCOM:?}} ; + ccegfile=${PDY}${cyc}_CMC_naefs_hr_latlon0p5x0p5_P ; + ccegfile2=_0${pert_num}.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=16 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="c${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="c${pert_posneg}${pert_num}" ;; + + fens) set +x; echo " " ; + echo " ++ FNMOC ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + fensdir=${fensdir:-${DCOM:?}} ; + fensgfile=ENSEMBLE.MET.fcst_et0${pert_num}. ; + gensgfile=.${PDY}${cyc} ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=22 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="n${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="n${pert_posneg}${pert_num}" ;; + + ngps) set +x; echo " " ; + echo " ++ operational NAVGEM chosen" ; + echo " "; set -x ; + + ngpsdir=${ngpsdir:-${DCOM:?}} ; + ngpsgfile=US058GMET-OPSbd2.NAVGEM ; + ngemgfile=-${PDY}${cyc}-NOAA-halfdeg.gr2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-180} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=7 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=29 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="ngx " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="ngx" ;; + + *) msg="FATAL ERROR: Model $cmodel is not recognized." ; + echo "$msg"; postmsg $jlogfile "$msg" ; + err_exit "FAILED ${jobid} -- UNKNOWN cmodel IN TRACKER SCRIPT - ABNORMAL EXIT";; + +esac + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${cmodel} = "gfs" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300)" + + elif [ ${cmodel} = "gdas" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300)" + + elif [ ${cmodel} = "ens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300)" + + elif [ ${cmodel} = "cens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:300|TMP:500|TMP:250)" + + elif [ ${cmodel} = "cmc" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250)" + + elif [ ${cmodel} = "fens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250)" + + elif [ ${cmodel} = "ngps" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:400|HGT:300|TMP:500|TMP:400|TMP:300)" + + fi + + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc GH:300 GH:400 GH:500 GH:925 T:300 T:400 T:500" +else + if [ ${cmodel} = "gfs" ]; then + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET)" + elif [ ${cmodel} = "gdas" ]; then + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET)" + elif [ ${cmodel} = "ens" ]; then + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET)" + else + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL)" + fi + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc" +fi + +#---------------------------------------------------------------# +# +# -------- TC Vitals processing -------- +# +# Check Steve Lord's operational tcvitals file to see if any +# vitals records were processed for this time by his system. +# If there were, then you'll find a file in /com/gfs/prod/gfs.yymmdd +# with the vitals in it. Also check the raw TC Vitals file in +# /com/arch/prod/syndat , since this may contain storms that Steve's +# system ignored (Steve's system will ignore all storms that are +# either over land or very close to land); We still want to track +# these inland storms, AS LONG AS THEY ARE NHC STORMS (don't +# bother trying to track inland storms that are outside of NHC's +# domain of responsibility -- we don't need that info). +# UPDATE 3/27/09 MARCHOK: The SREF is run at off-synoptic times +# (03,09,15,21Z). There are no tcvitals issued at these offtimes, +# so the updating of the "old" tcvitals is critical for running +# the tracker on SREF. For updating the old tcvitals for SREF, +# we need to look 3h back, not 6h as for the other models that +# run at synoptic times. +#--------------------------------------------------------------# + +#old_ymdh=`${NDATE:?} -${vit_incr} ${PDY}${cyc}` +old_ymdh=`${NDATE:?} -6 ${PDY}${cyc}` +old_4ymd=`echo ${old_ymdh} | cut -c1-8` +old_ymd=`echo ${old_ymdh} | cut -c3-8` +old_hh=`echo ${old_ymdh} | cut -c9-10` +old_str="${old_ymd} ${old_hh}00" + +#future_ymdh=`${NDATE:?} ${vit_incr} ${PDY}${cyc}` +future_ymdh=`${NDATE:?} 6 ${PDY}${cyc}` +future_4ymd=`echo ${future_ymdh} | cut -c1-8` +future_ymd=`echo ${future_ymdh} | cut -c3-8` +future_hh=`echo ${future_ymdh} | cut -c9-10` +future_str="${future_ymd} ${future_hh}00" + +if [ ${modtyp} = 'global' ] +then + #synvitdir=${COMINgfs:?}/${cyc}/${COMPONENT} + synvitdir=${COMINgfs:?} + synvitfile=gfs.t${cyc}z.syndata.tcvitals.tm00 + synvitold_dir=${synvitdir%.*}.${old_4ymd}/${old_hh}/${COMPONENT} + synvitold_file=gfs.t${old_hh}z.syndata.tcvitals.tm00 + synvitfuture_dir=${synvitdir%.*}.${future_4ymd}/${future_hh}/${COMPONENT} + synvitfuture_file=gfs.t${future_hh}z.syndata.tcvitals.tm00 +else +# synvitdir=${COMROOT}/nam/prod/nam.${PDY} + synvitdir=${COMINnam:?} + synvitfile=nam.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/nam/prod/nam.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=nam.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/nam/prod/nam.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=nam.t${future_hh}z.syndata.tcvitals.tm00 +fi + +set +x +echo " " +echo " -----------------------------" +echo " " +echo " Now sorting and updating the TC Vitals file. Please wait...." +echo " " +set -x + +current_str="${symd} ${cyc}00" + +if [ -s ${synvitdir}/${synvitfile} -o\ + -s ${synvitold_dir}/${synvitold_file} -o\ + -s ${synvitfuture_dir}/${synvitfuture_file} ] +then + grep "${old_str}" ${synvitold_dir}/${synvitold_file} \ + >${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${synvitdir}/${synvitfile} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${future_str}" ${synvitfuture_dir}/${synvitfuture_file} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} +else + set +x + echo " " + echo " There is no (synthetic) TC vitals file for ${cyc}z in ${synvitdir}," + echo " nor is there a TC vitals file for ${old_hh}z in ${synvitold_dir}." + echo " nor is there a TC vitals file for ${future_hh}z in ${synvitfuture_dir}," + echo " Checking the raw TC Vitals file ....." + echo " " + set -x +fi + +# Take the vitals from Steve Lord's /com/gfs/prod tcvitals file, +# and cat them with the NHC-only vitals from the raw, original +# /com/arch/prod/synda_tcvitals file. Do this because the nwprod +# tcvitals file is the original tcvitals file, and Steve runs a +# program that ignores the vitals for a storm that's over land or +# even just too close to land, and for tracking purposes for the +# US regional models, we need these locations. Only include these +# "inland" storm vitals for NHC (we're not going to track inland +# storms that are outside of NHC's domain of responsibility -- we +# don't need that info). +# UPDATE 5/12/98 MARCHOK: awk logic is added to screen NHC +# vitals such as "89E TEST", since TPC +# does not want tracks for such storms. + +grep "${old_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${current_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${future_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + + +# IMPORTANT: When "cat-ing" these files, make sure that the vitals +# files from the "raw" TC vitals files are first in order and Steve's +# TC vitals files second. This is because Steve's vitals file has +# been error-checked, so if we have a duplicate tc vitals record in +# these 2 files (very likely), program supvit.x below will +# only take the last vitals record listed for a particular storm in +# the vitals file (all previous duplicates are ignored, and Steve's +# error-checked vitals records are kept). + +cat ${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} ${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} \ + >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +#--------------------------------------------------------------# +# Now run a fortran program that will read all the TC vitals +# records for the current dtg and the dtg from 6h ago, and +# sort out any duplicates. If the program finds a storm that +# was included in the vitals file 6h ago but not for the current +# dtg, this program updates the 6h-old first guess position +# and puts these updated records as well as the records from +# the current dtg into a temporary vitals file. It is this +# temporary vitals file that is then used as the input for the +# tracking program. +#--------------------------------------------------------------# + +#oldymdh=`${NDATE:?} -${vit_incr} ${PDY}${cyc}` +oldymdh=`${NDATE:?} -6 ${PDY}${cyc}` +oldyy=`echo ${oldymdh} | cut -c3-4` +oldmm=`echo ${oldymdh} | cut -c5-6` +olddd=`echo ${oldymdh} | cut -c7-8` +oldhh=`echo ${oldymdh} | cut -c9-10` +oldymd=${oldyy}${oldmm}${olddd} + +futureymdh=`${NDATE:?} 6 ${PDY}${cyc}` +futureyy=`echo ${futureymdh} | cut -c3-4` +futuremm=`echo ${futureymdh} | cut -c5-6` +futuredd=`echo ${futureymdh} | cut -c7-8` +futurehh=`echo ${futureymdh} | cut -c9-10` +futureymd=${futureyy}${futuremm}${futuredd} + +echo "&datenowin dnow%yy=${syy}, dnow%mm=${smm}," >${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&dateoldin dold%yy=${oldyy}, dold%mm=${oldmm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dold%dd=${olddd}, dold%hh=${oldhh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&datefuturein dfuture%yy=${futureyy}, dfuture%mm=${futuremm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dfuture%dd=${futuredd}, dfuture%hh=${futurehh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&hourinfo vit_hr_incr=${vit_incr}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period." + echo "!!! File ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} is empty." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi + +fi + +# For tcgen cases, filter to use only vitals from the ocean +# basin of interest.... + +if [ ${trkrtype} = 'tcgen' ] + then + + if [ ${numvitrecs} -gt 0 ] + then + + fullvitfile=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + cp $fullvitfile ${TRKDATA}/vitals.all_basins.${atcfout}.${PDY}${cyc} + basin=` echo $regtype | cut -c1-2` + + if [ ${basin} = 'al' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "L") print $0}' \ + >${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + if [ ${basin} = 'ep' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "E") print $0}' \ + >${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + if [ ${basin} = 'wp' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "W") print $0}' \ + >${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + + cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + + fi + +fi + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Before running the program to read, sort and update the vitals, +# first run the vitals through some awk logic, the purpose of +# which is to convert all the 2-digit years into 4-digit years. +# We need this logic to ensure that all the vitals going +# into supvit.f have uniform, 4-digit years in their records. +# +# 1/8/2000: sed code added by Tim Marchok due to the fact that +# some of the vitals were getting past the syndata/qctropcy +# error-checking with a colon in them; the colon appeared +# in the character immediately to the left of the date, which +# was messing up the "(length($4) == 8)" statement logic. +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +sed -e "s/\:/ /g" ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} > ${TRKDATA}/tempvit +mv ${TRKDATA}/tempvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +awk ' +{ + yycheck = substr($0,20,2) + if ((yycheck == 20 || yycheck == 19) && (length($4) == 8)) { + printf ("%s\n",$0) + } + else { + if (yycheck >= 0 && yycheck <= 50) { + printf ("%s20%s\n",substr($0,1,19),substr($0,20)) + } + else { + printf ("%s19%s\n",substr($0,1,19),substr($0,20)) + } + } +} ' ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 + +mv ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +if [ ${numvitrecs} -gt 0 ] +then + + export pgm=supvit_g2 + . prep_step + + export FORT31=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + export FORT51=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_g2 <${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + suvrcc=$? + + if [ ${suvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit.x, " + echo "!!! which is the program that updates the TC Vitals file." + echo "!!! Return code from supvit.x = ${suvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING supvit_g2 IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + + touch ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#----------------------------------------------------------------- +# In this section, check to see if the user requested the use of +# operational TC vitals records for the initial time only. This +# option might be used for a retrospective medium range forecast +# in which the user wants to initialize with the storms that are +# currently there, but then let the model do its own thing for +# the next 10 or 14 days.... + + +if [ ${USE_OPER_VITALS} = 'INIT_ONLY' ]; then + + if [ ${init_flag} = 'yes' ]; then + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are currently at." + echo " " + set -x + else + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are now *PAST*." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + fi + +elif [ ${USE_OPER_VITALS} = 'NO' ]; then + + set +x + echo " " + echo "NOTE: User has requested that historical vitals not be used...." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#------------------------------------------------------------------# +# Now select all storms to be processed, that is, process every +# storm that's listed in the updated vitals file for the current +# forecast hour. If there are no storms for the current time, +# then exit. +#------------------------------------------------------------------# + +numvitrecs=`cat ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period " + echo "!!! in the UPDATED vitals file." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi +fi + +set +x +echo " " +echo " *--------------------------------*" +echo " | STORM SELECTION |" +echo " *--------------------------------*" +echo " " +set -x + +ict=1 +while [ $ict -le 15 ] +do + stormflag[${ict}]=3 + let ict=ict+1 +done + +dtg_current="${symd} ${cyc}00" +stormmax=` grep "${dtg_current}" ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +if [ ${stormmax} -gt 15 ] +then + stormmax=15 +fi + +sct=1 +while [ ${sct} -le ${stormmax} ] +do + stormflag[${sct}]=1 + let sct=sct+1 +done + + +#---------------------------------------------------------------# +# +# -------- "Genesis" Vitals processing -------- +# +# May 2006: This entire genesis tracking system is being +# upgraded to more comprehensively track and categorize storms. +# One thing that has been missing from the tracking system is +# the ability to keep track of storms from one analysis cycle +# to the next. That is, the current system has been very +# effective at tracking systems within a forecast, but we have +# no methods in place for keeping track of storms across +# difference initial times. For example, if we are running +# the tracker on today's 00z GFS analysis, we will get a +# position for various storms at the analysis time. But then +# if we go ahead and run again at 06z, we have no way of +# telling the tracker that we know about the 00z position of +# this storm. We now address that problem by creating +# "genesis" vitals, that is, when a storm is found at an +# analysis time, we not only produce "atcfunix" output to +# detail the track & intensity of a found storm, but we also +# produce a vitals record that will be used for the next +# run of the tracker script. These "genesis vitals" records +# will be of the format: +# +# YYYYMMDDHH_AAAH_LLLLX_TYP +# +# Where: +# +# YYYYMMDDHH = Date the storm was FIRST identified +# by the tracker. +# AAA = Abs(Latitude) * 10; integer value +# H = 'N' for norther hem, 'S' for southern hem +# LLLL = Abs(Longitude) * 10; integer value +# X = 'E' for eastern hem, 'W' for western hem +# TYP = Tropical cyclone storm id if this is a +# tropical cyclone (e.g., "12L", or "09W", etc). +# If this is one that the tracker instead "Found +# On the Fly (FOF)", we simply put those three +# "FOF" characters in there. +genvitfile=${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} +touch ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + +if [ -f $genvitfile ]; then + d6ago_ymdh=` ${NDATE:?} -6 ${PDY}${cyc}` + d6ago_4ymd=` echo ${d6ago_ymdh} | cut -c1-8` + d6ago_ymd=` echo ${d6ago_ymdh} | cut -c3-8` + d6ago_hh=` echo ${d6ago_ymdh} | cut -c9-10` + d6ago_str="${d6ago_ymd} ${d6ago_hh}00" + + d6ahead_ymdh=` ${NDATE:?} 6 ${PDY}${cyc}` + d6ahead_4ymd=` echo ${d6ahead_ymdh} | cut -c1-8` + d6ahead_ymd=` echo ${d6ahead_ymdh} | cut -c3-8` + d6ahead_hh=` echo ${d6ahead_ymdh} | cut -c9-10` + d6ahead_str="${d6ahead_ymd} ${d6ahead_hh}00" + + syyyym6=` echo ${d6ago_ymdh} | cut -c1-4` + smmm6=` echo ${d6ago_ymdh} | cut -c5-6` + sddm6=` echo ${d6ago_ymdh} | cut -c7-8` + shhm6=` echo ${d6ago_ymdh} | cut -c9-10` + + syyyyp6=` echo ${d6ahead_ymdh} | cut -c1-4` + smmp6=` echo ${d6ahead_ymdh} | cut -c5-6` + sddp6=` echo ${d6ahead_ymdh} | cut -c7-8` + shhp6=` echo ${d6ahead_ymdh} | cut -c9-10` + + set +x + echo " " + echo " d6ago_str= --->${d6ago_str}<---" + echo " current_str= --->${current_str}<---" + echo " d6ahead_str= --->${d6ahead_str}<---" + echo " " + #echo " Listing and contents of ${genvitfile} follow " + #echo " for the times 6h ago, current and 6h ahead:" + #echo " " + set -x + + #ls -la ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + #cat ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + + grep "${d6ago_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${d6ahead_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + num_gen_vits=`cat ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} | wc -l` +else + echo "WARNING: Genesis Vitals from previous run(s) not found!" +fi + +echo "&datenowin dnow%yy=${syyyy}, dnow%mm=${smm}," >${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6agoin d6ago%yy=${syyyym6}, d6ago%mm=${smmm6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ago%dd=${sddm6}, d6ago%hh=${shhm6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6aheadin d6ahead%yy=${syyyyp6}, d6ahead%mm=${smmp6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ahead%dd=${sddp6}, d6ahead%hh=${shhp6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + +num_gen_vits=0 + +if [ ${num_gen_vits} -gt 0 ] +then + export pgm=supvit_gen + . prep_step + + export FORT31=${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + export FORT51=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_gen <${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + sgvrcc=$? + + if [ ${sgvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit_gen, " + echo "!!! which is the program that updates the genesis vitals file." + echo "!!! Return code from supvit_gen = ${sgvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING SUPVIT_GEN IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + + touch ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + +fi + + +#-----------------------------------------------------------------# +# +# ------ CUT APART INPUT GRIB FILES ------- +# +# For the selected model, cut apart the GRIB input files in order +# to pull out only the variables that we need for the tracker. +# Put these selected variables from all forecast hours into 1 big +# GRIB file that we'll use as input for the tracker. +# +#-----------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------" +echo " NOW CUTTING APART INPUT GRIB FILES TO " +echo " CREATE 1 BIG GRIB INPUT FILE " +echo " -----------------------------------------" +echo " " +set -x + +regflag=`grep NHC ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +# -------------------------------------------------- +# Process GDAS data +# -------------------------------------------------- + +if [ ${model} -eq 8 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gdasgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gdasgribfile.${PDY}${cyc} + fi + + rm ${TRKDATA}/master.gdasgribfile.${PDY}${cyc}.f* + rm ${TRKDATA}/gdasgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gdasgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gdas wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gdasdir:?}/${gdasgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GDAS File missing: ${gdasdir:?}/${gdasgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GDAS FILE IN extrkr_gfs.sh: ${gdasdir:?}/${gdasgfile}${fhour}" + fi + + gfile=${gdasdir}/${gdasgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gdasgribfile.${PDY}${cyc}.f${fhour} + + gdas_master_file=${TRKDATA}/master.gdasgribfile.${PDY}${cyc}.f${fhour} + gdas_cat_file=${TRKDATA}/gdasgribfile.${PDY}${cyc} + cat ${gdas_master_file} >>${gdas_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gdasgribfile.${PDY}${cyc} ${TRKDATA}/gdasixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + + catfile=${TRKDATA}/gdas.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in 300-500hPa T-mean for model= $cmodel and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gdasgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gdasixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in 300-500hPa T-mean for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + + fi + + gfile=${TRKDATA}/gdasgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/gdasixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gdasgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gdasixfile.${PDY}${cyc} + +fi + +# -------------------------------------------------- +# Process GFS data +# -------------------------------------------------- + +if [ ${model} -eq 1 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gfsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc} + fi + + rm ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f* + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gfsgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gfs wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gfsdir:?}/${gfsgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GFS File missing: ${gfsdir}/${gfsgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GFS FILE IN extrkr_gfs.sh: ${gfsdir}/${gfsgfile}${fhour}" + fi + + gfile=${gfsdir}/${gfsgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + + gfs_master_file=${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + gfs_cat_file=${TRKDATA}/gfsgribfile.${PDY}${cyc} + cat ${gfs_master_file} >>${gfs_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gfsgribfile.${PDY}${cyc} ${TRKDATA}/gfsixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + + catfile=${TRKDATA}/gfs.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in 300-500hPa T-mean for model= $cmodel and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in 300-500hPa T-mean for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + + fi + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gfsixfile.${PDY}${cyc} + +fi + +# -------------------------------------------------- +# Process NCEP Ensemble perturbation, if selected +# -------------------------------------------------- + +if [ ${model} -eq 10 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ensdira:?}/${ensgfilea}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: ENSEMBLE ${PERT} File missing: ${ensdira}/${ensgfilea}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING GEFS FILE IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${TRKDATA}/${ensgfilea}${fhour} + cat ${ensdira}/${ensgfilea}${fhour} ${ensdirb}/${ensgfileb}${fhour} > $gfile + + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} + + cat ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} >> ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + done + + ${GRB2INDEX:?} ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/ens${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in 300-500hPa T-mean for pert= $pert and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour for $cmodel $pert " + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in 300-500hPa T-mean for pert= $pert and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + + gfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------------------------------ +# Process Canadian (CMC) hi-res deterministic, if selected +# ------------------------------------------------------ + +if [ ${model} -eq 15 ]; then + + if [ $loopnum -eq 1 ]; then + if [ -s ${TRKDATA}/cmcgribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/cmcgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + if [ ! -s ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} ]; then + set +x + echo " " + echo "FATAL ERROR: CMC File missing ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + fi + + gfile=${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + done +# J.Peng--04-05-2017--changing grid from 180E to 0E------------ +# wgrib2 junk -small_grib 0:359.76 -90:90 junk2 +# J.Peng--10-15-2019----uncomment the next 4 lines ------- + cp ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/junk + rm -f ${TRKDATA}/cmcgribfile.${PDY}${cyc} + ${WGRIB2} ${TRKDATA}/junk -small_grib 0:359.76 -90:90 ${TRKDATA}/junk2 + cp ${TRKDATA}/junk2 ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + ${GRB2INDEX:?} ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/cmcixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +fi + +# ------------------------------------------------------ +# Process Canadian Ensemble perturbation, if selected +# ------------------------------------------------------ +if [ ${model} -eq 16 ]; then + if [ $loopnum -eq 1 ]; then + + if [ -s ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + + if [ ! -s ${ccedir:?}/${ccegfile}${fhour}${ccegfile2} ]; then + set +x + echo " " + echo "FATAL ERROR: CANADIAN ENSEMBLE ${PERT} missing: ${ccegfile}${fhour}${ccegfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING CMC ENSEMBLE FILE IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${ccedir}/${ccegfile}${fhour}${ccegfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk +# Process the cyclone phase variables, if requested + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + gparm=11 + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour " + echo "rcc= $rcc EXITING....${cmodel}.${pert}.${PDY}${cyc} " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------ +# Process FNMOC Ensemble, if selected +# ------------------------------ +if [ ${model} -eq 22 ]; then + if [ $loopnum -eq 1 ]; then + + if [ -s ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ]; then + rm ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + fensfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + + let attempts=1 + while [ $attempts -le 30 ]; do + if [ -s $fensfile ]; then + break + else + sleep 60 + let attempts=attempts+1 + fi + done + if [ $attempts -gt 30 ] && [ ! -s $fensfile ]; then + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "$fensfile" >> ${DATA}/missing_fens.txt + exit + else + err_exit "$fensfile still not available after waiting 30 minutes... exiting" + fi + fi + #${WGRIB2:?} -set_byte 1 11 1 -s $fensfile >fens.ix # set_byte resets local table to 1 to remove error messages + + gfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + gparm=11 + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour " + echo "rcc= $rcc EXITING....${cmodel}.${pert}.${PDY}${cyc} " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ixfile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} +fi + +# ------------------------------ +# Process NAVGEM, if selected +# ------------------------------ +if [ ${model} -eq 7 ]; then + + if [ $loopnum -eq 1 ]; then + if [ -s ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + if [ ! -s ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} ]; then + set +x + echo " " + echo "FATAL ERROR: NAVGEM File missing ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" >> ${DATA}/missing_ngps.txt + exit + else + err_exit "MISSING NAVGEM FILE at $0:$LINENO" + fi + fi + + gfile=${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ${TRKDATA}/ngpsixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ngpsixfile.${PDY}${cyc} +fi + +#------------------------------------------------------------------------# +# Now run the tracker # +#------------------------------------------------------------------------# +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW EXECUTING TRACKER......" +echo " -----------------------------------------------" +echo " " +set -x + +ist=1 +while [ $ist -le 15 ] +do + if [ ${stormflag[${ist}]} -ne 1 ] + then + set +x; echo "Storm number $ist NOT selected for processing"; set -x + else + set +x; echo "Storm number $ist IS selected for processing...."; set -x + fi + let ist=ist+1 +done + +namelist=${TRKDATA}/input.${atcfout}.${PDY}${cyc} +ATCFNAME=` echo "${atcfname}" | tr '[a-z]' '[A-Z]'` + +if [ ${cmodel} = 'sref' ]; then + export atcfymdh=` ${NDATE:?} -3 ${scc}${syy}${smm}${sdd}${shh}` +else + export atcfymdh=${scc}${syy}${smm}${sdd}${shh} +fi + +#------07-26-2019----creating the fix-file for GFS tracking ----- +if [ ${model} -eq 1 -o ${model} -eq 8 ]; then +. prep_step + +# Input files +namelist_4_fix=${TRKDATA}/namelist_4_fix_input.${PDY}${cyc} +echo "&timein maxhrs=${fcstlen}," >${namelist_4_fix} +echo " dthrs=${vit_incr}/" >>${namelist_4_fix} + +# Output file +export FORT10=${TRKDATA}/${cmodel}.tracker_leadtimes + +${EXECens_tracker}/leadtime <${namelist_4_fix} +rcc=$? + +if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to leadtime at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "leadtime - ERROR AT extrkr_gfs.sh LINE $LINENO" +fi + +fi + +#------07-26-2019----creating the fix-file for GFS tracking ----- + +#contour_interval=100.0 +#write_vit=n +#want_oci=.TRUE. + +echo "&datein inp%bcc=${scc},inp%byy=${syy},inp%bmm=${smm}," >${namelist} +echo " inp%bdd=${sdd},inp%bhh=${shh},inp%model=${model}," >>${namelist} +echo " inp%modtyp='${modtyp}'," >>${namelist} +echo " inp%lt_units='${lead_time_units}'," >>${namelist} +echo " inp%file_seq='${file_sequence}'," >>${namelist} +echo " inp%nesttyp='${nest_type}'/" >>${namelist} + +echo "&atcfinfo atcfnum=${atcfnum},atcfname='${ATCFNAME}'," >>${namelist} +echo " atcfymdh=${atcfymdh},atcffreq=${atcffreq}/" >>${namelist} + +echo "&trackerinfo trkrinfo%westbd=${trkrwbd}," >>${namelist} +echo " trkrinfo%eastbd=${trkrebd}," >>${namelist} +echo " trkrinfo%northbd=${trkrnbd}," >>${namelist} +echo " trkrinfo%southbd=${trkrsbd}," >>${namelist} +echo " trkrinfo%type='${trkrtype}'," >>${namelist} +echo " trkrinfo%mslpthresh=${mslpthresh}," >>${namelist} +echo " trkrinfo%use_backup_mslp_grad_check='${use_backup_mslp_grad_check}'," >>${namelist} +echo " trkrinfo%max_mslp_850=${max_mslp_850}," >>${namelist} +echo " trkrinfo%v850thresh=${v850thresh}," >>${namelist} +echo " trkrinfo%use_backup_850_vt_check='${use_backup_850_vt_check}'," >>${namelist} +echo " trkrinfo%gridtype='${modtyp}'," >>${namelist} +echo " trkrinfo%enable_timing=1," >>${namelist} +echo " trkrinfo%contint=${contour_interval}," >>${namelist} +echo " trkrinfo%want_oci=${want_oci}," >>${namelist} +echo " trkrinfo%out_vit='${write_vit}'," >>${namelist} +echo " trkrinfo%use_land_mask='${use_land_mask}'," >>${namelist} +echo " trkrinfo%inp_data_type='${inp_data_type}'," >>${namelist} +echo " trkrinfo%gribver=${gribver}," >>${namelist} +echo " trkrinfo%g2_jpdtn=${g2_jpdtn}," >>${namelist} +echo " trkrinfo%g2_mslp_parm_id=${g2_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_mslp_parm_id=${g1_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_typ=${g1_sfcwind_lev_typ}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_val=${g1_sfcwind_lev_val}/" >>${namelist} + +echo "&phaseinfo phaseflag='${PHASEFLAG}'," >>${namelist} +echo " phasescheme='${PHASE_SCHEME}'," >>${namelist} +echo " wcore_depth=${WCORE_DEPTH}/" >>${namelist} + +echo "&structinfo structflag='${STRUCTFLAG}'," >>${namelist} +echo " ikeflag='${IKEFLAG}'/" >>${namelist} + +echo "&fnameinfo gmodname='${atcfname}'," >>${namelist} +echo " rundescr='${rundescr}'," >>${namelist} +echo " atcfdescr='${atcfdescr}'/" >>${namelist} + +echo "&waitinfo use_waitfor='n'," >>${namelist} +echo " wait_min_age=10," >>${namelist} +echo " wait_min_size=100," >>${namelist} +echo " wait_max_wait=1800," >>${namelist} +echo " wait_sleeptime=5," >>${namelist} +echo " per_fcst_command=''/" >>${namelist} + +echo "&netcdflist netcdfinfo%num_netcdf_vars=${ncdf_num_netcdf_vars}," >>${namelist} +echo " netcdfinfo%netcdf_filename='${netcdffile}'," >>${namelist} +echo " netcdfinfo%rv850name='${ncdf_rv850name}'," >>${namelist} +echo " netcdfinfo%rv700name='${ncdf_rv700name}'," >>${namelist} +echo " netcdfinfo%u850name='${ncdf_u850name}'," >>${namelist} +echo " netcdfinfo%v850name='${ncdf_v850name}'," >>${namelist} +echo " netcdfinfo%u700name='${ncdf_u700name}'," >>${namelist} +echo " netcdfinfo%v700name='${ncdf_v700name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%mslpname='${ncdf_mslpname}'," >>${namelist} +echo " netcdfinfo%usfcname='${ncdf_usfcname}'," >>${namelist} +echo " netcdfinfo%vsfcname='${ncdf_vsfcname}'," >>${namelist} +echo " netcdfinfo%u500name='${ncdf_u500name}'," >>${namelist} +echo " netcdfinfo%v500name='${ncdf_v500name}'," >>${namelist} +echo " netcdfinfo%tmean_300_500_name='${ncdf_tmean_300_500_name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z200name='${ncdf_z200name}'," >>${namelist} +echo " netcdfinfo%lmaskname='${ncdf_lmaskname}'," >>${namelist} +echo " netcdfinfo%z900name='${ncdf_z900name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z800name='${ncdf_z800name}'," >>${namelist} +echo " netcdfinfo%z750name='${ncdf_z750name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%z650name='${ncdf_z650name}'," >>${namelist} +echo " netcdfinfo%z600name='${ncdf_z600name}'," >>${namelist} +echo " netcdfinfo%z550name='${ncdf_z550name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z450name='${ncdf_z450name}'," >>${namelist} +echo " netcdfinfo%z400name='${ncdf_z400name}'," >>${namelist} +echo " netcdfinfo%z350name='${ncdf_z350name}'," >>${namelist} +echo " netcdfinfo%z300name='${ncdf_z300name}'," >>${namelist} +echo " netcdfinfo%time_name='${ncdf_time_name}'," >>${namelist} +echo " netcdfinfo%lon_name='${ncdf_lon_name}'," >>${namelist} +echo " netcdfinfo%lat_name='${ncdf_lat_name}'," >>${namelist} +echo " netcdfinfo%time_units='${ncdf_time_units}'/" >>${namelist} + +echo "&parmpreflist user_wants_to_track_zeta850='${user_wants_to_track_zeta850}'," >>${namelist} +echo " user_wants_to_track_zeta700='${user_wants_to_track_zeta700}'," >>${namelist} +echo " user_wants_to_track_wcirc850='${user_wants_to_track_wcirc850}'," >>${namelist} +echo " user_wants_to_track_wcirc700='${user_wants_to_track_wcirc700}'," >>${namelist} +echo " user_wants_to_track_gph850='${user_wants_to_track_gph850}'," >>${namelist} +echo " user_wants_to_track_gph700='${user_wants_to_track_gph700}'," >>${namelist} +echo " user_wants_to_track_mslp='${user_wants_to_track_mslp}'," >>${namelist} +echo " user_wants_to_track_wcircsfc='${user_wants_to_track_wcircsfc}'," >>${namelist} +echo " user_wants_to_track_zetasfc='${user_wants_to_track_zetasfc}'," >>${namelist} +echo " user_wants_to_track_thick500850='${user_wants_to_track_thick500850}'," >>${namelist} +echo " user_wants_to_track_thick200500='${user_wants_to_track_thick200500}'," >>${namelist} +echo " user_wants_to_track_thick200850='${user_wants_to_track_thick200850}'/" >>${namelist} + +echo "&verbose verb=3,verb_g2=0/" >>${namelist} + +export pgm=gettrk_gfs +. prep_step + +export FORT11=${gribfile} +export FORT12=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} +export FORT14=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +if [ ${model} -eq 1 -o ${model} -eq 8 ]; then + export FORT15=${TRKDATA}/${cmodel}.tracker_leadtimes +else + export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes +fi + +#if [ $FHOUT_CYCLONE -eq 3 ]; then export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes_3hr ; fi +#if [ $FHMAX_CYCLONE -eq 180 ]; then export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes_180 ; fi +#if [ $vit_incr -eq 3 ]; then export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes_3hr ; fi +export FORT31=${ixfile} + +#if [ -s ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} ]; then +# cp ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} tcvit_rsmc_storms.txt +#fi +#if [ -s ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} ]; then +# cp ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} tcvit_genesis_storms.txt +#fi + +if [ ${trkrtype} = 'tracker' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT61=${TRKDATA}/trak.${atcfout}.all.${stormenv}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${stormenv}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${stormenv}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${stormenv}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${stormenv}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${stormenv}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${stormenv}.${PDY}${cyc} + else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${PDY}${cyc} + fi +else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${regtype}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${regtype}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${regtype}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${regtype}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${regtype}.${PDY}${cyc} +fi + +if [ ${atcfname} = 'aear' ] +then + export FORT65=${TRKDATA}/trak.${atcfout}.initvitl.${PDY}${cyc} +fi + +if [ ${write_vit} = 'y' ] +then + export FORT67=${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} +fi + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${stormenv}.${PDY}${cyc} + else + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${PDY}${cyc} + fi +fi + +if [ ${STRUCTFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${stormenv}.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${stormenv}.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${stormenv}.${PDY}${cyc} + else + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${PDY}${cyc} + fi +fi + +if [ ${IKEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${stormenv}.${PDY}${cyc} + else + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${PDY}${cyc} + fi +fi + +if [ ${trkrtype} = 'midlat' -o ${trkrtype} = 'tcgen' ]; then + export FORT77=${TRKDATA}/trkrmask.${atcfout}.${regtype}.${PDY}${cyc} +fi + +msg="$pgm start for $atcfout at ${cyc}z" +postmsg "$jlogfile" "$msg" + +set +x +echo "+++ TIMING: BEFORE gettrk ---> `date`" +set -x + +set +x +echo " " +echo "TIMING: Before call to gettrk at `date`" +echo " " +set -x + +${EXECens_tracker}/gettrk_gfs <${namelist} + +gettrk_rcc=$? +if [ ${gettrk_rcc} -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR: An error occurred while running gettrk.x, " + echo "!!! which is the program that actually gets the track." + echo "!!! Return code from gettrk.x = ${gettrk_rcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING gettrk IN TRACKER SCRIPT- ABNORMAL EXIT" +fi + +set +x +echo " " +echo "TIMING: After call to gettrk at `date`" +echo " " +set -x + +set +x +echo "+++ TIMING: AFTER gettrk ---> `date`" +set -x + +#--------------------------------------------------------------# +# Now copy the output track files to different directories +#--------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW COPYING OUTPUT TRACK FILES TO COM " +echo " -----------------------------------------------" +echo " " +set -x + + +if [ -s ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} ]; then + cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh}|cut -c1-112 > ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} +# cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh}|cut -c1-95 > ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} ${TRKDATA}/long.${atcfout}.atcfunix.${PDY}${shh} + cp ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} +fi + +msg="$pgm end for $atcfout at ${cyc}z completed normally" +postmsg "$jlogfile" "$msg" + +# Copy atcf files to NHC archives. We'll use Steve Lord's original script, +# distatcf.sh, to do this, and that script requires the input atcf file to +# have the name "attk126", so first copy the file to that name, then call +# the distatcf.sh script. After that's done, then copy the full 0-72h +# track into the /com/hur/prod/global track archive file. + +if [ "$SENDCOM" = 'YES' ] +then + + glatuxarch=${glatuxarch:-${gltrkdir}/tracks.atcfunix.${syy}} + cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} >>${glatuxarch} + + if [ ${cmodel} = 'gfdl' ] + then + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} ${COMOUT}/${stormenv}.${PDY}${cyc}.trackeratcfunix + else + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} ${COMOUT}/${atcfout}.t${cyc}z.cyclone.trackatcfunix + cp ${TRKDATA}/long.${atcfout}.atcfunix.${PDY}${shh} ${COMOUT}/${atcfout}p.t${cyc}z.cyclone.trackatcfunix +# cat ${TRKDATA}/long.${atcfout}.atcfunix.${PDY}${shh} | \ +# sed s:GFSO:GFSP:g \ +# > ${COMOUT}/gfsp.t${cyc}z.cyclone.trackatcfunix + + fi + + if [ "$SENDDBN" = 'YES' ] + then + if [ ${cmodel} != 'gfdl' ] + then + $DBNROOT/bin/dbn_alert MODEL ENS_TRACKER $job ${COMOUT}/${atcfout}.t${cyc}z.cyclone.trackatcfunix + $DBNROOT/bin/dbn_alert MODEL ENS_TRACKER $job ${COMOUT}/${atcfout}p.t${cyc}z.cyclone.trackatcfunix + fi + fi + +# ------------------------------------------ +# Cat atcfunix files to storm trackers files +# ------------------------------------------ +# +# We need to parse apart the atcfunix file and distribute the forecasts to +# the necessary directories. To do this, first sort the atcfunix records +# by forecast hour (k6), then sort again by ocean basin (k1), storm number (k2) +# and then quadrant radii wind threshold (k12). Once you've got that organized +# file, break the file up by putting all the forecast records for each storm +# into a separate file. Then, for each file, find the corresponding atcfunix +# file in the storm trackers directory and dump the atcfunix records for that storm +# in there. NOTE: Only do this if the model run is NOT for the CMC or +# ECMWF ensemble. The reason is that we do NOT want to write out the individual +# member tracks to the atcfunix file. We only want to write out the ensemble +# mean track to the atcfunix file, and the mean track is calculated and written +# out in a separate script. + + if [ ${cmodel} = 'gfdl' ] + then + auxfile=${COMOUT}/${stormenv}.${PDY}${cyc}.trackeratcfunix + else + auxfile=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + fi + + sort -k6 ${auxfile} | sort -k1 -k2 -k12 >atcfunix.sorted + old_string="XX, XX" + + ict=0 + while read unixrec + do + storm_string=` echo "${unixrec}" | cut -c1-6` + if [ "${storm_string}" = "${old_string}" ] + then + echo "${unixrec}" >>atcfunix_file.${ict} + else + let ict=ict+1 + echo "${unixrec}" >atcfunix_file.${ict} + old_string="${storm_string}" + fi + done >$COMOUTatcf/${at}${NO}${syyyy}/ncep_a${at}${NO}${syyyy}.dat + if [ ${cmodel} = 'gfs' ]; then + cat atcfunix_file.$mct | sed s:AVNO:GFSO:g > gfso_atcfunix_file.$mct + cat gfso_atcfunix_file.$mct >>$COMOUTatcf/${at}${NO}${syyyy}/ncep_a${at}${NO}${syyyy}.dat + + cat atcfunix_file.$mct >>$COMOUTatcf/${at}${NO}${syyyy}/a${at}${NO}${syyyy}.dat + cat gfso_atcfunix_file.$mct >>$COMOUTatcf/${at}${NO}${syyyy}/a${at}${NO}${syyyy}.dat + fi + + set +x + echo " " + echo "+++ Adding records to TPC ATCFUNIX directory: $COMOUTatcf/${at}${NO}${syyyy}/ncep_${at}${NO}${syyyy}" + echo " " + set -x + done + fi +fi diff --git a/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh.old0206 b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh.old0206 new file mode 100755 index 0000000000..70ee9a480a --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh.old0206 @@ -0,0 +1,2624 @@ +#!/bin/ksh +export PS4=' + extrkr_gfs.sh line $LINENO: ' + +set +x +############################################################################## +echo " " +echo "------------------------------------------------" +echo " TC Track in model GRIB2 output " +echo " Models:GFS, GEFS, CENS, FENS, NAVGEM " +echo "------------J.Peng Jan.21, 2015 ----------------" +echo "Current time is: `date`" +echo " " +############################################################################## +set -x + +loopnum=$1 +cmodel=$2 +ymdh=$3 +pert=$4 +TRKDATA=$5 + +USE_OPER_VITALS=YES +# USE_OPER_VITALS=NO +# USE_OPER_VITALS=INIT_ONLY + +############################################################################## +# +# FLOW OF CONTROL +# +# 1. Define data directories and file names for the input model +# 2. Process input starting date/cycle information +# 3. Update TC Vitals file and select storms to be processed +# 4. Cut apart input GRIB files to select only the needed parms and hours +# 5. Execute the tracker +# 6. Copy the output track files to various locations +# +############################################################################## +msg="has begun for ${cmodel} ${pert} at ${cyc}z" +postmsg "$jlogfile" "$msg" + +# This script runs the hurricane tracker using operational GRIB model output. +# This script makes sure that the data files exist, it then pulls all of the +# needed data records out of the various GRIB forecast files and puts them +# into one, consolidated GRIB file, and then runs a program that reads the TC +# Vitals records for the input day and updates the TC Vitals (if necessary). +# It then runs gettrk, which actually does the tracking. +# +# Environmental variable inputs needed for this scripts: +# PDY -- The date for data being processed, in YYYYMMDD format +# cyc -- The numbers for the cycle for data being processed (00, 06, 12, 18) +# cmodel -- Model being processed (gfs, mrf, ukmet, ecmwf, nam, ngm, ngps, +# gdas, gfdl, ens (ncep ensemble)) +# envir -- 'prod' or 'test' +# SENDCOM -- 'YES' or 'NO' +# stormenv -- This is only needed by the tracker run for the GFDL model. +# 'stormenv' contains the name/id that is used in the input +# grib file names. +# pert -- This is only needed by the tracker run for the NCEP ensemble. +# 'pert' contains the ensemble member id (e.g., n2, p4, etc.) +# which is used as part of the grib file names. +# +# For testing script interactively in non-production set following vars: +# gltrkdir - Directory for output tracks +# archsyndir - Directory with syndir scripts/exec/fix +# + +qid=$$ +#----------------------------------------------# +# Get input date information # +#----------------------------------------------# + +export jobid=${jobid:-testjob} +export SENDCOM=${SENDCOM:-NO} + +# Define tracker working directory for this ensemble member + +if [ ! -d $TRKDATA ] +then + mkdir -p $TRKDATA +fi +cd $TRKDATA + +if [ ${#PDY} -eq 0 -o ${#cyc} -eq 0 -o ${#cmodel} -eq 0 ] +then + set +x + echo " " + echo "FATAL ERROR: Something wrong with input parameters." + echo "PDY= ${PDY}, cyc= ${cyc}, cmodel= ${cmodel}" + set -x + err_exit "FAILED ${jobid} -- BAD INPUTS AT LINE $LINENO IN TRACKER SCRIPT - ABNORMAL EXIT" +else + set +x + echo " " + echo " #-----------------------------------------------------------------#" + echo " At beginning of tracker script, the following imported variables " + echo " are defined: " + echo " PDY ................................... $PDY" + echo " cyc ................................... $cyc" + echo " cmodel ................................ $cmodel" + echo " jobid ................................. $jobid" + echo " envir ................................. $envir" + echo " SENDCOM ............................... $SENDCOM" + echo " " + set -x +fi + +scc=`echo ${PDY} | cut -c1-2` +syy=`echo ${PDY} | cut -c3-4` +smm=`echo ${PDY} | cut -c5-6` +sdd=`echo ${PDY} | cut -c7-8` +shh=${cyc} +symd=`echo ${PDY} | cut -c3-8` +syyyy=`echo ${PDY} | cut -c1-4` +CENT=`echo ${PDY} | cut -c1-2` + +#------J.Peng----01-21-2015--------- +archsyndir=${archsyndir:-${COMINsyn:?}} +gltrkdir=${gltrkdir:-${COMOUThur:?}} + +#----------------------------------------------------------------# +# +# --- Define data directories and data file names --- +# +# Convert the input model to lowercase letters and check to see +# if it's a valid model, and assign a model ID number to it. +# This model ID number is passed into the Fortran program to +# let the program know what set of forecast hours to use in the +# ifhours array. Also, set the directories for the operational +# input GRIB data files and create templates for the file names. +# +#----------------------------------------------------------------# + +cmodel=`echo ${cmodel} | tr "[A-Z]" "[a-z]"` + +#---- tracking variable list------------------------------------ +user_wants_to_track_zeta850='y' +user_wants_to_track_zeta700='y' +user_wants_to_track_wcirc850='y' +user_wants_to_track_wcirc700='y' +user_wants_to_track_gph850='y' +user_wants_to_track_gph700='y' +user_wants_to_track_mslp='y' +user_wants_to_track_wcircsfc='n' +user_wants_to_track_zetasfc='n' +user_wants_to_track_thick500850='n' +user_wants_to_track_thick200850='n' +user_wants_to_track_thick200500='n' + +case ${cmodel} in + gdas) set +x; echo " " ; + echo " ++ operational FV3-GDAS chosen" ; + echo " "; set -x ; + gdasdir=${gdasdir:-${COMINgdas:?}} ; + gdasgfile=gdas.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-3} ; + fcstlen=${FHMAX_CYCLONE:-9} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=8 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=72 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + +# PHASEFLAG='n' ; + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="gdas" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="gdas" ;; + + gfs) set +x; echo " " ; + echo " ++ operational FV3-GFS chosen" ; + echo " "; set -x ; + gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}} ; + gfsgfile=gfs.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=1 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=15 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + +# PHASEFLAG='n' ; + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="avno" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="avno" ;; + + ens) set +x; echo " " ; + echo " ++ operational ensemble member ${pert} chosen"; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + + ensdira=${ensdira:-${COMINgefs:?}/pgrb2ap5} ; + ensgfilea=ge${pert}.t${cyc}z.pgrb2a.0p50.f ; + ensdirb=${ensdirb:-${COMINgefs:?}/pgrb2bp5} ; + ensgfileb=ge${pert}.t${cyc}z.pgrb2b.0p50.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=10 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="a${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="a${pert_posneg}${pert_num}" ;; + + cmc) set +x; echo " " ; + echo " ++ operational CMC chosen" ; + echo " "; set -x ; + cmcdir=${cmcdir:-${DCOM:?}} ; + cmcgfile=CMC_glb_latlon.24x.24_${PDY}${cyc}_P ; + cmcgfile2=_NCEP.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=15 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=39 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="cmc " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="cmc" ;; + + cens) set +x; echo " " ; + echo " ++ Canadian ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + ccedir=${ccedir:-${DCOM:?}} ; + ccegfile=${PDY}${cyc}_CMC_naefs_hr_latlon0p5x0p5_P ; + ccegfile2=_0${pert_num}.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=16 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="c${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="c${pert_posneg}${pert_num}" ;; + + fens) set +x; echo " " ; + echo " ++ FNMOC ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + fensdir=${fensdir:-${DCOM:?}} ; + fensgfile=ENSEMBLE.MET.fcst_et0${pert_num}. ; + gensgfile=.${PDY}${cyc} ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=22 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="n${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="n${pert_posneg}${pert_num}" ;; + + ngps) set +x; echo " " ; + echo " ++ operational NAVGEM chosen" ; + echo " "; set -x ; + + ngpsdir=${ngpsdir:-${DCOM:?}} ; + ngpsgfile=US058GMET-OPSbd2.NAVGEM ; + ngemgfile=-${PDY}${cyc}-NOAA-halfdeg.gr2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-180} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=7 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=29 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="ngx " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="ngx" ;; + + *) msg="FATAL ERROR: Model $cmodel is not recognized." ; + echo "$msg"; postmsg $jlogfile "$msg" ; + err_exit "FAILED ${jobid} -- UNKNOWN cmodel IN TRACKER SCRIPT - ABNORMAL EXIT";; + +esac + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${cmodel} = "gfs" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300)" + + elif [ ${cmodel} = "gdas" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300)" + + elif [ ${cmodel} = "ens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300)" + + elif [ ${cmodel} = "cens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:300|TMP:500|TMP:250)" + + elif [ ${cmodel} = "cmc" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250)" + + elif [ ${cmodel} = "fens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250)" + + elif [ ${cmodel} = "ngps" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:400|HGT:300|TMP:500|TMP:400|TMP:300)" + + fi + + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc GH:300 GH:400 GH:500 GH:925 T:300 T:400 T:500" +else + if [ ${cmodel} = "gfs" ]; then + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET)" + elif [ ${cmodel} = "gdas" ]; then + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET)" + elif [ ${cmodel} = "ens" ]; then + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET)" + else + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL)" + fi + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc" +fi + +#---------------------------------------------------------------# +# +# -------- TC Vitals processing -------- +# +# Check Steve Lord's operational tcvitals file to see if any +# vitals records were processed for this time by his system. +# If there were, then you'll find a file in /com/gfs/prod/gfs.yymmdd +# with the vitals in it. Also check the raw TC Vitals file in +# /com/arch/prod/syndat , since this may contain storms that Steve's +# system ignored (Steve's system will ignore all storms that are +# either over land or very close to land); We still want to track +# these inland storms, AS LONG AS THEY ARE NHC STORMS (don't +# bother trying to track inland storms that are outside of NHC's +# domain of responsibility -- we don't need that info). +# UPDATE 3/27/09 MARCHOK: The SREF is run at off-synoptic times +# (03,09,15,21Z). There are no tcvitals issued at these offtimes, +# so the updating of the "old" tcvitals is critical for running +# the tracker on SREF. For updating the old tcvitals for SREF, +# we need to look 3h back, not 6h as for the other models that +# run at synoptic times. +#--------------------------------------------------------------# + +#old_ymdh=`${NDATE:?} -${vit_incr} ${PDY}${cyc}` +old_ymdh=`${NDATE:?} -6 ${PDY}${cyc}` +old_4ymd=`echo ${old_ymdh} | cut -c1-8` +old_ymd=`echo ${old_ymdh} | cut -c3-8` +old_hh=`echo ${old_ymdh} | cut -c9-10` +old_str="${old_ymd} ${old_hh}00" + +#future_ymdh=`${NDATE:?} ${vit_incr} ${PDY}${cyc}` +future_ymdh=`${NDATE:?} 6 ${PDY}${cyc}` +future_4ymd=`echo ${future_ymdh} | cut -c1-8` +future_ymd=`echo ${future_ymdh} | cut -c3-8` +future_hh=`echo ${future_ymdh} | cut -c9-10` +future_str="${future_ymd} ${future_hh}00" + +if [ ${modtyp} = 'global' ] +then +# synvitdir=${COMROOT}/gfs/prod/gfs.${PDY} + synvitdir=${COMINgfs:?}/${cyc} + synvitfile=gfs.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/gfs/prod/gfs.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd}/${old_hh} + synvitold_file=gfs.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/gfs/prod/gfs.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd}/${future_hh} + synvitfuture_file=gfs.t${future_hh}z.syndata.tcvitals.tm00 +else +# synvitdir=${COMROOT}/nam/prod/nam.${PDY} + synvitdir=${COMINnam:?} + synvitfile=nam.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/nam/prod/nam.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=nam.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/nam/prod/nam.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=nam.t${future_hh}z.syndata.tcvitals.tm00 +fi + +set +x +echo " " +echo " -----------------------------" +echo " " +echo " Now sorting and updating the TC Vitals file. Please wait...." +echo " " +set -x + +current_str="${symd} ${cyc}00" + +if [ -s ${synvitdir}/${synvitfile} -o\ + -s ${synvitold_dir}/${synvitold_file} -o\ + -s ${synvitfuture_dir}/${synvitfuture_file} ] +then + grep "${old_str}" ${synvitold_dir}/${synvitold_file} \ + >${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${synvitdir}/${synvitfile} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${future_str}" ${synvitfuture_dir}/${synvitfuture_file} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} +else + set +x + echo " " + echo " There is no (synthetic) TC vitals file for ${cyc}z in ${synvitdir}," + echo " nor is there a TC vitals file for ${old_hh}z in ${synvitold_dir}." + echo " nor is there a TC vitals file for ${future_hh}z in ${synvitfuture_dir}," + echo " Checking the raw TC Vitals file ....." + echo " " + set -x +fi + +# Take the vitals from Steve Lord's /com/gfs/prod tcvitals file, +# and cat them with the NHC-only vitals from the raw, original +# /com/arch/prod/synda_tcvitals file. Do this because the nwprod +# tcvitals file is the original tcvitals file, and Steve runs a +# program that ignores the vitals for a storm that's over land or +# even just too close to land, and for tracking purposes for the +# US regional models, we need these locations. Only include these +# "inland" storm vitals for NHC (we're not going to track inland +# storms that are outside of NHC's domain of responsibility -- we +# don't need that info). +# UPDATE 5/12/98 MARCHOK: awk logic is added to screen NHC +# vitals such as "89E TEST", since TPC +# does not want tracks for such storms. + +grep "${old_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${current_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${future_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + + +# IMPORTANT: When "cat-ing" these files, make sure that the vitals +# files from the "raw" TC vitals files are first in order and Steve's +# TC vitals files second. This is because Steve's vitals file has +# been error-checked, so if we have a duplicate tc vitals record in +# these 2 files (very likely), program supvit.x below will +# only take the last vitals record listed for a particular storm in +# the vitals file (all previous duplicates are ignored, and Steve's +# error-checked vitals records are kept). + +cat ${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} ${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} \ + >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +#--------------------------------------------------------------# +# Now run a fortran program that will read all the TC vitals +# records for the current dtg and the dtg from 6h ago, and +# sort out any duplicates. If the program finds a storm that +# was included in the vitals file 6h ago but not for the current +# dtg, this program updates the 6h-old first guess position +# and puts these updated records as well as the records from +# the current dtg into a temporary vitals file. It is this +# temporary vitals file that is then used as the input for the +# tracking program. +#--------------------------------------------------------------# + +#oldymdh=`${NDATE:?} -${vit_incr} ${PDY}${cyc}` +oldymdh=`${NDATE:?} -6 ${PDY}${cyc}` +oldyy=`echo ${oldymdh} | cut -c3-4` +oldmm=`echo ${oldymdh} | cut -c5-6` +olddd=`echo ${oldymdh} | cut -c7-8` +oldhh=`echo ${oldymdh} | cut -c9-10` +oldymd=${oldyy}${oldmm}${olddd} + +futureymdh=`${NDATE:?} 6 ${PDY}${cyc}` +futureyy=`echo ${futureymdh} | cut -c3-4` +futuremm=`echo ${futureymdh} | cut -c5-6` +futuredd=`echo ${futureymdh} | cut -c7-8` +futurehh=`echo ${futureymdh} | cut -c9-10` +futureymd=${futureyy}${futuremm}${futuredd} + +echo "&datenowin dnow%yy=${syy}, dnow%mm=${smm}," >${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&dateoldin dold%yy=${oldyy}, dold%mm=${oldmm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dold%dd=${olddd}, dold%hh=${oldhh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&datefuturein dfuture%yy=${futureyy}, dfuture%mm=${futuremm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dfuture%dd=${futuredd}, dfuture%hh=${futurehh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&hourinfo vit_hr_incr=${vit_incr}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period." + echo "!!! File ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} is empty." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi + +fi + +# For tcgen cases, filter to use only vitals from the ocean +# basin of interest.... + +if [ ${trkrtype} = 'tcgen' ] + then + + if [ ${numvitrecs} -gt 0 ] + then + + fullvitfile=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + cp $fullvitfile ${TRKDATA}/vitals.all_basins.${atcfout}.${PDY}${cyc} + basin=` echo $regtype | cut -c1-2` + + if [ ${basin} = 'al' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "L") print $0}' \ + >${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + if [ ${basin} = 'ep' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "E") print $0}' \ + >${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + if [ ${basin} = 'wp' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "W") print $0}' \ + >${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + + cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + + fi + +fi + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Before running the program to read, sort and update the vitals, +# first run the vitals through some awk logic, the purpose of +# which is to convert all the 2-digit years into 4-digit years. +# We need this logic to ensure that all the vitals going +# into supvit.f have uniform, 4-digit years in their records. +# +# 1/8/2000: sed code added by Tim Marchok due to the fact that +# some of the vitals were getting past the syndata/qctropcy +# error-checking with a colon in them; the colon appeared +# in the character immediately to the left of the date, which +# was messing up the "(length($4) == 8)" statement logic. +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +sed -e "s/\:/ /g" ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} > ${TRKDATA}/tempvit +mv ${TRKDATA}/tempvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +awk ' +{ + yycheck = substr($0,20,2) + if ((yycheck == 20 || yycheck == 19) && (length($4) == 8)) { + printf ("%s\n",$0) + } + else { + if (yycheck >= 0 && yycheck <= 50) { + printf ("%s20%s\n",substr($0,1,19),substr($0,20)) + } + else { + printf ("%s19%s\n",substr($0,1,19),substr($0,20)) + } + } +} ' ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 + +mv ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +if [ ${numvitrecs} -gt 0 ] +then + + export pgm=supvit_g2 + . prep_step + + export FORT31=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + export FORT51=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_g2 <${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + suvrcc=$? + + if [ ${suvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit.x, " + echo "!!! which is the program that updates the TC Vitals file." + echo "!!! Return code from supvit.x = ${suvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING supvit_g2 IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + + touch ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#----------------------------------------------------------------- +# In this section, check to see if the user requested the use of +# operational TC vitals records for the initial time only. This +# option might be used for a retrospective medium range forecast +# in which the user wants to initialize with the storms that are +# currently there, but then let the model do its own thing for +# the next 10 or 14 days.... + + +if [ ${USE_OPER_VITALS} = 'INIT_ONLY' ]; then + + if [ ${init_flag} = 'yes' ]; then + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are currently at." + echo " " + set -x + else + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are now *PAST*." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + fi + +elif [ ${USE_OPER_VITALS} = 'NO' ]; then + + set +x + echo " " + echo "NOTE: User has requested that historical vitals not be used...." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#------------------------------------------------------------------# +# Now select all storms to be processed, that is, process every +# storm that's listed in the updated vitals file for the current +# forecast hour. If there are no storms for the current time, +# then exit. +#------------------------------------------------------------------# + +numvitrecs=`cat ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period " + echo "!!! in the UPDATED vitals file." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi +fi + +set +x +echo " " +echo " *--------------------------------*" +echo " | STORM SELECTION |" +echo " *--------------------------------*" +echo " " +set -x + +ict=1 +while [ $ict -le 15 ] +do + stormflag[${ict}]=3 + let ict=ict+1 +done + +dtg_current="${symd} ${cyc}00" +stormmax=` grep "${dtg_current}" ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +if [ ${stormmax} -gt 15 ] +then + stormmax=15 +fi + +sct=1 +while [ ${sct} -le ${stormmax} ] +do + stormflag[${sct}]=1 + let sct=sct+1 +done + + +#---------------------------------------------------------------# +# +# -------- "Genesis" Vitals processing -------- +# +# May 2006: This entire genesis tracking system is being +# upgraded to more comprehensively track and categorize storms. +# One thing that has been missing from the tracking system is +# the ability to keep track of storms from one analysis cycle +# to the next. That is, the current system has been very +# effective at tracking systems within a forecast, but we have +# no methods in place for keeping track of storms across +# difference initial times. For example, if we are running +# the tracker on today's 00z GFS analysis, we will get a +# position for various storms at the analysis time. But then +# if we go ahead and run again at 06z, we have no way of +# telling the tracker that we know about the 00z position of +# this storm. We now address that problem by creating +# "genesis" vitals, that is, when a storm is found at an +# analysis time, we not only produce "atcfunix" output to +# detail the track & intensity of a found storm, but we also +# produce a vitals record that will be used for the next +# run of the tracker script. These "genesis vitals" records +# will be of the format: +# +# YYYYMMDDHH_AAAH_LLLLX_TYP +# +# Where: +# +# YYYYMMDDHH = Date the storm was FIRST identified +# by the tracker. +# AAA = Abs(Latitude) * 10; integer value +# H = 'N' for norther hem, 'S' for southern hem +# LLLL = Abs(Longitude) * 10; integer value +# X = 'E' for eastern hem, 'W' for western hem +# TYP = Tropical cyclone storm id if this is a +# tropical cyclone (e.g., "12L", or "09W", etc). +# If this is one that the tracker instead "Found +# On the Fly (FOF)", we simply put those three +# "FOF" characters in there. +genvitfile=${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} +touch ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + +if [ -f $genvitfile ]; then + d6ago_ymdh=` ${NDATE:?} -6 ${PDY}${cyc}` + d6ago_4ymd=` echo ${d6ago_ymdh} | cut -c1-8` + d6ago_ymd=` echo ${d6ago_ymdh} | cut -c3-8` + d6ago_hh=` echo ${d6ago_ymdh} | cut -c9-10` + d6ago_str="${d6ago_ymd} ${d6ago_hh}00" + + d6ahead_ymdh=` ${NDATE:?} 6 ${PDY}${cyc}` + d6ahead_4ymd=` echo ${d6ahead_ymdh} | cut -c1-8` + d6ahead_ymd=` echo ${d6ahead_ymdh} | cut -c3-8` + d6ahead_hh=` echo ${d6ahead_ymdh} | cut -c9-10` + d6ahead_str="${d6ahead_ymd} ${d6ahead_hh}00" + + syyyym6=` echo ${d6ago_ymdh} | cut -c1-4` + smmm6=` echo ${d6ago_ymdh} | cut -c5-6` + sddm6=` echo ${d6ago_ymdh} | cut -c7-8` + shhm6=` echo ${d6ago_ymdh} | cut -c9-10` + + syyyyp6=` echo ${d6ahead_ymdh} | cut -c1-4` + smmp6=` echo ${d6ahead_ymdh} | cut -c5-6` + sddp6=` echo ${d6ahead_ymdh} | cut -c7-8` + shhp6=` echo ${d6ahead_ymdh} | cut -c9-10` + + set +x + echo " " + echo " d6ago_str= --->${d6ago_str}<---" + echo " current_str= --->${current_str}<---" + echo " d6ahead_str= --->${d6ahead_str}<---" + echo " " + #echo " Listing and contents of ${genvitfile} follow " + #echo " for the times 6h ago, current and 6h ahead:" + #echo " " + set -x + + #ls -la ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + #cat ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + + grep "${d6ago_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${d6ahead_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + num_gen_vits=`cat ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} | wc -l` +else + echo "WARNING: Genesis Vitals from previous run(s) not found!" +fi + +echo "&datenowin dnow%yy=${syyyy}, dnow%mm=${smm}," >${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6agoin d6ago%yy=${syyyym6}, d6ago%mm=${smmm6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ago%dd=${sddm6}, d6ago%hh=${shhm6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6aheadin d6ahead%yy=${syyyyp6}, d6ahead%mm=${smmp6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ahead%dd=${sddp6}, d6ahead%hh=${shhp6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + +num_gen_vits=0 + +if [ ${num_gen_vits} -gt 0 ] +then + export pgm=supvit_gen + . prep_step + + export FORT31=${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + export FORT51=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_gen <${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + sgvrcc=$? + + if [ ${sgvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit_gen, " + echo "!!! which is the program that updates the genesis vitals file." + echo "!!! Return code from supvit_gen = ${sgvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING SUPVIT_GEN IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + + touch ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + +fi + + +#-----------------------------------------------------------------# +# +# ------ CUT APART INPUT GRIB FILES ------- +# +# For the selected model, cut apart the GRIB input files in order +# to pull out only the variables that we need for the tracker. +# Put these selected variables from all forecast hours into 1 big +# GRIB file that we'll use as input for the tracker. +# +#-----------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------" +echo " NOW CUTTING APART INPUT GRIB FILES TO " +echo " CREATE 1 BIG GRIB INPUT FILE " +echo " -----------------------------------------" +echo " " +set -x + +regflag=`grep NHC ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +# -------------------------------------------------- +# Process GDAS data +# -------------------------------------------------- + +if [ ${model} -eq 8 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gdasgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gdasgribfile.${PDY}${cyc} + fi + + rm ${TRKDATA}/master.gdasgribfile.${PDY}${cyc}.f* + rm ${TRKDATA}/gdasgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gdasgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gdas wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gdasdir:?}/${gdasgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GDAS File missing: ${gdasdir:?}/${gdasgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GDAS FILE IN extrkr_g2.sh: ${gdasdir:?}/${gdasgfile}${fhour}" + fi + + gfile=${gdasdir}/${gdasgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gdasgribfile.${PDY}${cyc}.f${fhour} + + gdas_master_file=${TRKDATA}/master.gdasgribfile.${PDY}${cyc}.f${fhour} + gdas_cat_file=${TRKDATA}/gdasgribfile.${PDY}${cyc} + cat ${gdas_master_file} >>${gdas_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gdasgribfile.${PDY}${cyc} ${TRKDATA}/gdasixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + + catfile=${TRKDATA}/gdas.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in 300-500hPa T-mean for model= $cmodel and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gdasgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gdasixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in 300-500hPa T-mean for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + + fi + + gfile=${TRKDATA}/gdasgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/gdasixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gdasgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gdasixfile.${PDY}${cyc} + +fi + +# -------------------------------------------------- +# Process GFS data +# -------------------------------------------------- + +if [ ${model} -eq 1 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gfsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc} + fi + + rm ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f* + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gfsgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gfs wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gfsdir:?}/${gfsgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GFS File missing: ${gfsdir}/${gfsgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GFS FILE IN extrkr_g2.sh: ${gfsdir}/${gfsgfile}${fhour}" + fi + + gfile=${gfsdir}/${gfsgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + + gfs_master_file=${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + gfs_cat_file=${TRKDATA}/gfsgribfile.${PDY}${cyc} + cat ${gfs_master_file} >>${gfs_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gfsgribfile.${PDY}${cyc} ${TRKDATA}/gfsixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + + catfile=${TRKDATA}/gfs.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in 300-500hPa T-mean for model= $cmodel and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in 300-500hPa T-mean for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + + fi + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gfsixfile.${PDY}${cyc} + +fi + +# -------------------------------------------------- +# Process NCEP Ensemble perturbation, if selected +# -------------------------------------------------- + +if [ ${model} -eq 10 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ensdira:?}/${ensgfilea}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: ENSEMBLE ${PERT} File missing: ${ensdira}/${ensgfilea}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING GEFS FILE IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${TRKDATA}/${ensgfilea}${fhour} + cat ${ensdira}/${ensgfilea}${fhour} ${ensdirb}/${ensgfileb}${fhour} > $gfile + + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} + + cat ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} >> ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + done + + ${GRB2INDEX:?} ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/ens${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in 300-500hPa T-mean for pert= $pert and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour for $cmodel $pert " + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in 300-500hPa T-mean for pert= $pert and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + + gfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------------------------------ +# Process Canadian (CMC) hi-res deterministic, if selected +# ------------------------------------------------------ + +if [ ${model} -eq 15 ]; then + + if [ $loopnum -eq 1 ]; then + if [ -s ${TRKDATA}/cmcgribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/cmcgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + if [ ! -s ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} ]; then + set +x + echo " " + echo "FATAL ERROR: CMC File missing ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + fi + + gfile=${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + done +# J.Peng--04-05-2017--changing grid from 180E to 0E------------ +# wgrib2 junk -small_grib 0:359.76 -90:90 junk2 +# cp ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/junk +# rm -f ${TRKDATA}/cmcgribfile.${PDY}${cyc} +# ${WGRIB2} ${TRKDATA}/junk -small_grib 0:359.76 -90:90 ${TRKDATA}/junk2 +# cp ${TRKDATA}/junk2 ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + ${GRB2INDEX:?} ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/cmcixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +fi + +# ------------------------------------------------------ +# Process Canadian Ensemble perturbation, if selected +# ------------------------------------------------------ +if [ ${model} -eq 16 ]; then + if [ $loopnum -eq 1 ]; then + + if [ -s ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + + if [ ! -s ${ccedir:?}/${ccegfile}${fhour}${ccegfile2} ]; then + set +x + echo " " + echo "FATAL ERROR: CANADIAN ENSEMBLE ${PERT} missing: ${ccegfile}${fhour}${ccegfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING CMC ENSEMBLE FILE IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${ccedir}/${ccegfile}${fhour}${ccegfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk +# Process the cyclone phase variables, if requested + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + gparm=11 + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour " + echo "rcc= $rcc EXITING....${cmodel}.${pert}.${PDY}${cyc} " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------ +# Process FNMOC Ensemble, if selected +# ------------------------------ +if [ ${model} -eq 22 ]; then + if [ $loopnum -eq 1 ]; then + + if [ -s ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ]; then + rm ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + fensfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + + let attempts=1 + while [ $attempts -le 30 ]; do + if [ -s $fensfile ]; then + break + else + sleep 60 + let attempts=attempts+1 + fi + done + if [ $attempts -gt 30 ] && [ ! -s $fensfile ]; then + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "$fensfile" >> ${DATA}/missing_fens.txt + exit + else + err_exit "$fensfile still not available after waiting 30 minutes... exiting" + fi + fi + #${WGRIB2:?} -set_byte 1 11 1 -s $fensfile >fens.ix # set_byte resets local table to 1 to remove error messages + + gfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + gparm=11 + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour " + echo "rcc= $rcc EXITING....${cmodel}.${pert}.${PDY}${cyc} " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ixfile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} +fi + +# ------------------------------ +# Process NAVGEM, if selected +# ------------------------------ +if [ ${model} -eq 7 ]; then + + if [ $loopnum -eq 1 ]; then + if [ -s ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + if [ ! -s ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} ]; then + set +x + echo " " + echo "FATAL ERROR: NAVGEM File missing ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" >> ${DATA}/missing_ngps.txt + exit + else + err_exit "MISSING NAVGEM FILE at $0:$LINENO" + fi + fi + + gfile=${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ${TRKDATA}/ngpsixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ngpsixfile.${PDY}${cyc} +fi + +#------------------------------------------------------------------------# +# Now run the tracker # +#------------------------------------------------------------------------# +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW EXECUTING TRACKER......" +echo " -----------------------------------------------" +echo " " +set -x + +ist=1 +while [ $ist -le 15 ] +do + if [ ${stormflag[${ist}]} -ne 1 ] + then + set +x; echo "Storm number $ist NOT selected for processing"; set -x + else + set +x; echo "Storm number $ist IS selected for processing...."; set -x + fi + let ist=ist+1 +done + +namelist=${TRKDATA}/input.${atcfout}.${PDY}${cyc} +ATCFNAME=` echo "${atcfname}" | tr '[a-z]' '[A-Z]'` + +if [ ${cmodel} = 'sref' ]; then + export atcfymdh=` ${NDATE:?} -3 ${scc}${syy}${smm}${sdd}${shh}` +else + export atcfymdh=${scc}${syy}${smm}${sdd}${shh} +fi + +#contour_interval=100.0 +#write_vit=n +#want_oci=.TRUE. + +echo "&datein inp%bcc=${scc},inp%byy=${syy},inp%bmm=${smm}," >${namelist} +echo " inp%bdd=${sdd},inp%bhh=${shh},inp%model=${model}," >>${namelist} +echo " inp%modtyp='${modtyp}'," >>${namelist} +echo " inp%lt_units='${lead_time_units}'," >>${namelist} +echo " inp%file_seq='${file_sequence}'," >>${namelist} +echo " inp%nesttyp='${nest_type}'/" >>${namelist} + +echo "&atcfinfo atcfnum=${atcfnum},atcfname='${ATCFNAME}'," >>${namelist} +echo " atcfymdh=${atcfymdh},atcffreq=${atcffreq}/" >>${namelist} + +echo "&trackerinfo trkrinfo%westbd=${trkrwbd}," >>${namelist} +echo " trkrinfo%eastbd=${trkrebd}," >>${namelist} +echo " trkrinfo%northbd=${trkrnbd}," >>${namelist} +echo " trkrinfo%southbd=${trkrsbd}," >>${namelist} +echo " trkrinfo%type='${trkrtype}'," >>${namelist} +echo " trkrinfo%mslpthresh=${mslpthresh}," >>${namelist} +echo " trkrinfo%use_backup_mslp_grad_check='${use_backup_mslp_grad_check}'," >>${namelist} +echo " trkrinfo%max_mslp_850=${max_mslp_850}," >>${namelist} +echo " trkrinfo%v850thresh=${v850thresh}," >>${namelist} +echo " trkrinfo%use_backup_850_vt_check='${use_backup_850_vt_check}'," >>${namelist} +echo " trkrinfo%gridtype='${modtyp}'," >>${namelist} +echo " trkrinfo%enable_timing=1," >>${namelist} +echo " trkrinfo%contint=${contour_interval}," >>${namelist} +echo " trkrinfo%want_oci=${want_oci}," >>${namelist} +echo " trkrinfo%out_vit='${write_vit}'," >>${namelist} +echo " trkrinfo%use_land_mask='${use_land_mask}'," >>${namelist} +echo " trkrinfo%inp_data_type='${inp_data_type}'," >>${namelist} +echo " trkrinfo%gribver=${gribver}," >>${namelist} +echo " trkrinfo%g2_jpdtn=${g2_jpdtn}," >>${namelist} +echo " trkrinfo%g2_mslp_parm_id=${g2_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_mslp_parm_id=${g1_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_typ=${g1_sfcwind_lev_typ}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_val=${g1_sfcwind_lev_val}/" >>${namelist} + +echo "&phaseinfo phaseflag='${PHASEFLAG}'," >>${namelist} +echo " phasescheme='${PHASE_SCHEME}'," >>${namelist} +echo " wcore_depth=${WCORE_DEPTH}/" >>${namelist} + +echo "&structinfo structflag='${STRUCTFLAG}'," >>${namelist} +echo " ikeflag='${IKEFLAG}'/" >>${namelist} + +echo "&fnameinfo gmodname='${atcfname}'," >>${namelist} +echo " rundescr='${rundescr}'," >>${namelist} +echo " atcfdescr='${atcfdescr}'/" >>${namelist} + +echo "&waitinfo use_waitfor='n'," >>${namelist} +echo " wait_min_age=10," >>${namelist} +echo " wait_min_size=100," >>${namelist} +echo " wait_max_wait=1800," >>${namelist} +echo " wait_sleeptime=5," >>${namelist} +echo " per_fcst_command=''/" >>${namelist} + +echo "&netcdflist netcdfinfo%num_netcdf_vars=${ncdf_num_netcdf_vars}," >>${namelist} +echo " netcdfinfo%netcdf_filename='${netcdffile}'," >>${namelist} +echo " netcdfinfo%rv850name='${ncdf_rv850name}'," >>${namelist} +echo " netcdfinfo%rv700name='${ncdf_rv700name}'," >>${namelist} +echo " netcdfinfo%u850name='${ncdf_u850name}'," >>${namelist} +echo " netcdfinfo%v850name='${ncdf_v850name}'," >>${namelist} +echo " netcdfinfo%u700name='${ncdf_u700name}'," >>${namelist} +echo " netcdfinfo%v700name='${ncdf_v700name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%mslpname='${ncdf_mslpname}'," >>${namelist} +echo " netcdfinfo%usfcname='${ncdf_usfcname}'," >>${namelist} +echo " netcdfinfo%vsfcname='${ncdf_vsfcname}'," >>${namelist} +echo " netcdfinfo%u500name='${ncdf_u500name}'," >>${namelist} +echo " netcdfinfo%v500name='${ncdf_v500name}'," >>${namelist} +echo " netcdfinfo%tmean_300_500_name='${ncdf_tmean_300_500_name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z200name='${ncdf_z200name}'," >>${namelist} +echo " netcdfinfo%lmaskname='${ncdf_lmaskname}'," >>${namelist} +echo " netcdfinfo%z900name='${ncdf_z900name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z800name='${ncdf_z800name}'," >>${namelist} +echo " netcdfinfo%z750name='${ncdf_z750name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%z650name='${ncdf_z650name}'," >>${namelist} +echo " netcdfinfo%z600name='${ncdf_z600name}'," >>${namelist} +echo " netcdfinfo%z550name='${ncdf_z550name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z450name='${ncdf_z450name}'," >>${namelist} +echo " netcdfinfo%z400name='${ncdf_z400name}'," >>${namelist} +echo " netcdfinfo%z350name='${ncdf_z350name}'," >>${namelist} +echo " netcdfinfo%z300name='${ncdf_z300name}'," >>${namelist} +echo " netcdfinfo%time_name='${ncdf_time_name}'," >>${namelist} +echo " netcdfinfo%lon_name='${ncdf_lon_name}'," >>${namelist} +echo " netcdfinfo%lat_name='${ncdf_lat_name}'," >>${namelist} +echo " netcdfinfo%time_units='${ncdf_time_units}'/" >>${namelist} + +echo "&parmpreflist user_wants_to_track_zeta850='${user_wants_to_track_zeta850}'," >>${namelist} +echo " user_wants_to_track_zeta700='${user_wants_to_track_zeta700}'," >>${namelist} +echo " user_wants_to_track_wcirc850='${user_wants_to_track_wcirc850}'," >>${namelist} +echo " user_wants_to_track_wcirc700='${user_wants_to_track_wcirc700}'," >>${namelist} +echo " user_wants_to_track_gph850='${user_wants_to_track_gph850}'," >>${namelist} +echo " user_wants_to_track_gph700='${user_wants_to_track_gph700}'," >>${namelist} +echo " user_wants_to_track_mslp='${user_wants_to_track_mslp}'," >>${namelist} +echo " user_wants_to_track_wcircsfc='${user_wants_to_track_wcircsfc}'," >>${namelist} +echo " user_wants_to_track_zetasfc='${user_wants_to_track_zetasfc}'," >>${namelist} +echo " user_wants_to_track_thick500850='${user_wants_to_track_thick500850}'," >>${namelist} +echo " user_wants_to_track_thick200500='${user_wants_to_track_thick200500}'," >>${namelist} +echo " user_wants_to_track_thick200850='${user_wants_to_track_thick200850}'/" >>${namelist} + +echo "&verbose verb=3,verb_g2=0/" >>${namelist} + +export pgm=gettrk_gfs +. prep_step + +export FORT11=${gribfile} +export FORT12=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} +export FORT14=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes + +#if [ $FHOUT_CYCLONE -eq 3 ]; then export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes_3hr ; fi +if [ $FHMAX_CYCLONE -eq 180 ]; then export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes_180 ; fi +if [ $vit_incr -eq 3 ]; then export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes_3hr ; fi +export FORT31=${ixfile} + +#if [ -s ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} ]; then +# cp ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} tcvit_rsmc_storms.txt +#fi +#if [ -s ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} ]; then +# cp ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} tcvit_genesis_storms.txt +#fi + +if [ ${trkrtype} = 'tracker' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT61=${TRKDATA}/trak.${atcfout}.all.${stormenv}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${stormenv}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${stormenv}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${stormenv}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${stormenv}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${stormenv}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${stormenv}.${PDY}${cyc} + else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${PDY}${cyc} + fi +else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${regtype}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${regtype}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${regtype}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${regtype}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${regtype}.${PDY}${cyc} +fi + +if [ ${atcfname} = 'aear' ] +then + export FORT65=${TRKDATA}/trak.${atcfout}.initvitl.${PDY}${cyc} +fi + +if [ ${write_vit} = 'y' ] +then + export FORT67=${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} +fi + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${stormenv}.${PDY}${cyc} + else + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${PDY}${cyc} + fi +fi + +if [ ${STRUCTFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${stormenv}.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${stormenv}.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${stormenv}.${PDY}${cyc} + else + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${PDY}${cyc} + fi +fi + +if [ ${IKEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${stormenv}.${PDY}${cyc} + else + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${PDY}${cyc} + fi +fi + +if [ ${trkrtype} = 'midlat' -o ${trkrtype} = 'tcgen' ]; then + export FORT77=${TRKDATA}/trkrmask.${atcfout}.${regtype}.${PDY}${cyc} +fi + +msg="$pgm start for $atcfout at ${cyc}z" +postmsg "$jlogfile" "$msg" + +set +x +echo "+++ TIMING: BEFORE gettrk ---> `date`" +set -x + +set +x +echo " " +echo "TIMING: Before call to gettrk at `date`" +echo " " +set -x + +${EXECens_tracker}/gettrk_gfs <${namelist} + +gettrk_rcc=$? +if [ ${gettrk_rcc} -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR: An error occurred while running gettrk.x, " + echo "!!! which is the program that actually gets the track." + echo "!!! Return code from gettrk.x = ${gettrk_rcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING gettrk IN TRACKER SCRIPT- ABNORMAL EXIT" +fi + +set +x +echo " " +echo "TIMING: After call to gettrk at `date`" +echo " " +set -x + +set +x +echo "+++ TIMING: AFTER gettrk ---> `date`" +set -x + +#--------------------------------------------------------------# +# Now copy the output track files to different directories +#--------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW COPYING OUTPUT TRACK FILES TO COM " +echo " -----------------------------------------------" +echo " " +set -x + + +if [ -s ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} ]; then + cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh}|cut -c1-112 > ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} +# cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh}|cut -c1-95 > ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} ${TRKDATA}/long.${atcfout}.atcfunix.${PDY}${shh} + cp ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} +fi + +msg="$pgm end for $atcfout at ${cyc}z completed normally" +postmsg "$jlogfile" "$msg" + +# Copy atcf files to NHC archives. We'll use Steve Lord's original script, +# distatcf.sh, to do this, and that script requires the input atcf file to +# have the name "attk126", so first copy the file to that name, then call +# the distatcf.sh script. After that's done, then copy the full 0-72h +# track into the /com/hur/prod/global track archive file. + +if [ "$SENDCOM" = 'YES' ] +then + + glatuxarch=${glatuxarch:-${gltrkdir}/tracks.atcfunix.${syy}} + cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} >>${glatuxarch} + + if [ ${cmodel} = 'gfdl' ] + then + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} ${COMOUT}/${stormenv}.${PDY}${cyc}.trackeratcfunix + else + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} ${COMOUT}/${atcfout}.t${cyc}z.cyclone.trackatcfunix + cp ${TRKDATA}/long.${atcfout}.atcfunix.${PDY}${shh} ${COMOUT}/${atcfout}p.t${cyc}z.cyclone.trackatcfunix +# cat ${TRKDATA}/long.${atcfout}.atcfunix.${PDY}${shh} | \ +# sed s:GFSO:GFSP:g \ +# > ${COMOUT}/gfsp.t${cyc}z.cyclone.trackatcfunix + + fi + + if [ "$SENDDBN" = 'YES' ] + then + if [ ${cmodel} != 'gfdl' ] + then + $DBNROOT/bin/dbn_alert MODEL ENS_TRACKER $job ${COMOUT}/${atcfout}.t${cyc}z.cyclone.trackatcfunix + fi + fi + +# ------------------------------------------ +# Cat atcfunix files to storm trackers files +# ------------------------------------------ +# +# We need to parse apart the atcfunix file and distribute the forecasts to +# the necessary directories. To do this, first sort the atcfunix records +# by forecast hour (k6), then sort again by ocean basin (k1), storm number (k2) +# and then quadrant radii wind threshold (k12). Once you've got that organized +# file, break the file up by putting all the forecast records for each storm +# into a separate file. Then, for each file, find the corresponding atcfunix +# file in the storm trackers directory and dump the atcfunix records for that storm +# in there. NOTE: Only do this if the model run is NOT for the CMC or +# ECMWF ensemble. The reason is that we do NOT want to write out the individual +# member tracks to the atcfunix file. We only want to write out the ensemble +# mean track to the atcfunix file, and the mean track is calculated and written +# out in a separate script. + + if [ ${cmodel} = 'gfdl' ] + then + auxfile=${COMOUT}/${stormenv}.${PDY}${cyc}.trackeratcfunix + else + auxfile=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + fi + + sort -k6 ${auxfile} | sort -k1 -k2 -k12 >atcfunix.sorted + old_string="XX, XX" + + ict=0 + while read unixrec + do + storm_string=` echo "${unixrec}" | cut -c1-6` + if [ "${storm_string}" = "${old_string}" ] + then + echo "${unixrec}" >>atcfunix_file.${ict} + else + let ict=ict+1 + echo "${unixrec}" >atcfunix_file.${ict} + old_string="${storm_string}" + fi + done >$COMOUTatcf/${at}${NO}${syyyy}/ncep_a${at}${NO}${syyyy}.dat + if [ ${cmodel} = 'gfs' ]; then + cat atcfunix_file.$mct | sed s:AVNO:GFSO:g > gfso_atcfunix_file.$mct + cat gfso_atcfunix_file.$mct >>$COMOUTatcf/${at}${NO}${syyyy}/ncep_a${at}${NO}${syyyy}.dat + + cat atcfunix_file.$mct >>$COMOUTatcf/${at}${NO}${syyyy}/a${at}${NO}${syyyy}.dat + cat gfso_atcfunix_file.$mct >>$COMOUTatcf/${at}${NO}${syyyy}/a${at}${NO}${syyyy}.dat + fi + + set +x + echo " " + echo "+++ Adding records to TPC ATCFUNIX directory: $COMOUTatcf/${at}${NO}${syyyy}/ncep_${at}${NO}${syyyy}" + echo " " + set -x + done + fi +fi diff --git a/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh_07262019 b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh_07262019 new file mode 100755 index 0000000000..955b4cade3 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh_07262019 @@ -0,0 +1,2625 @@ +#!/bin/ksh +export PS4=' + extrkr_gfs.sh line $LINENO: ' + +set +x +############################################################################## +echo " " +echo "------------------------------------------------" +echo " TC Track in model GRIB2 output " +echo " Models:GFS, GEFS, CENS, FENS, NAVGEM " +echo "------------J.Peng Jan.21, 2015 ----------------" +echo "Current time is: `date`" +echo " " +############################################################################## +set -x + +loopnum=$1 +cmodel=$2 +ymdh=$3 +pert=$4 +TRKDATA=$5 + +USE_OPER_VITALS=YES +# USE_OPER_VITALS=NO +# USE_OPER_VITALS=INIT_ONLY + +############################################################################## +# +# FLOW OF CONTROL +# +# 1. Define data directories and file names for the input model +# 2. Process input starting date/cycle information +# 3. Update TC Vitals file and select storms to be processed +# 4. Cut apart input GRIB files to select only the needed parms and hours +# 5. Execute the tracker +# 6. Copy the output track files to various locations +# +############################################################################## +msg="has begun for ${cmodel} ${pert} at ${cyc}z" +postmsg "$jlogfile" "$msg" + +# This script runs the hurricane tracker using operational GRIB model output. +# This script makes sure that the data files exist, it then pulls all of the +# needed data records out of the various GRIB forecast files and puts them +# into one, consolidated GRIB file, and then runs a program that reads the TC +# Vitals records for the input day and updates the TC Vitals (if necessary). +# It then runs gettrk, which actually does the tracking. +# +# Environmental variable inputs needed for this scripts: +# PDY -- The date for data being processed, in YYYYMMDD format +# cyc -- The numbers for the cycle for data being processed (00, 06, 12, 18) +# cmodel -- Model being processed (gfs, mrf, ukmet, ecmwf, nam, ngm, ngps, +# gdas, gfdl, ens (ncep ensemble)) +# envir -- 'prod' or 'test' +# SENDCOM -- 'YES' or 'NO' +# stormenv -- This is only needed by the tracker run for the GFDL model. +# 'stormenv' contains the name/id that is used in the input +# grib file names. +# pert -- This is only needed by the tracker run for the NCEP ensemble. +# 'pert' contains the ensemble member id (e.g., n2, p4, etc.) +# which is used as part of the grib file names. +# +# For testing script interactively in non-production set following vars: +# gltrkdir - Directory for output tracks +# archsyndir - Directory with syndir scripts/exec/fix +# + +qid=$$ +#----------------------------------------------# +# Get input date information # +#----------------------------------------------# + +export jobid=${jobid:-testjob} +export SENDCOM=${SENDCOM:-NO} + +# Define tracker working directory for this ensemble member + +if [ ! -d $TRKDATA ] +then + mkdir -p $TRKDATA +fi +cd $TRKDATA + +if [ ${#PDY} -eq 0 -o ${#cyc} -eq 0 -o ${#cmodel} -eq 0 ] +then + set +x + echo " " + echo "FATAL ERROR: Something wrong with input parameters." + echo "PDY= ${PDY}, cyc= ${cyc}, cmodel= ${cmodel}" + set -x + err_exit "FAILED ${jobid} -- BAD INPUTS AT LINE $LINENO IN TRACKER SCRIPT - ABNORMAL EXIT" +else + set +x + echo " " + echo " #-----------------------------------------------------------------#" + echo " At beginning of tracker script, the following imported variables " + echo " are defined: " + echo " PDY ................................... $PDY" + echo " cyc ................................... $cyc" + echo " cmodel ................................ $cmodel" + echo " jobid ................................. $jobid" + echo " envir ................................. $envir" + echo " SENDCOM ............................... $SENDCOM" + echo " " + set -x +fi + +scc=`echo ${PDY} | cut -c1-2` +syy=`echo ${PDY} | cut -c3-4` +smm=`echo ${PDY} | cut -c5-6` +sdd=`echo ${PDY} | cut -c7-8` +shh=${cyc} +symd=`echo ${PDY} | cut -c3-8` +syyyy=`echo ${PDY} | cut -c1-4` +CENT=`echo ${PDY} | cut -c1-2` + +#------J.Peng----01-21-2015--------- +archsyndir=${archsyndir:-${COMINsyn:?}} +gltrkdir=${gltrkdir:-${COMOUThur:?}} + +#----------------------------------------------------------------# +# +# --- Define data directories and data file names --- +# +# Convert the input model to lowercase letters and check to see +# if it's a valid model, and assign a model ID number to it. +# This model ID number is passed into the Fortran program to +# let the program know what set of forecast hours to use in the +# ifhours array. Also, set the directories for the operational +# input GRIB data files and create templates for the file names. +# +#----------------------------------------------------------------# + +cmodel=`echo ${cmodel} | tr "[A-Z]" "[a-z]"` + +#---- tracking variable list------------------------------------ +user_wants_to_track_zeta850='y' +user_wants_to_track_zeta700='y' +user_wants_to_track_wcirc850='y' +user_wants_to_track_wcirc700='y' +user_wants_to_track_gph850='y' +user_wants_to_track_gph700='y' +user_wants_to_track_mslp='y' +user_wants_to_track_wcircsfc='n' +user_wants_to_track_zetasfc='n' +user_wants_to_track_thick500850='n' +user_wants_to_track_thick200850='n' +user_wants_to_track_thick200500='n' + +case ${cmodel} in + gdas) set +x; echo " " ; + echo " ++ operational FV3-GDAS chosen" ; + echo " "; set -x ; + gdasdir=${gdasdir:-${COMINgdas:?}} ; + gdasgfile=gdas.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-3} ; + fcstlen=${FHMAX_CYCLONE:-9} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=8 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=72 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + +# PHASEFLAG='n' ; + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="gdas" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="gdas" ;; + + gfs) set +x; echo " " ; + echo " ++ operational FV3-GFS chosen" ; + echo " "; set -x ; + gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}} ; + gfsgfile=gfs.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=1 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=15 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + +# PHASEFLAG='n' ; + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="avno" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="avno" ;; + + ens) set +x; echo " " ; + echo " ++ operational ensemble member ${pert} chosen"; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + + ensdira=${ensdira:-${COMINgefs:?}/pgrb2ap5} ; + ensgfilea=ge${pert}.t${cyc}z.pgrb2a.0p50.f ; + ensdirb=${ensdirb:-${COMINgefs:?}/pgrb2bp5} ; + ensgfileb=ge${pert}.t${cyc}z.pgrb2b.0p50.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=10 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="a${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="a${pert_posneg}${pert_num}" ;; + + cmc) set +x; echo " " ; + echo " ++ operational CMC chosen" ; + echo " "; set -x ; + cmcdir=${cmcdir:-${DCOM:?}} ; + cmcgfile=CMC_glb_latlon.24x.24_${PDY}${cyc}_P ; + cmcgfile2=_NCEP.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=15 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=39 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="cmc " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="cmc" ;; + + cens) set +x; echo " " ; + echo " ++ Canadian ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + ccedir=${ccedir:-${DCOM:?}} ; + ccegfile=${PDY}${cyc}_CMC_naefs_hr_latlon0p5x0p5_P ; + ccegfile2=_0${pert_num}.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=16 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="c${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="c${pert_posneg}${pert_num}" ;; + + fens) set +x; echo " " ; + echo " ++ FNMOC ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + fensdir=${fensdir:-${DCOM:?}} ; + fensgfile=ENSEMBLE.MET.fcst_et0${pert_num}. ; + gensgfile=.${PDY}${cyc} ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=22 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="n${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="n${pert_posneg}${pert_num}" ;; + + ngps) set +x; echo " " ; + echo " ++ operational NAVGEM chosen" ; + echo " "; set -x ; + + ngpsdir=${ngpsdir:-${DCOM:?}} ; + ngpsgfile=US058GMET-OPSbd2.NAVGEM ; + ngemgfile=-${PDY}${cyc}-NOAA-halfdeg.gr2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-180} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=7 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=29 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="ngx " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="ngx" ;; + + *) msg="FATAL ERROR: Model $cmodel is not recognized." ; + echo "$msg"; postmsg $jlogfile "$msg" ; + err_exit "FAILED ${jobid} -- UNKNOWN cmodel IN TRACKER SCRIPT - ABNORMAL EXIT";; + +esac + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${cmodel} = "gfs" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300)" + + elif [ ${cmodel} = "gdas" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300)" + + elif [ ${cmodel} = "ens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300)" + + elif [ ${cmodel} = "cens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:300|TMP:500|TMP:250)" + + elif [ ${cmodel} = "cmc" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250)" + + elif [ ${cmodel} = "fens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250)" + + elif [ ${cmodel} = "ngps" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:400|HGT:300|TMP:500|TMP:400|TMP:300)" + + fi + + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc GH:300 GH:400 GH:500 GH:925 T:300 T:400 T:500" +else + if [ ${cmodel} = "gfs" ]; then + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET)" + elif [ ${cmodel} = "gdas" ]; then + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET)" + elif [ ${cmodel} = "ens" ]; then + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET)" + else + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL)" + fi + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc" +fi + +#---------------------------------------------------------------# +# +# -------- TC Vitals processing -------- +# +# Check Steve Lord's operational tcvitals file to see if any +# vitals records were processed for this time by his system. +# If there were, then you'll find a file in /com/gfs/prod/gfs.yymmdd +# with the vitals in it. Also check the raw TC Vitals file in +# /com/arch/prod/syndat , since this may contain storms that Steve's +# system ignored (Steve's system will ignore all storms that are +# either over land or very close to land); We still want to track +# these inland storms, AS LONG AS THEY ARE NHC STORMS (don't +# bother trying to track inland storms that are outside of NHC's +# domain of responsibility -- we don't need that info). +# UPDATE 3/27/09 MARCHOK: The SREF is run at off-synoptic times +# (03,09,15,21Z). There are no tcvitals issued at these offtimes, +# so the updating of the "old" tcvitals is critical for running +# the tracker on SREF. For updating the old tcvitals for SREF, +# we need to look 3h back, not 6h as for the other models that +# run at synoptic times. +#--------------------------------------------------------------# + +#old_ymdh=`${NDATE:?} -${vit_incr} ${PDY}${cyc}` +old_ymdh=`${NDATE:?} -6 ${PDY}${cyc}` +old_4ymd=`echo ${old_ymdh} | cut -c1-8` +old_ymd=`echo ${old_ymdh} | cut -c3-8` +old_hh=`echo ${old_ymdh} | cut -c9-10` +old_str="${old_ymd} ${old_hh}00" + +#future_ymdh=`${NDATE:?} ${vit_incr} ${PDY}${cyc}` +future_ymdh=`${NDATE:?} 6 ${PDY}${cyc}` +future_4ymd=`echo ${future_ymdh} | cut -c1-8` +future_ymd=`echo ${future_ymdh} | cut -c3-8` +future_hh=`echo ${future_ymdh} | cut -c9-10` +future_str="${future_ymd} ${future_hh}00" + +if [ ${modtyp} = 'global' ] +then +# synvitdir=${COMROOT}/gfs/prod/gfs.${PDY} + synvitdir=${COMINgfs:?}/${cyc} + synvitfile=gfs.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/gfs/prod/gfs.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd}/${old_hh} + synvitold_file=gfs.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/gfs/prod/gfs.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd}/${future_hh} + synvitfuture_file=gfs.t${future_hh}z.syndata.tcvitals.tm00 +else +# synvitdir=${COMROOT}/nam/prod/nam.${PDY} + synvitdir=${COMINnam:?} + synvitfile=nam.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/nam/prod/nam.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=nam.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/nam/prod/nam.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=nam.t${future_hh}z.syndata.tcvitals.tm00 +fi + +set +x +echo " " +echo " -----------------------------" +echo " " +echo " Now sorting and updating the TC Vitals file. Please wait...." +echo " " +set -x + +current_str="${symd} ${cyc}00" + +if [ -s ${synvitdir}/${synvitfile} -o\ + -s ${synvitold_dir}/${synvitold_file} -o\ + -s ${synvitfuture_dir}/${synvitfuture_file} ] +then + grep "${old_str}" ${synvitold_dir}/${synvitold_file} \ + >${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${synvitdir}/${synvitfile} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${future_str}" ${synvitfuture_dir}/${synvitfuture_file} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} +else + set +x + echo " " + echo " There is no (synthetic) TC vitals file for ${cyc}z in ${synvitdir}," + echo " nor is there a TC vitals file for ${old_hh}z in ${synvitold_dir}." + echo " nor is there a TC vitals file for ${future_hh}z in ${synvitfuture_dir}," + echo " Checking the raw TC Vitals file ....." + echo " " + set -x +fi + +# Take the vitals from Steve Lord's /com/gfs/prod tcvitals file, +# and cat them with the NHC-only vitals from the raw, original +# /com/arch/prod/synda_tcvitals file. Do this because the nwprod +# tcvitals file is the original tcvitals file, and Steve runs a +# program that ignores the vitals for a storm that's over land or +# even just too close to land, and for tracking purposes for the +# US regional models, we need these locations. Only include these +# "inland" storm vitals for NHC (we're not going to track inland +# storms that are outside of NHC's domain of responsibility -- we +# don't need that info). +# UPDATE 5/12/98 MARCHOK: awk logic is added to screen NHC +# vitals such as "89E TEST", since TPC +# does not want tracks for such storms. + +grep "${old_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${current_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${future_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + + +# IMPORTANT: When "cat-ing" these files, make sure that the vitals +# files from the "raw" TC vitals files are first in order and Steve's +# TC vitals files second. This is because Steve's vitals file has +# been error-checked, so if we have a duplicate tc vitals record in +# these 2 files (very likely), program supvit.x below will +# only take the last vitals record listed for a particular storm in +# the vitals file (all previous duplicates are ignored, and Steve's +# error-checked vitals records are kept). + +cat ${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} ${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} \ + >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +#--------------------------------------------------------------# +# Now run a fortran program that will read all the TC vitals +# records for the current dtg and the dtg from 6h ago, and +# sort out any duplicates. If the program finds a storm that +# was included in the vitals file 6h ago but not for the current +# dtg, this program updates the 6h-old first guess position +# and puts these updated records as well as the records from +# the current dtg into a temporary vitals file. It is this +# temporary vitals file that is then used as the input for the +# tracking program. +#--------------------------------------------------------------# + +#oldymdh=`${NDATE:?} -${vit_incr} ${PDY}${cyc}` +oldymdh=`${NDATE:?} -6 ${PDY}${cyc}` +oldyy=`echo ${oldymdh} | cut -c3-4` +oldmm=`echo ${oldymdh} | cut -c5-6` +olddd=`echo ${oldymdh} | cut -c7-8` +oldhh=`echo ${oldymdh} | cut -c9-10` +oldymd=${oldyy}${oldmm}${olddd} + +futureymdh=`${NDATE:?} 6 ${PDY}${cyc}` +futureyy=`echo ${futureymdh} | cut -c3-4` +futuremm=`echo ${futureymdh} | cut -c5-6` +futuredd=`echo ${futureymdh} | cut -c7-8` +futurehh=`echo ${futureymdh} | cut -c9-10` +futureymd=${futureyy}${futuremm}${futuredd} + +echo "&datenowin dnow%yy=${syy}, dnow%mm=${smm}," >${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&dateoldin dold%yy=${oldyy}, dold%mm=${oldmm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dold%dd=${olddd}, dold%hh=${oldhh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&datefuturein dfuture%yy=${futureyy}, dfuture%mm=${futuremm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dfuture%dd=${futuredd}, dfuture%hh=${futurehh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&hourinfo vit_hr_incr=${vit_incr}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period." + echo "!!! File ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} is empty." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi + +fi + +# For tcgen cases, filter to use only vitals from the ocean +# basin of interest.... + +if [ ${trkrtype} = 'tcgen' ] + then + + if [ ${numvitrecs} -gt 0 ] + then + + fullvitfile=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + cp $fullvitfile ${TRKDATA}/vitals.all_basins.${atcfout}.${PDY}${cyc} + basin=` echo $regtype | cut -c1-2` + + if [ ${basin} = 'al' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "L") print $0}' \ + >${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + if [ ${basin} = 'ep' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "E") print $0}' \ + >${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + if [ ${basin} = 'wp' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "W") print $0}' \ + >${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + + cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + + fi + +fi + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Before running the program to read, sort and update the vitals, +# first run the vitals through some awk logic, the purpose of +# which is to convert all the 2-digit years into 4-digit years. +# We need this logic to ensure that all the vitals going +# into supvit.f have uniform, 4-digit years in their records. +# +# 1/8/2000: sed code added by Tim Marchok due to the fact that +# some of the vitals were getting past the syndata/qctropcy +# error-checking with a colon in them; the colon appeared +# in the character immediately to the left of the date, which +# was messing up the "(length($4) == 8)" statement logic. +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +sed -e "s/\:/ /g" ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} > ${TRKDATA}/tempvit +mv ${TRKDATA}/tempvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +awk ' +{ + yycheck = substr($0,20,2) + if ((yycheck == 20 || yycheck == 19) && (length($4) == 8)) { + printf ("%s\n",$0) + } + else { + if (yycheck >= 0 && yycheck <= 50) { + printf ("%s20%s\n",substr($0,1,19),substr($0,20)) + } + else { + printf ("%s19%s\n",substr($0,1,19),substr($0,20)) + } + } +} ' ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 + +mv ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +if [ ${numvitrecs} -gt 0 ] +then + + export pgm=supvit_g2 + . prep_step + + export FORT31=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + export FORT51=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_g2 <${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + suvrcc=$? + + if [ ${suvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit.x, " + echo "!!! which is the program that updates the TC Vitals file." + echo "!!! Return code from supvit.x = ${suvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING supvit_g2 IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + + touch ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#----------------------------------------------------------------- +# In this section, check to see if the user requested the use of +# operational TC vitals records for the initial time only. This +# option might be used for a retrospective medium range forecast +# in which the user wants to initialize with the storms that are +# currently there, but then let the model do its own thing for +# the next 10 or 14 days.... + + +if [ ${USE_OPER_VITALS} = 'INIT_ONLY' ]; then + + if [ ${init_flag} = 'yes' ]; then + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are currently at." + echo " " + set -x + else + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are now *PAST*." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + fi + +elif [ ${USE_OPER_VITALS} = 'NO' ]; then + + set +x + echo " " + echo "NOTE: User has requested that historical vitals not be used...." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#------------------------------------------------------------------# +# Now select all storms to be processed, that is, process every +# storm that's listed in the updated vitals file for the current +# forecast hour. If there are no storms for the current time, +# then exit. +#------------------------------------------------------------------# + +numvitrecs=`cat ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period " + echo "!!! in the UPDATED vitals file." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi +fi + +set +x +echo " " +echo " *--------------------------------*" +echo " | STORM SELECTION |" +echo " *--------------------------------*" +echo " " +set -x + +ict=1 +while [ $ict -le 15 ] +do + stormflag[${ict}]=3 + let ict=ict+1 +done + +dtg_current="${symd} ${cyc}00" +stormmax=` grep "${dtg_current}" ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +if [ ${stormmax} -gt 15 ] +then + stormmax=15 +fi + +sct=1 +while [ ${sct} -le ${stormmax} ] +do + stormflag[${sct}]=1 + let sct=sct+1 +done + + +#---------------------------------------------------------------# +# +# -------- "Genesis" Vitals processing -------- +# +# May 2006: This entire genesis tracking system is being +# upgraded to more comprehensively track and categorize storms. +# One thing that has been missing from the tracking system is +# the ability to keep track of storms from one analysis cycle +# to the next. That is, the current system has been very +# effective at tracking systems within a forecast, but we have +# no methods in place for keeping track of storms across +# difference initial times. For example, if we are running +# the tracker on today's 00z GFS analysis, we will get a +# position for various storms at the analysis time. But then +# if we go ahead and run again at 06z, we have no way of +# telling the tracker that we know about the 00z position of +# this storm. We now address that problem by creating +# "genesis" vitals, that is, when a storm is found at an +# analysis time, we not only produce "atcfunix" output to +# detail the track & intensity of a found storm, but we also +# produce a vitals record that will be used for the next +# run of the tracker script. These "genesis vitals" records +# will be of the format: +# +# YYYYMMDDHH_AAAH_LLLLX_TYP +# +# Where: +# +# YYYYMMDDHH = Date the storm was FIRST identified +# by the tracker. +# AAA = Abs(Latitude) * 10; integer value +# H = 'N' for norther hem, 'S' for southern hem +# LLLL = Abs(Longitude) * 10; integer value +# X = 'E' for eastern hem, 'W' for western hem +# TYP = Tropical cyclone storm id if this is a +# tropical cyclone (e.g., "12L", or "09W", etc). +# If this is one that the tracker instead "Found +# On the Fly (FOF)", we simply put those three +# "FOF" characters in there. +genvitfile=${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} +touch ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + +if [ -f $genvitfile ]; then + d6ago_ymdh=` ${NDATE:?} -6 ${PDY}${cyc}` + d6ago_4ymd=` echo ${d6ago_ymdh} | cut -c1-8` + d6ago_ymd=` echo ${d6ago_ymdh} | cut -c3-8` + d6ago_hh=` echo ${d6ago_ymdh} | cut -c9-10` + d6ago_str="${d6ago_ymd} ${d6ago_hh}00" + + d6ahead_ymdh=` ${NDATE:?} 6 ${PDY}${cyc}` + d6ahead_4ymd=` echo ${d6ahead_ymdh} | cut -c1-8` + d6ahead_ymd=` echo ${d6ahead_ymdh} | cut -c3-8` + d6ahead_hh=` echo ${d6ahead_ymdh} | cut -c9-10` + d6ahead_str="${d6ahead_ymd} ${d6ahead_hh}00" + + syyyym6=` echo ${d6ago_ymdh} | cut -c1-4` + smmm6=` echo ${d6ago_ymdh} | cut -c5-6` + sddm6=` echo ${d6ago_ymdh} | cut -c7-8` + shhm6=` echo ${d6ago_ymdh} | cut -c9-10` + + syyyyp6=` echo ${d6ahead_ymdh} | cut -c1-4` + smmp6=` echo ${d6ahead_ymdh} | cut -c5-6` + sddp6=` echo ${d6ahead_ymdh} | cut -c7-8` + shhp6=` echo ${d6ahead_ymdh} | cut -c9-10` + + set +x + echo " " + echo " d6ago_str= --->${d6ago_str}<---" + echo " current_str= --->${current_str}<---" + echo " d6ahead_str= --->${d6ahead_str}<---" + echo " " + #echo " Listing and contents of ${genvitfile} follow " + #echo " for the times 6h ago, current and 6h ahead:" + #echo " " + set -x + + #ls -la ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + #cat ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + + grep "${d6ago_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${d6ahead_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + num_gen_vits=`cat ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} | wc -l` +else + echo "WARNING: Genesis Vitals from previous run(s) not found!" +fi + +echo "&datenowin dnow%yy=${syyyy}, dnow%mm=${smm}," >${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6agoin d6ago%yy=${syyyym6}, d6ago%mm=${smmm6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ago%dd=${sddm6}, d6ago%hh=${shhm6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6aheadin d6ahead%yy=${syyyyp6}, d6ahead%mm=${smmp6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ahead%dd=${sddp6}, d6ahead%hh=${shhp6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + +num_gen_vits=0 + +if [ ${num_gen_vits} -gt 0 ] +then + export pgm=supvit_gen + . prep_step + + export FORT31=${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + export FORT51=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_gen <${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + sgvrcc=$? + + if [ ${sgvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit_gen, " + echo "!!! which is the program that updates the genesis vitals file." + echo "!!! Return code from supvit_gen = ${sgvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING SUPVIT_GEN IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + + touch ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + +fi + + +#-----------------------------------------------------------------# +# +# ------ CUT APART INPUT GRIB FILES ------- +# +# For the selected model, cut apart the GRIB input files in order +# to pull out only the variables that we need for the tracker. +# Put these selected variables from all forecast hours into 1 big +# GRIB file that we'll use as input for the tracker. +# +#-----------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------" +echo " NOW CUTTING APART INPUT GRIB FILES TO " +echo " CREATE 1 BIG GRIB INPUT FILE " +echo " -----------------------------------------" +echo " " +set -x + +regflag=`grep NHC ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +# -------------------------------------------------- +# Process GDAS data +# -------------------------------------------------- + +if [ ${model} -eq 8 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gdasgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gdasgribfile.${PDY}${cyc} + fi + + rm ${TRKDATA}/master.gdasgribfile.${PDY}${cyc}.f* + rm ${TRKDATA}/gdasgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gdasgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gdas wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gdasdir:?}/${gdasgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GDAS File missing: ${gdasdir:?}/${gdasgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GDAS FILE IN extrkr_g2.sh: ${gdasdir:?}/${gdasgfile}${fhour}" + fi + + gfile=${gdasdir}/${gdasgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gdasgribfile.${PDY}${cyc}.f${fhour} + + gdas_master_file=${TRKDATA}/master.gdasgribfile.${PDY}${cyc}.f${fhour} + gdas_cat_file=${TRKDATA}/gdasgribfile.${PDY}${cyc} + cat ${gdas_master_file} >>${gdas_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gdasgribfile.${PDY}${cyc} ${TRKDATA}/gdasixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + + catfile=${TRKDATA}/gdas.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in 300-500hPa T-mean for model= $cmodel and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gdasgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gdasixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in 300-500hPa T-mean for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + + fi + + gfile=${TRKDATA}/gdasgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/gdasixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gdasgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gdasixfile.${PDY}${cyc} + +fi + +# -------------------------------------------------- +# Process GFS data +# -------------------------------------------------- + +if [ ${model} -eq 1 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gfsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc} + fi + + rm ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f* + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gfsgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gfs wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gfsdir:?}/${gfsgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GFS File missing: ${gfsdir}/${gfsgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GFS FILE IN extrkr_g2.sh: ${gfsdir}/${gfsgfile}${fhour}" + fi + + gfile=${gfsdir}/${gfsgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + + gfs_master_file=${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + gfs_cat_file=${TRKDATA}/gfsgribfile.${PDY}${cyc} + cat ${gfs_master_file} >>${gfs_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gfsgribfile.${PDY}${cyc} ${TRKDATA}/gfsixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + + catfile=${TRKDATA}/gfs.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in 300-500hPa T-mean for model= $cmodel and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in 300-500hPa T-mean for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + + fi + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gfsixfile.${PDY}${cyc} + +fi + +# -------------------------------------------------- +# Process NCEP Ensemble perturbation, if selected +# -------------------------------------------------- + +if [ ${model} -eq 10 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ensdira:?}/${ensgfilea}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: ENSEMBLE ${PERT} File missing: ${ensdira}/${ensgfilea}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING GEFS FILE IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${TRKDATA}/${ensgfilea}${fhour} + cat ${ensdira}/${ensgfilea}${fhour} ${ensdirb}/${ensgfileb}${fhour} > $gfile + + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} + + cat ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} >> ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + done + + ${GRB2INDEX:?} ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/ens${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in 300-500hPa T-mean for pert= $pert and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour for $cmodel $pert " + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in 300-500hPa T-mean for pert= $pert and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + + gfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------------------------------ +# Process Canadian (CMC) hi-res deterministic, if selected +# ------------------------------------------------------ + +if [ ${model} -eq 15 ]; then + + if [ $loopnum -eq 1 ]; then + if [ -s ${TRKDATA}/cmcgribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/cmcgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + if [ ! -s ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} ]; then + set +x + echo " " + echo "FATAL ERROR: CMC File missing ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + fi + + gfile=${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + done +# J.Peng--04-05-2017--changing grid from 180E to 0E------------ +# wgrib2 junk -small_grib 0:359.76 -90:90 junk2 +# cp ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/junk +# rm -f ${TRKDATA}/cmcgribfile.${PDY}${cyc} +# ${WGRIB2} ${TRKDATA}/junk -small_grib 0:359.76 -90:90 ${TRKDATA}/junk2 +# cp ${TRKDATA}/junk2 ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + ${GRB2INDEX:?} ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/cmcixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +fi + +# ------------------------------------------------------ +# Process Canadian Ensemble perturbation, if selected +# ------------------------------------------------------ +if [ ${model} -eq 16 ]; then + if [ $loopnum -eq 1 ]; then + + if [ -s ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + + if [ ! -s ${ccedir:?}/${ccegfile}${fhour}${ccegfile2} ]; then + set +x + echo " " + echo "FATAL ERROR: CANADIAN ENSEMBLE ${PERT} missing: ${ccegfile}${fhour}${ccegfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING CMC ENSEMBLE FILE IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${ccedir}/${ccegfile}${fhour}${ccegfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk +# Process the cyclone phase variables, if requested + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + gparm=11 + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour " + echo "rcc= $rcc EXITING....${cmodel}.${pert}.${PDY}${cyc} " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------ +# Process FNMOC Ensemble, if selected +# ------------------------------ +if [ ${model} -eq 22 ]; then + if [ $loopnum -eq 1 ]; then + + if [ -s ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ]; then + rm ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + fensfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + + let attempts=1 + while [ $attempts -le 30 ]; do + if [ -s $fensfile ]; then + break + else + sleep 60 + let attempts=attempts+1 + fi + done + if [ $attempts -gt 30 ] && [ ! -s $fensfile ]; then + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "$fensfile" >> ${DATA}/missing_fens.txt + exit + else + err_exit "$fensfile still not available after waiting 30 minutes... exiting" + fi + fi + #${WGRIB2:?} -set_byte 1 11 1 -s $fensfile >fens.ix # set_byte resets local table to 1 to remove error messages + + gfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + gparm=11 + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour " + echo "rcc= $rcc EXITING....${cmodel}.${pert}.${PDY}${cyc} " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ixfile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} +fi + +# ------------------------------ +# Process NAVGEM, if selected +# ------------------------------ +if [ ${model} -eq 7 ]; then + + if [ $loopnum -eq 1 ]; then + if [ -s ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + if [ ! -s ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} ]; then + set +x + echo " " + echo "FATAL ERROR: NAVGEM File missing ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" >> ${DATA}/missing_ngps.txt + exit + else + err_exit "MISSING NAVGEM FILE at $0:$LINENO" + fi + fi + + gfile=${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ${TRKDATA}/ngpsixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_g2.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_g2.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ngpsixfile.${PDY}${cyc} +fi + +#------------------------------------------------------------------------# +# Now run the tracker # +#------------------------------------------------------------------------# +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW EXECUTING TRACKER......" +echo " -----------------------------------------------" +echo " " +set -x + +ist=1 +while [ $ist -le 15 ] +do + if [ ${stormflag[${ist}]} -ne 1 ] + then + set +x; echo "Storm number $ist NOT selected for processing"; set -x + else + set +x; echo "Storm number $ist IS selected for processing...."; set -x + fi + let ist=ist+1 +done + +namelist=${TRKDATA}/input.${atcfout}.${PDY}${cyc} +ATCFNAME=` echo "${atcfname}" | tr '[a-z]' '[A-Z]'` + +if [ ${cmodel} = 'sref' ]; then + export atcfymdh=` ${NDATE:?} -3 ${scc}${syy}${smm}${sdd}${shh}` +else + export atcfymdh=${scc}${syy}${smm}${sdd}${shh} +fi + +#contour_interval=100.0 +#write_vit=n +#want_oci=.TRUE. + +echo "&datein inp%bcc=${scc},inp%byy=${syy},inp%bmm=${smm}," >${namelist} +echo " inp%bdd=${sdd},inp%bhh=${shh},inp%model=${model}," >>${namelist} +echo " inp%modtyp='${modtyp}'," >>${namelist} +echo " inp%lt_units='${lead_time_units}'," >>${namelist} +echo " inp%file_seq='${file_sequence}'," >>${namelist} +echo " inp%nesttyp='${nest_type}'/" >>${namelist} + +echo "&atcfinfo atcfnum=${atcfnum},atcfname='${ATCFNAME}'," >>${namelist} +echo " atcfymdh=${atcfymdh},atcffreq=${atcffreq}/" >>${namelist} + +echo "&trackerinfo trkrinfo%westbd=${trkrwbd}," >>${namelist} +echo " trkrinfo%eastbd=${trkrebd}," >>${namelist} +echo " trkrinfo%northbd=${trkrnbd}," >>${namelist} +echo " trkrinfo%southbd=${trkrsbd}," >>${namelist} +echo " trkrinfo%type='${trkrtype}'," >>${namelist} +echo " trkrinfo%mslpthresh=${mslpthresh}," >>${namelist} +echo " trkrinfo%use_backup_mslp_grad_check='${use_backup_mslp_grad_check}'," >>${namelist} +echo " trkrinfo%max_mslp_850=${max_mslp_850}," >>${namelist} +echo " trkrinfo%v850thresh=${v850thresh}," >>${namelist} +echo " trkrinfo%use_backup_850_vt_check='${use_backup_850_vt_check}'," >>${namelist} +echo " trkrinfo%gridtype='${modtyp}'," >>${namelist} +echo " trkrinfo%enable_timing=1," >>${namelist} +echo " trkrinfo%contint=${contour_interval}," >>${namelist} +echo " trkrinfo%want_oci=${want_oci}," >>${namelist} +echo " trkrinfo%out_vit='${write_vit}'," >>${namelist} +echo " trkrinfo%use_land_mask='${use_land_mask}'," >>${namelist} +echo " trkrinfo%inp_data_type='${inp_data_type}'," >>${namelist} +echo " trkrinfo%gribver=${gribver}," >>${namelist} +echo " trkrinfo%g2_jpdtn=${g2_jpdtn}," >>${namelist} +echo " trkrinfo%g2_mslp_parm_id=${g2_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_mslp_parm_id=${g1_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_typ=${g1_sfcwind_lev_typ}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_val=${g1_sfcwind_lev_val}/" >>${namelist} + +echo "&phaseinfo phaseflag='${PHASEFLAG}'," >>${namelist} +echo " phasescheme='${PHASE_SCHEME}'," >>${namelist} +echo " wcore_depth=${WCORE_DEPTH}/" >>${namelist} + +echo "&structinfo structflag='${STRUCTFLAG}'," >>${namelist} +echo " ikeflag='${IKEFLAG}'/" >>${namelist} + +echo "&fnameinfo gmodname='${atcfname}'," >>${namelist} +echo " rundescr='${rundescr}'," >>${namelist} +echo " atcfdescr='${atcfdescr}'/" >>${namelist} + +echo "&waitinfo use_waitfor='n'," >>${namelist} +echo " wait_min_age=10," >>${namelist} +echo " wait_min_size=100," >>${namelist} +echo " wait_max_wait=1800," >>${namelist} +echo " wait_sleeptime=5," >>${namelist} +echo " per_fcst_command=''/" >>${namelist} + +echo "&netcdflist netcdfinfo%num_netcdf_vars=${ncdf_num_netcdf_vars}," >>${namelist} +echo " netcdfinfo%netcdf_filename='${netcdffile}'," >>${namelist} +echo " netcdfinfo%rv850name='${ncdf_rv850name}'," >>${namelist} +echo " netcdfinfo%rv700name='${ncdf_rv700name}'," >>${namelist} +echo " netcdfinfo%u850name='${ncdf_u850name}'," >>${namelist} +echo " netcdfinfo%v850name='${ncdf_v850name}'," >>${namelist} +echo " netcdfinfo%u700name='${ncdf_u700name}'," >>${namelist} +echo " netcdfinfo%v700name='${ncdf_v700name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%mslpname='${ncdf_mslpname}'," >>${namelist} +echo " netcdfinfo%usfcname='${ncdf_usfcname}'," >>${namelist} +echo " netcdfinfo%vsfcname='${ncdf_vsfcname}'," >>${namelist} +echo " netcdfinfo%u500name='${ncdf_u500name}'," >>${namelist} +echo " netcdfinfo%v500name='${ncdf_v500name}'," >>${namelist} +echo " netcdfinfo%tmean_300_500_name='${ncdf_tmean_300_500_name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z200name='${ncdf_z200name}'," >>${namelist} +echo " netcdfinfo%lmaskname='${ncdf_lmaskname}'," >>${namelist} +echo " netcdfinfo%z900name='${ncdf_z900name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z800name='${ncdf_z800name}'," >>${namelist} +echo " netcdfinfo%z750name='${ncdf_z750name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%z650name='${ncdf_z650name}'," >>${namelist} +echo " netcdfinfo%z600name='${ncdf_z600name}'," >>${namelist} +echo " netcdfinfo%z550name='${ncdf_z550name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z450name='${ncdf_z450name}'," >>${namelist} +echo " netcdfinfo%z400name='${ncdf_z400name}'," >>${namelist} +echo " netcdfinfo%z350name='${ncdf_z350name}'," >>${namelist} +echo " netcdfinfo%z300name='${ncdf_z300name}'," >>${namelist} +echo " netcdfinfo%time_name='${ncdf_time_name}'," >>${namelist} +echo " netcdfinfo%lon_name='${ncdf_lon_name}'," >>${namelist} +echo " netcdfinfo%lat_name='${ncdf_lat_name}'," >>${namelist} +echo " netcdfinfo%time_units='${ncdf_time_units}'/" >>${namelist} + +echo "&parmpreflist user_wants_to_track_zeta850='${user_wants_to_track_zeta850}'," >>${namelist} +echo " user_wants_to_track_zeta700='${user_wants_to_track_zeta700}'," >>${namelist} +echo " user_wants_to_track_wcirc850='${user_wants_to_track_wcirc850}'," >>${namelist} +echo " user_wants_to_track_wcirc700='${user_wants_to_track_wcirc700}'," >>${namelist} +echo " user_wants_to_track_gph850='${user_wants_to_track_gph850}'," >>${namelist} +echo " user_wants_to_track_gph700='${user_wants_to_track_gph700}'," >>${namelist} +echo " user_wants_to_track_mslp='${user_wants_to_track_mslp}'," >>${namelist} +echo " user_wants_to_track_wcircsfc='${user_wants_to_track_wcircsfc}'," >>${namelist} +echo " user_wants_to_track_zetasfc='${user_wants_to_track_zetasfc}'," >>${namelist} +echo " user_wants_to_track_thick500850='${user_wants_to_track_thick500850}'," >>${namelist} +echo " user_wants_to_track_thick200500='${user_wants_to_track_thick200500}'," >>${namelist} +echo " user_wants_to_track_thick200850='${user_wants_to_track_thick200850}'/" >>${namelist} + +echo "&verbose verb=3,verb_g2=0/" >>${namelist} + +export pgm=gettrk_gfs +. prep_step + +export FORT11=${gribfile} +export FORT12=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} +export FORT14=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes + +#if [ $FHOUT_CYCLONE -eq 3 ]; then export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes_3hr ; fi +if [ $FHMAX_CYCLONE -eq 180 ]; then export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes_180 ; fi +if [ $vit_incr -eq 3 ]; then export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes_3hr ; fi +export FORT31=${ixfile} + +#if [ -s ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} ]; then +# cp ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} tcvit_rsmc_storms.txt +#fi +#if [ -s ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} ]; then +# cp ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} tcvit_genesis_storms.txt +#fi + +if [ ${trkrtype} = 'tracker' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT61=${TRKDATA}/trak.${atcfout}.all.${stormenv}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${stormenv}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${stormenv}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${stormenv}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${stormenv}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${stormenv}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${stormenv}.${PDY}${cyc} + else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${PDY}${cyc} + fi +else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${regtype}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${regtype}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${regtype}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${regtype}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${regtype}.${PDY}${cyc} +fi + +if [ ${atcfname} = 'aear' ] +then + export FORT65=${TRKDATA}/trak.${atcfout}.initvitl.${PDY}${cyc} +fi + +if [ ${write_vit} = 'y' ] +then + export FORT67=${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} +fi + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${stormenv}.${PDY}${cyc} + else + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${PDY}${cyc} + fi +fi + +if [ ${STRUCTFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${stormenv}.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${stormenv}.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${stormenv}.${PDY}${cyc} + else + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${PDY}${cyc} + fi +fi + +if [ ${IKEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${stormenv}.${PDY}${cyc} + else + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${PDY}${cyc} + fi +fi + +if [ ${trkrtype} = 'midlat' -o ${trkrtype} = 'tcgen' ]; then + export FORT77=${TRKDATA}/trkrmask.${atcfout}.${regtype}.${PDY}${cyc} +fi + +msg="$pgm start for $atcfout at ${cyc}z" +postmsg "$jlogfile" "$msg" + +set +x +echo "+++ TIMING: BEFORE gettrk ---> `date`" +set -x + +set +x +echo " " +echo "TIMING: Before call to gettrk at `date`" +echo " " +set -x + +${EXECens_tracker}/gettrk_gfs <${namelist} + +gettrk_rcc=$? +if [ ${gettrk_rcc} -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR: An error occurred while running gettrk.x, " + echo "!!! which is the program that actually gets the track." + echo "!!! Return code from gettrk.x = ${gettrk_rcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING gettrk IN TRACKER SCRIPT- ABNORMAL EXIT" +fi + +set +x +echo " " +echo "TIMING: After call to gettrk at `date`" +echo " " +set -x + +set +x +echo "+++ TIMING: AFTER gettrk ---> `date`" +set -x + +#--------------------------------------------------------------# +# Now copy the output track files to different directories +#--------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW COPYING OUTPUT TRACK FILES TO COM " +echo " -----------------------------------------------" +echo " " +set -x + + +if [ -s ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} ]; then + cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh}|cut -c1-112 > ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} +# cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh}|cut -c1-95 > ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} ${TRKDATA}/long.${atcfout}.atcfunix.${PDY}${shh} + cp ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} +fi + +msg="$pgm end for $atcfout at ${cyc}z completed normally" +postmsg "$jlogfile" "$msg" + +# Copy atcf files to NHC archives. We'll use Steve Lord's original script, +# distatcf.sh, to do this, and that script requires the input atcf file to +# have the name "attk126", so first copy the file to that name, then call +# the distatcf.sh script. After that's done, then copy the full 0-72h +# track into the /com/hur/prod/global track archive file. + +if [ "$SENDCOM" = 'YES' ] +then + + glatuxarch=${glatuxarch:-${gltrkdir}/tracks.atcfunix.${syy}} + cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} >>${glatuxarch} + + if [ ${cmodel} = 'gfdl' ] + then + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} ${COMOUT}/${stormenv}.${PDY}${cyc}.trackeratcfunix + else + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} ${COMOUT}/${atcfout}.t${cyc}z.cyclone.trackatcfunix + cp ${TRKDATA}/long.${atcfout}.atcfunix.${PDY}${shh} ${COMOUT}/${atcfout}p.t${cyc}z.cyclone.trackatcfunix +# cat ${TRKDATA}/long.${atcfout}.atcfunix.${PDY}${shh} | \ +# sed s:GFSO:GFSP:g \ +# > ${COMOUT}/gfsp.t${cyc}z.cyclone.trackatcfunix + + fi + + if [ "$SENDDBN" = 'YES' ] + then + if [ ${cmodel} != 'gfdl' ] + then + $DBNROOT/bin/dbn_alert MODEL ENS_TRACKER $job ${COMOUT}/${atcfout}.t${cyc}z.cyclone.trackatcfunix + $DBNROOT/bin/dbn_alert MODEL ENS_TRACKER $job ${COMOUT}/${atcfout}p.t${cyc}z.cyclone.trackatcfunix + fi + fi + +# ------------------------------------------ +# Cat atcfunix files to storm trackers files +# ------------------------------------------ +# +# We need to parse apart the atcfunix file and distribute the forecasts to +# the necessary directories. To do this, first sort the atcfunix records +# by forecast hour (k6), then sort again by ocean basin (k1), storm number (k2) +# and then quadrant radii wind threshold (k12). Once you've got that organized +# file, break the file up by putting all the forecast records for each storm +# into a separate file. Then, for each file, find the corresponding atcfunix +# file in the storm trackers directory and dump the atcfunix records for that storm +# in there. NOTE: Only do this if the model run is NOT for the CMC or +# ECMWF ensemble. The reason is that we do NOT want to write out the individual +# member tracks to the atcfunix file. We only want to write out the ensemble +# mean track to the atcfunix file, and the mean track is calculated and written +# out in a separate script. + + if [ ${cmodel} = 'gfdl' ] + then + auxfile=${COMOUT}/${stormenv}.${PDY}${cyc}.trackeratcfunix + else + auxfile=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + fi + + sort -k6 ${auxfile} | sort -k1 -k2 -k12 >atcfunix.sorted + old_string="XX, XX" + + ict=0 + while read unixrec + do + storm_string=` echo "${unixrec}" | cut -c1-6` + if [ "${storm_string}" = "${old_string}" ] + then + echo "${unixrec}" >>atcfunix_file.${ict} + else + let ict=ict+1 + echo "${unixrec}" >atcfunix_file.${ict} + old_string="${storm_string}" + fi + done >$COMOUTatcf/${at}${NO}${syyyy}/ncep_a${at}${NO}${syyyy}.dat + if [ ${cmodel} = 'gfs' ]; then + cat atcfunix_file.$mct | sed s:AVNO:GFSO:g > gfso_atcfunix_file.$mct + cat gfso_atcfunix_file.$mct >>$COMOUTatcf/${at}${NO}${syyyy}/ncep_a${at}${NO}${syyyy}.dat + + cat atcfunix_file.$mct >>$COMOUTatcf/${at}${NO}${syyyy}/a${at}${NO}${syyyy}.dat + cat gfso_atcfunix_file.$mct >>$COMOUTatcf/${at}${NO}${syyyy}/a${at}${NO}${syyyy}.dat + fi + + set +x + echo " " + echo "+++ Adding records to TPC ATCFUNIX directory: $COMOUTatcf/${at}${NO}${syyyy}/ncep_${at}${NO}${syyyy}" + echo " " + set -x + done + fi +fi diff --git a/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh_10152019 b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh_10152019 new file mode 100755 index 0000000000..535e6f5560 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh_10152019 @@ -0,0 +1,2658 @@ +#!/bin/ksh +export PS4=' + extrkr_gfs.sh line $LINENO: ' + +set +x +############################################################################## +echo " " +echo "------------------------------------------------" +echo " TC Track in model GRIB2 output " +echo " Models:GFS, GEFS, CENS, FENS, NAVGEM " +echo "------------J.Peng Jan.21, 2015 ----------------" +echo "Current time is: `date`" +echo " " +############################################################################## +set -x + +loopnum=$1 +cmodel=$2 +ymdh=$3 +pert=$4 +TRKDATA=$5 + +USE_OPER_VITALS=YES +# USE_OPER_VITALS=NO +# USE_OPER_VITALS=INIT_ONLY + +############################################################################## +# +# FLOW OF CONTROL +# +# 1. Define data directories and file names for the input model +# 2. Process input starting date/cycle information +# 3. Update TC Vitals file and select storms to be processed +# 4. Cut apart input GRIB files to select only the needed parms and hours +# 5. Execute the tracker +# 6. Copy the output track files to various locations +# +############################################################################## +msg="has begun for ${cmodel} ${pert} at ${cyc}z" +postmsg "$jlogfile" "$msg" + +# This script runs the hurricane tracker using operational GRIB model output. +# This script makes sure that the data files exist, it then pulls all of the +# needed data records out of the various GRIB forecast files and puts them +# into one, consolidated GRIB file, and then runs a program that reads the TC +# Vitals records for the input day and updates the TC Vitals (if necessary). +# It then runs gettrk, which actually does the tracking. +# +# Environmental variable inputs needed for this scripts: +# PDY -- The date for data being processed, in YYYYMMDD format +# cyc -- The numbers for the cycle for data being processed (00, 06, 12, 18) +# cmodel -- Model being processed (gfs, mrf, ukmet, ecmwf, nam, ngm, ngps, +# gdas, gfdl, ens (ncep ensemble)) +# envir -- 'prod' or 'test' +# SENDCOM -- 'YES' or 'NO' +# stormenv -- This is only needed by the tracker run for the GFDL model. +# 'stormenv' contains the name/id that is used in the input +# grib file names. +# pert -- This is only needed by the tracker run for the NCEP ensemble. +# 'pert' contains the ensemble member id (e.g., n2, p4, etc.) +# which is used as part of the grib file names. +# +# For testing script interactively in non-production set following vars: +# gltrkdir - Directory for output tracks +# archsyndir - Directory with syndir scripts/exec/fix +# + +qid=$$ +#----------------------------------------------# +# Get input date information # +#----------------------------------------------# + +export jobid=${jobid:-testjob} +export SENDCOM=${SENDCOM:-NO} + +# Define tracker working directory for this ensemble member + +if [ ! -d $TRKDATA ] +then + mkdir -p $TRKDATA +fi +cd $TRKDATA + +if [ ${#PDY} -eq 0 -o ${#cyc} -eq 0 -o ${#cmodel} -eq 0 ] +then + set +x + echo " " + echo "FATAL ERROR: Something wrong with input parameters." + echo "PDY= ${PDY}, cyc= ${cyc}, cmodel= ${cmodel}" + set -x + err_exit "FAILED ${jobid} -- BAD INPUTS AT LINE $LINENO IN TRACKER SCRIPT - ABNORMAL EXIT" +else + set +x + echo " " + echo " #-----------------------------------------------------------------#" + echo " At beginning of tracker script, the following imported variables " + echo " are defined: " + echo " PDY ................................... $PDY" + echo " cyc ................................... $cyc" + echo " cmodel ................................ $cmodel" + echo " jobid ................................. $jobid" + echo " envir ................................. $envir" + echo " SENDCOM ............................... $SENDCOM" + echo " " + set -x +fi + +scc=`echo ${PDY} | cut -c1-2` +syy=`echo ${PDY} | cut -c3-4` +smm=`echo ${PDY} | cut -c5-6` +sdd=`echo ${PDY} | cut -c7-8` +shh=${cyc} +symd=`echo ${PDY} | cut -c3-8` +syyyy=`echo ${PDY} | cut -c1-4` +CENT=`echo ${PDY} | cut -c1-2` + +#------J.Peng----01-21-2015--------- +archsyndir=${archsyndir:-${COMINsyn:?}} +gltrkdir=${gltrkdir:-${COMOUThur:?}} + +#----------------------------------------------------------------# +# +# --- Define data directories and data file names --- +# +# Convert the input model to lowercase letters and check to see +# if it's a valid model, and assign a model ID number to it. +# This model ID number is passed into the Fortran program to +# let the program know what set of forecast hours to use in the +# ifhours array. Also, set the directories for the operational +# input GRIB data files and create templates for the file names. +# +#----------------------------------------------------------------# + +cmodel=`echo ${cmodel} | tr "[A-Z]" "[a-z]"` + +#---- tracking variable list------------------------------------ +user_wants_to_track_zeta850='y' +user_wants_to_track_zeta700='y' +user_wants_to_track_wcirc850='y' +user_wants_to_track_wcirc700='y' +user_wants_to_track_gph850='y' +user_wants_to_track_gph700='y' +user_wants_to_track_mslp='y' +user_wants_to_track_wcircsfc='n' +user_wants_to_track_zetasfc='n' +user_wants_to_track_thick500850='n' +user_wants_to_track_thick200850='n' +user_wants_to_track_thick200500='n' + +case ${cmodel} in + gdas) set +x; echo " " ; + echo " ++ operational FV3-GDAS chosen" ; + echo " "; set -x ; + gdasdir=${gdasdir:-${COMINgdas:?}} ; + gdasgfile=gdas.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-3} ; + fcstlen=${FHMAX_CYCLONE:-9} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=8 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=72 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + +# PHASEFLAG='n' ; + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="gdas" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="gdas" ;; + + gfs) set +x; echo " " ; + echo " ++ operational FV3-GFS chosen" ; + echo " "; set -x ; + gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}} ; + gfsgfile=gfs.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=1 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=15 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + +# PHASEFLAG='n' ; + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="avno" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="avno" ;; + + ens) set +x; echo " " ; + echo " ++ operational ensemble member ${pert} chosen"; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + + ensdira=${ensdira:-${COMINgefs:?}/pgrb2ap5} ; + ensgfilea=ge${pert}.t${cyc}z.pgrb2a.0p50.f ; + ensdirb=${ensdirb:-${COMINgefs:?}/pgrb2bp5} ; + ensgfileb=ge${pert}.t${cyc}z.pgrb2b.0p50.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=10 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="a${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="a${pert_posneg}${pert_num}" ;; + + cmc) set +x; echo " " ; + echo " ++ operational CMC chosen" ; + echo " "; set -x ; + cmcdir=${cmcdir:-${DCOM:?}} ; + cmcgfile=CMC_glb_latlon.24x.24_${PDY}${cyc}_P ; + cmcgfile2=_NCEP.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=15 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=39 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="cmc " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="cmc" ;; + + cens) set +x; echo " " ; + echo " ++ Canadian ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + ccedir=${ccedir:-${DCOM:?}} ; + ccegfile=${PDY}${cyc}_CMC_naefs_hr_latlon0p5x0p5_P ; + ccegfile2=_0${pert_num}.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=16 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="c${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="c${pert_posneg}${pert_num}" ;; + + fens) set +x; echo " " ; + echo " ++ FNMOC ensemble member ${pert} chosen" ; + echo " "; set -x ; + pert=` echo ${pert} | tr '[A-Z]' '[a-z]'` ; + PERT=` echo ${pert} | tr '[a-z]' '[A-Z]'` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + fensdir=${fensdir:-${DCOM:?}} ; + fensgfile=ENSEMBLE.MET.fcst_et0${pert_num}. ; + gensgfile=.${PDY}${cyc} ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-240} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=22 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=91 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=1 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + pert_posneg=` echo "${pert}" | cut -c1-1` ; + pert_num=` echo "${pert}" | cut -c2-3` ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="n${pert_posneg}${pert_num}" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="n${pert_posneg}${pert_num}" ;; + + ngps) set +x; echo " " ; + echo " ++ operational NAVGEM chosen" ; + echo " "; set -x ; + + ngpsdir=${ngpsdir:-${DCOM:?}} ; + ngpsgfile=US058GMET-OPSbd2.NAVGEM ; + ngemgfile=-${PDY}${cyc}-NOAA-halfdeg.gr2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-180} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=7 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=29 ; + atcffreq=$((vit_incr*100)) ; + + trkrwbd=0 ; + trkrebd=360 ; + trkrnbd=90 ; + trkrsbd=-90 ; + trkrtype='tracker' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='n' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="ngx " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="ngx" ;; + + *) msg="FATAL ERROR: Model $cmodel is not recognized." ; + echo "$msg"; postmsg $jlogfile "$msg" ; + err_exit "FAILED ${jobid} -- UNKNOWN cmodel IN TRACKER SCRIPT - ABNORMAL EXIT";; + +esac + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${cmodel} = "gfs" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300)" + + elif [ ${cmodel} = "gdas" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300)" + + elif [ ${cmodel} = "ens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300)" + + elif [ ${cmodel} = "cens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:300|TMP:500|TMP:250)" + + elif [ ${cmodel} = "cmc" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250)" + + elif [ ${cmodel} = "fens" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250)" + + elif [ ${cmodel} = "ngps" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:400|HGT:300|TMP:500|TMP:400|TMP:300)" + + fi + + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc GH:300 GH:400 GH:500 GH:925 T:300 T:400 T:500" +else + if [ ${cmodel} = "gfs" ]; then + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET)" + elif [ ${cmodel} = "gdas" ]; then + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET)" + elif [ ${cmodel} = "ens" ]; then + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET)" + else + PARMlist="(HGT:850|HGT:700|UGRD:850|UGRD:700|UGRD:500|VGRD:850|VGRD:700|VGRD:500|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL)" + fi + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc" +fi + +#---------------------------------------------------------------# +# +# -------- TC Vitals processing -------- +# +# Check Steve Lord's operational tcvitals file to see if any +# vitals records were processed for this time by his system. +# If there were, then you'll find a file in /com/gfs/prod/gfs.yymmdd +# with the vitals in it. Also check the raw TC Vitals file in +# /com/arch/prod/syndat , since this may contain storms that Steve's +# system ignored (Steve's system will ignore all storms that are +# either over land or very close to land); We still want to track +# these inland storms, AS LONG AS THEY ARE NHC STORMS (don't +# bother trying to track inland storms that are outside of NHC's +# domain of responsibility -- we don't need that info). +# UPDATE 3/27/09 MARCHOK: The SREF is run at off-synoptic times +# (03,09,15,21Z). There are no tcvitals issued at these offtimes, +# so the updating of the "old" tcvitals is critical for running +# the tracker on SREF. For updating the old tcvitals for SREF, +# we need to look 3h back, not 6h as for the other models that +# run at synoptic times. +#--------------------------------------------------------------# + +#old_ymdh=`${NDATE:?} -${vit_incr} ${PDY}${cyc}` +old_ymdh=`${NDATE:?} -6 ${PDY}${cyc}` +old_4ymd=`echo ${old_ymdh} | cut -c1-8` +old_ymd=`echo ${old_ymdh} | cut -c3-8` +old_hh=`echo ${old_ymdh} | cut -c9-10` +old_str="${old_ymd} ${old_hh}00" + +#future_ymdh=`${NDATE:?} ${vit_incr} ${PDY}${cyc}` +future_ymdh=`${NDATE:?} 6 ${PDY}${cyc}` +future_4ymd=`echo ${future_ymdh} | cut -c1-8` +future_ymd=`echo ${future_ymdh} | cut -c3-8` +future_hh=`echo ${future_ymdh} | cut -c9-10` +future_str="${future_ymd} ${future_hh}00" + +if [ ${modtyp} = 'global' ] +then +# synvitdir=${COMROOT}/gfs/prod/gfs.${PDY} + synvitdir=${COMINgfs:?}/${cyc} + synvitfile=gfs.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/gfs/prod/gfs.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd}/${old_hh} + synvitold_file=gfs.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/gfs/prod/gfs.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd}/${future_hh} + synvitfuture_file=gfs.t${future_hh}z.syndata.tcvitals.tm00 +else +# synvitdir=${COMROOT}/nam/prod/nam.${PDY} + synvitdir=${COMINnam:?} + synvitfile=nam.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/nam/prod/nam.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=nam.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/nam/prod/nam.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=nam.t${future_hh}z.syndata.tcvitals.tm00 +fi + +set +x +echo " " +echo " -----------------------------" +echo " " +echo " Now sorting and updating the TC Vitals file. Please wait...." +echo " " +set -x + +current_str="${symd} ${cyc}00" + +if [ -s ${synvitdir}/${synvitfile} -o\ + -s ${synvitold_dir}/${synvitold_file} -o\ + -s ${synvitfuture_dir}/${synvitfuture_file} ] +then + grep "${old_str}" ${synvitold_dir}/${synvitold_file} \ + >${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${synvitdir}/${synvitfile} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${future_str}" ${synvitfuture_dir}/${synvitfuture_file} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} +else + set +x + echo " " + echo " There is no (synthetic) TC vitals file for ${cyc}z in ${synvitdir}," + echo " nor is there a TC vitals file for ${old_hh}z in ${synvitold_dir}." + echo " nor is there a TC vitals file for ${future_hh}z in ${synvitfuture_dir}," + echo " Checking the raw TC Vitals file ....." + echo " " + set -x +fi + +# Take the vitals from Steve Lord's /com/gfs/prod tcvitals file, +# and cat them with the NHC-only vitals from the raw, original +# /com/arch/prod/synda_tcvitals file. Do this because the nwprod +# tcvitals file is the original tcvitals file, and Steve runs a +# program that ignores the vitals for a storm that's over land or +# even just too close to land, and for tracking purposes for the +# US regional models, we need these locations. Only include these +# "inland" storm vitals for NHC (we're not going to track inland +# storms that are outside of NHC's domain of responsibility -- we +# don't need that info). +# UPDATE 5/12/98 MARCHOK: awk logic is added to screen NHC +# vitals such as "89E TEST", since TPC +# does not want tracks for such storms. + +grep "${old_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${current_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${future_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + + +# IMPORTANT: When "cat-ing" these files, make sure that the vitals +# files from the "raw" TC vitals files are first in order and Steve's +# TC vitals files second. This is because Steve's vitals file has +# been error-checked, so if we have a duplicate tc vitals record in +# these 2 files (very likely), program supvit.x below will +# only take the last vitals record listed for a particular storm in +# the vitals file (all previous duplicates are ignored, and Steve's +# error-checked vitals records are kept). + +cat ${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} ${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} \ + >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +#--------------------------------------------------------------# +# Now run a fortran program that will read all the TC vitals +# records for the current dtg and the dtg from 6h ago, and +# sort out any duplicates. If the program finds a storm that +# was included in the vitals file 6h ago but not for the current +# dtg, this program updates the 6h-old first guess position +# and puts these updated records as well as the records from +# the current dtg into a temporary vitals file. It is this +# temporary vitals file that is then used as the input for the +# tracking program. +#--------------------------------------------------------------# + +#oldymdh=`${NDATE:?} -${vit_incr} ${PDY}${cyc}` +oldymdh=`${NDATE:?} -6 ${PDY}${cyc}` +oldyy=`echo ${oldymdh} | cut -c3-4` +oldmm=`echo ${oldymdh} | cut -c5-6` +olddd=`echo ${oldymdh} | cut -c7-8` +oldhh=`echo ${oldymdh} | cut -c9-10` +oldymd=${oldyy}${oldmm}${olddd} + +futureymdh=`${NDATE:?} 6 ${PDY}${cyc}` +futureyy=`echo ${futureymdh} | cut -c3-4` +futuremm=`echo ${futureymdh} | cut -c5-6` +futuredd=`echo ${futureymdh} | cut -c7-8` +futurehh=`echo ${futureymdh} | cut -c9-10` +futureymd=${futureyy}${futuremm}${futuredd} + +echo "&datenowin dnow%yy=${syy}, dnow%mm=${smm}," >${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&dateoldin dold%yy=${oldyy}, dold%mm=${oldmm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dold%dd=${olddd}, dold%hh=${oldhh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&datefuturein dfuture%yy=${futureyy}, dfuture%mm=${futuremm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dfuture%dd=${futuredd}, dfuture%hh=${futurehh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&hourinfo vit_hr_incr=${vit_incr}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period." + echo "!!! File ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} is empty." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi + +fi + +# For tcgen cases, filter to use only vitals from the ocean +# basin of interest.... + +if [ ${trkrtype} = 'tcgen' ] + then + + if [ ${numvitrecs} -gt 0 ] + then + + fullvitfile=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + cp $fullvitfile ${TRKDATA}/vitals.all_basins.${atcfout}.${PDY}${cyc} + basin=` echo $regtype | cut -c1-2` + + if [ ${basin} = 'al' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "L") print $0}' \ + >${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + if [ ${basin} = 'ep' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "E") print $0}' \ + >${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + if [ ${basin} = 'wp' ]; then + cat $fullvitfile | awk '{if (substr($0,8,1) == "W") print $0}' \ + >${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} + cp ${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} \ + ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + fi + + cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + + fi + +fi + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Before running the program to read, sort and update the vitals, +# first run the vitals through some awk logic, the purpose of +# which is to convert all the 2-digit years into 4-digit years. +# We need this logic to ensure that all the vitals going +# into supvit.f have uniform, 4-digit years in their records. +# +# 1/8/2000: sed code added by Tim Marchok due to the fact that +# some of the vitals were getting past the syndata/qctropcy +# error-checking with a colon in them; the colon appeared +# in the character immediately to the left of the date, which +# was messing up the "(length($4) == 8)" statement logic. +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +sed -e "s/\:/ /g" ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} > ${TRKDATA}/tempvit +mv ${TRKDATA}/tempvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +awk ' +{ + yycheck = substr($0,20,2) + if ((yycheck == 20 || yycheck == 19) && (length($4) == 8)) { + printf ("%s\n",$0) + } + else { + if (yycheck >= 0 && yycheck <= 50) { + printf ("%s20%s\n",substr($0,1,19),substr($0,20)) + } + else { + printf ("%s19%s\n",substr($0,1,19),substr($0,20)) + } + } +} ' ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 + +mv ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +if [ ${numvitrecs} -gt 0 ] +then + + export pgm=supvit_g2 + . prep_step + + export FORT31=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + export FORT51=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_g2 <${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + suvrcc=$? + + if [ ${suvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit.x, " + echo "!!! which is the program that updates the TC Vitals file." + echo "!!! Return code from supvit.x = ${suvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING supvit_g2 IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + + touch ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#----------------------------------------------------------------- +# In this section, check to see if the user requested the use of +# operational TC vitals records for the initial time only. This +# option might be used for a retrospective medium range forecast +# in which the user wants to initialize with the storms that are +# currently there, but then let the model do its own thing for +# the next 10 or 14 days.... + + +if [ ${USE_OPER_VITALS} = 'INIT_ONLY' ]; then + + if [ ${init_flag} = 'yes' ]; then + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are currently at." + echo " " + set -x + else + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are now *PAST*." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + fi + +elif [ ${USE_OPER_VITALS} = 'NO' ]; then + + set +x + echo " " + echo "NOTE: User has requested that historical vitals not be used...." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#------------------------------------------------------------------# +# Now select all storms to be processed, that is, process every +# storm that's listed in the updated vitals file for the current +# forecast hour. If there are no storms for the current time, +# then exit. +#------------------------------------------------------------------# + +numvitrecs=`cat ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period " + echo "!!! in the UPDATED vitals file." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi +fi + +set +x +echo " " +echo " *--------------------------------*" +echo " | STORM SELECTION |" +echo " *--------------------------------*" +echo " " +set -x + +ict=1 +while [ $ict -le 15 ] +do + stormflag[${ict}]=3 + let ict=ict+1 +done + +dtg_current="${symd} ${cyc}00" +stormmax=` grep "${dtg_current}" ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +if [ ${stormmax} -gt 15 ] +then + stormmax=15 +fi + +sct=1 +while [ ${sct} -le ${stormmax} ] +do + stormflag[${sct}]=1 + let sct=sct+1 +done + + +#---------------------------------------------------------------# +# +# -------- "Genesis" Vitals processing -------- +# +# May 2006: This entire genesis tracking system is being +# upgraded to more comprehensively track and categorize storms. +# One thing that has been missing from the tracking system is +# the ability to keep track of storms from one analysis cycle +# to the next. That is, the current system has been very +# effective at tracking systems within a forecast, but we have +# no methods in place for keeping track of storms across +# difference initial times. For example, if we are running +# the tracker on today's 00z GFS analysis, we will get a +# position for various storms at the analysis time. But then +# if we go ahead and run again at 06z, we have no way of +# telling the tracker that we know about the 00z position of +# this storm. We now address that problem by creating +# "genesis" vitals, that is, when a storm is found at an +# analysis time, we not only produce "atcfunix" output to +# detail the track & intensity of a found storm, but we also +# produce a vitals record that will be used for the next +# run of the tracker script. These "genesis vitals" records +# will be of the format: +# +# YYYYMMDDHH_AAAH_LLLLX_TYP +# +# Where: +# +# YYYYMMDDHH = Date the storm was FIRST identified +# by the tracker. +# AAA = Abs(Latitude) * 10; integer value +# H = 'N' for norther hem, 'S' for southern hem +# LLLL = Abs(Longitude) * 10; integer value +# X = 'E' for eastern hem, 'W' for western hem +# TYP = Tropical cyclone storm id if this is a +# tropical cyclone (e.g., "12L", or "09W", etc). +# If this is one that the tracker instead "Found +# On the Fly (FOF)", we simply put those three +# "FOF" characters in there. +genvitfile=${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} +touch ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + +if [ -f $genvitfile ]; then + d6ago_ymdh=` ${NDATE:?} -6 ${PDY}${cyc}` + d6ago_4ymd=` echo ${d6ago_ymdh} | cut -c1-8` + d6ago_ymd=` echo ${d6ago_ymdh} | cut -c3-8` + d6ago_hh=` echo ${d6ago_ymdh} | cut -c9-10` + d6ago_str="${d6ago_ymd} ${d6ago_hh}00" + + d6ahead_ymdh=` ${NDATE:?} 6 ${PDY}${cyc}` + d6ahead_4ymd=` echo ${d6ahead_ymdh} | cut -c1-8` + d6ahead_ymd=` echo ${d6ahead_ymdh} | cut -c3-8` + d6ahead_hh=` echo ${d6ahead_ymdh} | cut -c9-10` + d6ahead_str="${d6ahead_ymd} ${d6ahead_hh}00" + + syyyym6=` echo ${d6ago_ymdh} | cut -c1-4` + smmm6=` echo ${d6ago_ymdh} | cut -c5-6` + sddm6=` echo ${d6ago_ymdh} | cut -c7-8` + shhm6=` echo ${d6ago_ymdh} | cut -c9-10` + + syyyyp6=` echo ${d6ahead_ymdh} | cut -c1-4` + smmp6=` echo ${d6ahead_ymdh} | cut -c5-6` + sddp6=` echo ${d6ahead_ymdh} | cut -c7-8` + shhp6=` echo ${d6ahead_ymdh} | cut -c9-10` + + set +x + echo " " + echo " d6ago_str= --->${d6ago_str}<---" + echo " current_str= --->${current_str}<---" + echo " d6ahead_str= --->${d6ahead_str}<---" + echo " " + #echo " Listing and contents of ${genvitfile} follow " + #echo " for the times 6h ago, current and 6h ahead:" + #echo " " + set -x + + #ls -la ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + #cat ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + + grep "${d6ago_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${d6ahead_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + num_gen_vits=`cat ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} | wc -l` +else + echo "WARNING: Genesis Vitals from previous run(s) not found!" +fi + +echo "&datenowin dnow%yy=${syyyy}, dnow%mm=${smm}," >${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6agoin d6ago%yy=${syyyym6}, d6ago%mm=${smmm6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ago%dd=${sddm6}, d6ago%hh=${shhm6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6aheadin d6ahead%yy=${syyyyp6}, d6ahead%mm=${smmp6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ahead%dd=${sddp6}, d6ahead%hh=${shhp6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + +num_gen_vits=0 + +if [ ${num_gen_vits} -gt 0 ] +then + export pgm=supvit_gen + . prep_step + + export FORT31=${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + export FORT51=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_gen <${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + sgvrcc=$? + + if [ ${sgvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit_gen, " + echo "!!! which is the program that updates the genesis vitals file." + echo "!!! Return code from supvit_gen = ${sgvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING SUPVIT_GEN IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + + touch ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + +fi + + +#-----------------------------------------------------------------# +# +# ------ CUT APART INPUT GRIB FILES ------- +# +# For the selected model, cut apart the GRIB input files in order +# to pull out only the variables that we need for the tracker. +# Put these selected variables from all forecast hours into 1 big +# GRIB file that we'll use as input for the tracker. +# +#-----------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------" +echo " NOW CUTTING APART INPUT GRIB FILES TO " +echo " CREATE 1 BIG GRIB INPUT FILE " +echo " -----------------------------------------" +echo " " +set -x + +regflag=`grep NHC ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +# -------------------------------------------------- +# Process GDAS data +# -------------------------------------------------- + +if [ ${model} -eq 8 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gdasgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gdasgribfile.${PDY}${cyc} + fi + + rm ${TRKDATA}/master.gdasgribfile.${PDY}${cyc}.f* + rm ${TRKDATA}/gdasgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gdasgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gdas wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gdasdir:?}/${gdasgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GDAS File missing: ${gdasdir:?}/${gdasgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GDAS FILE IN extrkr_gfs.sh: ${gdasdir:?}/${gdasgfile}${fhour}" + fi + + gfile=${gdasdir}/${gdasgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gdasgribfile.${PDY}${cyc}.f${fhour} + + gdas_master_file=${TRKDATA}/master.gdasgribfile.${PDY}${cyc}.f${fhour} + gdas_cat_file=${TRKDATA}/gdasgribfile.${PDY}${cyc} + cat ${gdas_master_file} >>${gdas_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gdasgribfile.${PDY}${cyc} ${TRKDATA}/gdasixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + + catfile=${TRKDATA}/gdas.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in 300-500hPa T-mean for model= $cmodel and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gdasgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gdasixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in 300-500hPa T-mean for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + + fi + + gfile=${TRKDATA}/gdasgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/gdasixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gdasgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gdasixfile.${PDY}${cyc} + +fi + +# -------------------------------------------------- +# Process GFS data +# -------------------------------------------------- + +if [ ${model} -eq 1 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gfsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc} + fi + + rm ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f* + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gfsgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gfs wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gfsdir:?}/${gfsgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GFS File missing: ${gfsdir}/${gfsgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "MISSING GFS FILE IN extrkr_gfs.sh: ${gfsdir}/${gfsgfile}${fhour}" + fi + + gfile=${gfsdir}/${gfsgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + + gfs_master_file=${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + gfs_cat_file=${TRKDATA}/gfsgribfile.${PDY}${cyc} + cat ${gfs_master_file} >>${gfs_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gfsgribfile.${PDY}${cyc} ${TRKDATA}/gfsixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + + catfile=${TRKDATA}/gfs.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in 300-500hPa T-mean for model= $cmodel and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in 300-500hPa T-mean for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + + fi + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gfsixfile.${PDY}${cyc} + +fi + +# -------------------------------------------------- +# Process NCEP Ensemble perturbation, if selected +# -------------------------------------------------- + +if [ ${model} -eq 10 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ensdira:?}/${ensgfilea}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: ENSEMBLE ${PERT} File missing: ${ensdira}/${ensgfilea}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING GEFS FILE IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${TRKDATA}/${ensgfilea}${fhour} + cat ${ensdira}/${ensgfilea}${fhour} ${ensdirb}/${ensgfileb}${fhour} > $gfile + + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} + + cat ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc}.${fhour} >> ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + done + + ${GRB2INDEX:?} ${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} ${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/ens${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in 300-500hPa T-mean for pert= $pert and fhour= $fhour before = `date`" + echo " " + set -x + + ffile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour for $cmodel $pert " + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_${pert}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in 300-500hPa T-mean for pert= $pert and fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + + gfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ens${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ens${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------------------------------ +# Process Canadian (CMC) hi-res deterministic, if selected +# ------------------------------------------------------ + +if [ ${model} -eq 15 ]; then + + if [ $loopnum -eq 1 ]; then + if [ -s ${TRKDATA}/cmcgribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/cmcgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + if [ ! -s ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} ]; then + set +x + echo " " + echo "FATAL ERROR: CMC File missing ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + fi + + gfile=${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + done +# J.Peng--04-05-2017--changing grid from 180E to 0E------------ +# wgrib2 junk -small_grib 0:359.76 -90:90 junk2 +# cp ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/junk +# rm -f ${TRKDATA}/cmcgribfile.${PDY}${cyc} +# ${WGRIB2} ${TRKDATA}/junk -small_grib 0:359.76 -90:90 ${TRKDATA}/junk2 +# cp ${TRKDATA}/junk2 ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + ${GRB2INDEX:?} ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/cmcixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +fi + +# ------------------------------------------------------ +# Process Canadian Ensemble perturbation, if selected +# ------------------------------------------------------ +if [ ${model} -eq 16 ]; then + if [ $loopnum -eq 1 ]; then + + if [ -s ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + + if [ ! -s ${ccedir:?}/${ccegfile}${fhour}${ccegfile2} ]; then + set +x + echo " " + echo "FATAL ERROR: CANADIAN ENSEMBLE ${PERT} missing: ${ccegfile}${fhour}${ccegfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING CMC ENSEMBLE FILE IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${ccedir}/${ccegfile}${fhour}${ccegfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} ${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + export err=$?; err_chk +# Process the cyclone phase variables, if requested + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/${cmodel}_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + gparm=11 + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour " + echo "rcc= $rcc EXITING....${cmodel}.${pert}.${PDY}${cyc} " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + fi + fi + gfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/cce${pert}gribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cce${pert}ixfile.${PDY}${cyc} + +fi + +# ------------------------------ +# Process FNMOC Ensemble, if selected +# ------------------------------ +if [ ${model} -eq 22 ]; then + if [ $loopnum -eq 1 ]; then + + if [ -s ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ]; then + rm ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + fensfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + + let attempts=1 + while [ $attempts -le 30 ]; do + if [ -s $fensfile ]; then + break + else + sleep 60 + let attempts=attempts+1 + fi + done + if [ $attempts -gt 30 ] && [ ! -s $fensfile ]; then + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "$fensfile" >> ${DATA}/missing_fens.txt + exit + else + err_exit "$fensfile still not available after waiting 30 minutes... exiting" + fi + fi + #${WGRIB2:?} -set_byte 1 11 1 -s $fensfile >fens.ix # set_byte resets local table to 1 to remove error messages + + gfile=${fensdir}/${fensgfile}${fhour}${gensgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} ${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + export err=$?; err_chk + +# -------------------------------------------- + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${pert}.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/fens_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... ${cmodel}.${pert}.${PDY}${cyc}" + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + gparm=11 + ffile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour " + echo "rcc= $rcc EXITING....${cmodel}.${pert}.${PDY}${cyc} " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${pert}.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${pert}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + ifile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/fensgribfile.${pert}.${PDY}${cyc} + ixfile=${TRKDATA}/fensixfile.${pert}.${PDY}${cyc} +fi + +# ------------------------------ +# Process NAVGEM, if selected +# ------------------------------ +if [ ${model} -eq 7 ]; then + + if [ $loopnum -eq 1 ]; then + if [ -s ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ]; then + rm ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs}; do + if [ ! -s ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} ]; then + set +x + echo " " + echo "FATAL ERROR: NAVGEM File missing ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" >> ${DATA}/missing_ngps.txt + exit + else + err_exit "MISSING NAVGEM FILE at $0:$LINENO" + fi + fi + + gfile=${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + + done + + ${GRB2INDEX:?} ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ${TRKDATA}/ngpsixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs}; do + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for GPH AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + . prep_step + + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "vint_g2.x- ERROR for T AT extrkr_gfs.sh LINE $LINENO" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "tave_g2.x- ERROR AT extrkr_gfs.sh LINE $LINENO" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ngpsixfile.${PDY}${cyc} +fi + +#------------------------------------------------------------------------# +# Now run the tracker # +#------------------------------------------------------------------------# +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW EXECUTING TRACKER......" +echo " -----------------------------------------------" +echo " " +set -x + +ist=1 +while [ $ist -le 15 ] +do + if [ ${stormflag[${ist}]} -ne 1 ] + then + set +x; echo "Storm number $ist NOT selected for processing"; set -x + else + set +x; echo "Storm number $ist IS selected for processing...."; set -x + fi + let ist=ist+1 +done + +namelist=${TRKDATA}/input.${atcfout}.${PDY}${cyc} +ATCFNAME=` echo "${atcfname}" | tr '[a-z]' '[A-Z]'` + +if [ ${cmodel} = 'sref' ]; then + export atcfymdh=` ${NDATE:?} -3 ${scc}${syy}${smm}${sdd}${shh}` +else + export atcfymdh=${scc}${syy}${smm}${sdd}${shh} +fi + +#------07-26-2019----creating the fix-file for GFS tracking ----- +if [ ${model} -eq 1 -o ${model} -eq 8 ]; then +. prep_step + +# Input files +namelist_4_fix=${TRKDATA}/namelist_4_fix_input.${PDY}${cyc} +echo "&timein maxhrs=${fcstlen}," >${namelist_4_fix} +echo " dthrs=${vit_incr}/" >>${namelist_4_fix} + +# Output file +export FORT10=${TRKDATA}/${cmodel}.tracker_leadtimes + +${EXECens_tracker}/leadtime <${namelist_4_fix} +rcc=$? + +if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to leadtime at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "leadtime - ERROR AT extrkr_gfs.sh LINE $LINENO" +fi + +fi + +#------07-26-2019----creating the fix-file for GFS tracking ----- + +#contour_interval=100.0 +#write_vit=n +#want_oci=.TRUE. + +echo "&datein inp%bcc=${scc},inp%byy=${syy},inp%bmm=${smm}," >${namelist} +echo " inp%bdd=${sdd},inp%bhh=${shh},inp%model=${model}," >>${namelist} +echo " inp%modtyp='${modtyp}'," >>${namelist} +echo " inp%lt_units='${lead_time_units}'," >>${namelist} +echo " inp%file_seq='${file_sequence}'," >>${namelist} +echo " inp%nesttyp='${nest_type}'/" >>${namelist} + +echo "&atcfinfo atcfnum=${atcfnum},atcfname='${ATCFNAME}'," >>${namelist} +echo " atcfymdh=${atcfymdh},atcffreq=${atcffreq}/" >>${namelist} + +echo "&trackerinfo trkrinfo%westbd=${trkrwbd}," >>${namelist} +echo " trkrinfo%eastbd=${trkrebd}," >>${namelist} +echo " trkrinfo%northbd=${trkrnbd}," >>${namelist} +echo " trkrinfo%southbd=${trkrsbd}," >>${namelist} +echo " trkrinfo%type='${trkrtype}'," >>${namelist} +echo " trkrinfo%mslpthresh=${mslpthresh}," >>${namelist} +echo " trkrinfo%use_backup_mslp_grad_check='${use_backup_mslp_grad_check}'," >>${namelist} +echo " trkrinfo%max_mslp_850=${max_mslp_850}," >>${namelist} +echo " trkrinfo%v850thresh=${v850thresh}," >>${namelist} +echo " trkrinfo%use_backup_850_vt_check='${use_backup_850_vt_check}'," >>${namelist} +echo " trkrinfo%gridtype='${modtyp}'," >>${namelist} +echo " trkrinfo%enable_timing=1," >>${namelist} +echo " trkrinfo%contint=${contour_interval}," >>${namelist} +echo " trkrinfo%want_oci=${want_oci}," >>${namelist} +echo " trkrinfo%out_vit='${write_vit}'," >>${namelist} +echo " trkrinfo%use_land_mask='${use_land_mask}'," >>${namelist} +echo " trkrinfo%inp_data_type='${inp_data_type}'," >>${namelist} +echo " trkrinfo%gribver=${gribver}," >>${namelist} +echo " trkrinfo%g2_jpdtn=${g2_jpdtn}," >>${namelist} +echo " trkrinfo%g2_mslp_parm_id=${g2_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_mslp_parm_id=${g1_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_typ=${g1_sfcwind_lev_typ}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_val=${g1_sfcwind_lev_val}/" >>${namelist} + +echo "&phaseinfo phaseflag='${PHASEFLAG}'," >>${namelist} +echo " phasescheme='${PHASE_SCHEME}'," >>${namelist} +echo " wcore_depth=${WCORE_DEPTH}/" >>${namelist} + +echo "&structinfo structflag='${STRUCTFLAG}'," >>${namelist} +echo " ikeflag='${IKEFLAG}'/" >>${namelist} + +echo "&fnameinfo gmodname='${atcfname}'," >>${namelist} +echo " rundescr='${rundescr}'," >>${namelist} +echo " atcfdescr='${atcfdescr}'/" >>${namelist} + +echo "&waitinfo use_waitfor='n'," >>${namelist} +echo " wait_min_age=10," >>${namelist} +echo " wait_min_size=100," >>${namelist} +echo " wait_max_wait=1800," >>${namelist} +echo " wait_sleeptime=5," >>${namelist} +echo " per_fcst_command=''/" >>${namelist} + +echo "&netcdflist netcdfinfo%num_netcdf_vars=${ncdf_num_netcdf_vars}," >>${namelist} +echo " netcdfinfo%netcdf_filename='${netcdffile}'," >>${namelist} +echo " netcdfinfo%rv850name='${ncdf_rv850name}'," >>${namelist} +echo " netcdfinfo%rv700name='${ncdf_rv700name}'," >>${namelist} +echo " netcdfinfo%u850name='${ncdf_u850name}'," >>${namelist} +echo " netcdfinfo%v850name='${ncdf_v850name}'," >>${namelist} +echo " netcdfinfo%u700name='${ncdf_u700name}'," >>${namelist} +echo " netcdfinfo%v700name='${ncdf_v700name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%mslpname='${ncdf_mslpname}'," >>${namelist} +echo " netcdfinfo%usfcname='${ncdf_usfcname}'," >>${namelist} +echo " netcdfinfo%vsfcname='${ncdf_vsfcname}'," >>${namelist} +echo " netcdfinfo%u500name='${ncdf_u500name}'," >>${namelist} +echo " netcdfinfo%v500name='${ncdf_v500name}'," >>${namelist} +echo " netcdfinfo%tmean_300_500_name='${ncdf_tmean_300_500_name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z200name='${ncdf_z200name}'," >>${namelist} +echo " netcdfinfo%lmaskname='${ncdf_lmaskname}'," >>${namelist} +echo " netcdfinfo%z900name='${ncdf_z900name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z800name='${ncdf_z800name}'," >>${namelist} +echo " netcdfinfo%z750name='${ncdf_z750name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%z650name='${ncdf_z650name}'," >>${namelist} +echo " netcdfinfo%z600name='${ncdf_z600name}'," >>${namelist} +echo " netcdfinfo%z550name='${ncdf_z550name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z450name='${ncdf_z450name}'," >>${namelist} +echo " netcdfinfo%z400name='${ncdf_z400name}'," >>${namelist} +echo " netcdfinfo%z350name='${ncdf_z350name}'," >>${namelist} +echo " netcdfinfo%z300name='${ncdf_z300name}'," >>${namelist} +echo " netcdfinfo%time_name='${ncdf_time_name}'," >>${namelist} +echo " netcdfinfo%lon_name='${ncdf_lon_name}'," >>${namelist} +echo " netcdfinfo%lat_name='${ncdf_lat_name}'," >>${namelist} +echo " netcdfinfo%time_units='${ncdf_time_units}'/" >>${namelist} + +echo "&parmpreflist user_wants_to_track_zeta850='${user_wants_to_track_zeta850}'," >>${namelist} +echo " user_wants_to_track_zeta700='${user_wants_to_track_zeta700}'," >>${namelist} +echo " user_wants_to_track_wcirc850='${user_wants_to_track_wcirc850}'," >>${namelist} +echo " user_wants_to_track_wcirc700='${user_wants_to_track_wcirc700}'," >>${namelist} +echo " user_wants_to_track_gph850='${user_wants_to_track_gph850}'," >>${namelist} +echo " user_wants_to_track_gph700='${user_wants_to_track_gph700}'," >>${namelist} +echo " user_wants_to_track_mslp='${user_wants_to_track_mslp}'," >>${namelist} +echo " user_wants_to_track_wcircsfc='${user_wants_to_track_wcircsfc}'," >>${namelist} +echo " user_wants_to_track_zetasfc='${user_wants_to_track_zetasfc}'," >>${namelist} +echo " user_wants_to_track_thick500850='${user_wants_to_track_thick500850}'," >>${namelist} +echo " user_wants_to_track_thick200500='${user_wants_to_track_thick200500}'," >>${namelist} +echo " user_wants_to_track_thick200850='${user_wants_to_track_thick200850}'/" >>${namelist} + +echo "&verbose verb=3,verb_g2=0/" >>${namelist} + +export pgm=gettrk_gfs +. prep_step + +export FORT11=${gribfile} +export FORT12=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} +export FORT14=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +if [ ${model} -eq 1 -o ${model} -eq 8 ]; then + export FORT15=${TRKDATA}/${cmodel}.tracker_leadtimes +else + export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes +fi + +#if [ $FHOUT_CYCLONE -eq 3 ]; then export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes_3hr ; fi +#if [ $FHMAX_CYCLONE -eq 180 ]; then export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes_180 ; fi +#if [ $vit_incr -eq 3 ]; then export FORT15=${FIXens_tracker}/${cmodel}.tracker_leadtimes_3hr ; fi +export FORT31=${ixfile} + +#if [ -s ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} ]; then +# cp ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} tcvit_rsmc_storms.txt +#fi +#if [ -s ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} ]; then +# cp ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} tcvit_genesis_storms.txt +#fi + +if [ ${trkrtype} = 'tracker' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT61=${TRKDATA}/trak.${atcfout}.all.${stormenv}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${stormenv}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${stormenv}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${stormenv}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${stormenv}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${stormenv}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${stormenv}.${PDY}${cyc} + else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${PDY}${cyc} + fi +else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${regtype}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${regtype}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${regtype}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${regtype}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${regtype}.${PDY}${cyc} +fi + +if [ ${atcfname} = 'aear' ] +then + export FORT65=${TRKDATA}/trak.${atcfout}.initvitl.${PDY}${cyc} +fi + +if [ ${write_vit} = 'y' ] +then + export FORT67=${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} +fi + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${stormenv}.${PDY}${cyc} + else + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${PDY}${cyc} + fi +fi + +if [ ${STRUCTFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${stormenv}.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${stormenv}.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${stormenv}.${PDY}${cyc} + else + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${PDY}${cyc} + fi +fi + +if [ ${IKEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${stormenv}.${PDY}${cyc} + else + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${PDY}${cyc} + fi +fi + +if [ ${trkrtype} = 'midlat' -o ${trkrtype} = 'tcgen' ]; then + export FORT77=${TRKDATA}/trkrmask.${atcfout}.${regtype}.${PDY}${cyc} +fi + +msg="$pgm start for $atcfout at ${cyc}z" +postmsg "$jlogfile" "$msg" + +set +x +echo "+++ TIMING: BEFORE gettrk ---> `date`" +set -x + +set +x +echo " " +echo "TIMING: Before call to gettrk at `date`" +echo " " +set -x + +${EXECens_tracker}/gettrk_gfs <${namelist} + +gettrk_rcc=$? +if [ ${gettrk_rcc} -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR: An error occurred while running gettrk.x, " + echo "!!! which is the program that actually gets the track." + echo "!!! Return code from gettrk.x = ${gettrk_rcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING gettrk IN TRACKER SCRIPT- ABNORMAL EXIT" +fi + +set +x +echo " " +echo "TIMING: After call to gettrk at `date`" +echo " " +set -x + +set +x +echo "+++ TIMING: AFTER gettrk ---> `date`" +set -x + +#--------------------------------------------------------------# +# Now copy the output track files to different directories +#--------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW COPYING OUTPUT TRACK FILES TO COM " +echo " -----------------------------------------------" +echo " " +set -x + + +if [ -s ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} ]; then + cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh}|cut -c1-112 > ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} +# cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh}|cut -c1-95 > ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} ${TRKDATA}/long.${atcfout}.atcfunix.${PDY}${shh} + cp ${TRKDATA}/short.${atcfout}.atcfunix.${PDY}${shh} ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${shh} +fi + +msg="$pgm end for $atcfout at ${cyc}z completed normally" +postmsg "$jlogfile" "$msg" + +# Copy atcf files to NHC archives. We'll use Steve Lord's original script, +# distatcf.sh, to do this, and that script requires the input atcf file to +# have the name "attk126", so first copy the file to that name, then call +# the distatcf.sh script. After that's done, then copy the full 0-72h +# track into the /com/hur/prod/global track archive file. + +if [ "$SENDCOM" = 'YES' ] +then + + glatuxarch=${glatuxarch:-${gltrkdir}/tracks.atcfunix.${syy}} + cat ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} >>${glatuxarch} + + if [ ${cmodel} = 'gfdl' ] + then + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} ${COMOUT}/${stormenv}.${PDY}${cyc}.trackeratcfunix + else + cp ${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} ${COMOUT}/${atcfout}.t${cyc}z.cyclone.trackatcfunix + cp ${TRKDATA}/long.${atcfout}.atcfunix.${PDY}${shh} ${COMOUT}/${atcfout}p.t${cyc}z.cyclone.trackatcfunix +# cat ${TRKDATA}/long.${atcfout}.atcfunix.${PDY}${shh} | \ +# sed s:GFSO:GFSP:g \ +# > ${COMOUT}/gfsp.t${cyc}z.cyclone.trackatcfunix + + fi + + if [ "$SENDDBN" = 'YES' ] + then + if [ ${cmodel} != 'gfdl' ] + then + $DBNROOT/bin/dbn_alert MODEL ENS_TRACKER $job ${COMOUT}/${atcfout}.t${cyc}z.cyclone.trackatcfunix + $DBNROOT/bin/dbn_alert MODEL ENS_TRACKER $job ${COMOUT}/${atcfout}p.t${cyc}z.cyclone.trackatcfunix + fi + fi + +# ------------------------------------------ +# Cat atcfunix files to storm trackers files +# ------------------------------------------ +# +# We need to parse apart the atcfunix file and distribute the forecasts to +# the necessary directories. To do this, first sort the atcfunix records +# by forecast hour (k6), then sort again by ocean basin (k1), storm number (k2) +# and then quadrant radii wind threshold (k12). Once you've got that organized +# file, break the file up by putting all the forecast records for each storm +# into a separate file. Then, for each file, find the corresponding atcfunix +# file in the storm trackers directory and dump the atcfunix records for that storm +# in there. NOTE: Only do this if the model run is NOT for the CMC or +# ECMWF ensemble. The reason is that we do NOT want to write out the individual +# member tracks to the atcfunix file. We only want to write out the ensemble +# mean track to the atcfunix file, and the mean track is calculated and written +# out in a separate script. + + if [ ${cmodel} = 'gfdl' ] + then + auxfile=${COMOUT}/${stormenv}.${PDY}${cyc}.trackeratcfunix + else + auxfile=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + fi + + sort -k6 ${auxfile} | sort -k1 -k2 -k12 >atcfunix.sorted + old_string="XX, XX" + + ict=0 + while read unixrec + do + storm_string=` echo "${unixrec}" | cut -c1-6` + if [ "${storm_string}" = "${old_string}" ] + then + echo "${unixrec}" >>atcfunix_file.${ict} + else + let ict=ict+1 + echo "${unixrec}" >atcfunix_file.${ict} + old_string="${storm_string}" + fi + done >$COMOUTatcf/${at}${NO}${syyyy}/ncep_a${at}${NO}${syyyy}.dat + if [ ${cmodel} = 'gfs' ]; then + cat atcfunix_file.$mct | sed s:AVNO:GFSO:g > gfso_atcfunix_file.$mct + cat gfso_atcfunix_file.$mct >>$COMOUTatcf/${at}${NO}${syyyy}/ncep_a${at}${NO}${syyyy}.dat + + cat atcfunix_file.$mct >>$COMOUTatcf/${at}${NO}${syyyy}/a${at}${NO}${syyyy}.dat + cat gfso_atcfunix_file.$mct >>$COMOUTatcf/${at}${NO}${syyyy}/a${at}${NO}${syyyy}.dat + fi + + set +x + echo " " + echo "+++ Adding records to TPC ATCFUNIX directory: $COMOUTatcf/${at}${NO}${syyyy}/ncep_${at}${NO}${syyyy}" + echo " " + set -x + done + fi +fi diff --git a/sorc/ens_tracker.v1.1.15.2/ush/extrkr_gfs.sh b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh_bak20200806 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/extrkr_gfs.sh rename to sorc/ens_tracker.v1.1.15.4/ush/extrkr_gfs.sh_bak20200806 diff --git a/sorc/ens_tracker.v1.1.15.2/ush/extrkr_tcv_g1.sh b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_g1.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/extrkr_tcv_g1.sh rename to sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_g1.sh diff --git a/sorc/ens_tracker.v1.1.15.2/ush/extrkr_tcv_g2.sh b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_g2.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/extrkr_tcv_g2.sh rename to sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_g2.sh diff --git a/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_g2.sh_10152019 b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_g2.sh_10152019 new file mode 100755 index 0000000000..0f0d7c20db --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_g2.sh_10152019 @@ -0,0 +1,1826 @@ +#!/bin/ksh +export PS4=' + extrkr_tcv_g2.sh line $LINENO: ' + +set +x +############################################################################## +echo " " +echo "------------------------------------------------" +echo "xxxx - Track vortices in model GRIB2 output" +echo "J.Peng---10-06-2014---------------------------- " +echo "Current time is: `date`" +echo " " +############################################################################## +set -x + +loopnum=$1 +cmodel=$2 +pert=$3 +TRKDATA=$4 + +export gribver=2 +#init_flag=$5 +# USE_OPER_VITALS=NO +# USE_OPER_VITALS=INIT_ONLY +USE_OPER_VITALS=YES + +############################################################################## +# +# FLOW OF CONTROL +# +# 1. Define data directories and file names for the input model +# 2. Process input starting date/cycle information +# 3. Update TC Vitals file and select storms to be processed +# 4. Cut apart input GRIB files to select only the needed parms and hours +# 5. Execute the tracker +# 6. Copy the output track files to various locations +# +############################################################################## +msg="has begun for ${cmodel} ${pert} at ${cyc}z" +postmsg "$jlogfile" "$msg" +######################################## + +# This script runs the hurricane tracker using operational GRIB model output. +# This script makes sure that the data files exist, it then pulls all of the +# needed data records out of the various GRIB forecast files and puts them +# into one, consolidated GRIB file, and then runs a program that reads the TC +# Vitals records for the input day and updates the TC Vitals (if necessary). +# It then runs gettrk, which actually does the tracking. +# +# Environmental variable inputs needed for this scripts: +# PDY -- The date for data being processed, in YYYYMMDD format +# cyc -- The numbers for the cycle for data being processed (00, 06, 12, 18) +# cmodel -- Model being processed (gfs, mrf, ukmet, ecmwf, nam, ngm, ngps, +# gdas, gfdl, ens (ncep ensemble)) +# envir -- 'prod' or 'test' +# SENDCOM -- 'YES' or 'NO' +# stormenv -- This is only needed by the tracker run for the GFDL model. +# 'stormenv' contains the name/id that is used in the input +# grib file names. +# pert -- This is only needed by the tracker run for the NCEP ensemble. +# 'pert' contains the ensemble member id (e.g., n2, p4, etc.) +# which is used as part of the grib file names. +# +# For testing script interactively in non-production set following vars: +# archsyndir - Directory with syndir scripts/exec/fix +# + +qid=$$ +#----------------------------------------------# +# Get input date information # +#----------------------------------------------# + +#export PDY=${PDY:-$1} +#export cyc=${cyc:-$2} +#export cmodel=${cmodel:-$3} +export jobid=${jobid:-testjob} +export SENDCOM=${SENDCOM:-NO} +export PHASEFLAG=y +export WCORE_DEPTH=1.0 +#export PHASE_SCHEME=vtt +#export PHASE_SCHEME=cps +export PHASE_SCHEME=both +export STRUCTFLAG=n +export IKEFLAG=n + +if [ ! -d $TRKDATA ] +then + mkdir -p $TRKDATA +fi +cd $TRKDATA + +if [ ${#PDY} -eq 0 -o ${#cyc} -eq 0 -o ${#cmodel} -eq 0 ] +then + set +x + echo " " + echo "FATAL ERROR: Something wrong with input parameters." + echo "PDY= ${PDY}, cyc= ${cyc}, cmodel= ${cmodel}" + set -x + err_exit "FAILED ${jobid} -- BAD INPUTS AT LINE $LINENO IN TRACKER SCRIPT - ABNORMAL EXIT" +else + set +x + echo " " + echo " #-----------------------------------------------------------------#" + echo " At beginning of tracker script, the following imported variables " + echo " are defined: " + echo " PDY ................................... $PDY" + echo " cyc ................................... $cyc" + echo " cmodel ................................ $cmodel" + echo " jobid ................................. $jobid" + echo " envir ................................. $envir" + echo " SENDCOM ............................... $SENDCOM" + echo " " + set -x +fi + +scc=`echo ${PDY} | cut -c1-2` +syy=`echo ${PDY} | cut -c3-4` +smm=`echo ${PDY} | cut -c5-6` +sdd=`echo ${PDY} | cut -c7-8` +shh=${cyc} +symd=`echo ${PDY} | cut -c3-8` +syyyy=`echo ${PDY} | cut -c1-4` +CENT=`echo ${PDY} | cut -c1-2` + +#export archsyndir=${archsyndir:-${COMROOT}/arch/prod/syndat} +archsyndir=${archsyndir:-${COMINsyn:?}} + +#----------------------------------------------------------------# +# +# --- Define data directories and data file names --- +# +# Convert the input model to lowercase letters and check to see +# if it's a valid model, and assign a model ID number to it. +# This model ID number is passed into the Fortran program to +# let the program know what set of forecast hours to use in the +# ifhours array. Also, set the directories for the operational +# input GRIB data files and create templates for the file names. +# While only 1 of these sets of directories and file name +# templates is used during a particular run of this script, +# "gfsvitdir" is used every time, because that is the directory +# that contains the error-checked TC vitals file that Steve Lord +# produces, and so it is included after the case statement. +# +# NOTE: The varible PDY is now defined within the J-Jobs that +# call this script. Therefore there is no reason to do this +# here. +# +# NOTE: The script that processes the ECMWF data defines PDY as +# the current day, and in this script we need PDY to be +# yesterday's date (for the ecmwf ONLY). So instead, the ecmwf +# script will pass the variable PDYm1 to this script, and in the +# case statement below we change that to PDY. +# +# NOTE: Do NOT try to standardize this script by changing all of +# these various data directories' variable names to be all the +# same, such as "datadir". As you'll see in the data cutting +# part of this script below, different methods are used to cut +# apart different models, thus it is important to know the +# difference between them.... +#----------------------------------------------------------------# + +cmodel=`echo ${cmodel} | tr "[A-Z]" "[a-z]"` + +case ${cmodel} in + + gfs) set +x; echo " " ; + echo " ++ operational GFS chosen" ; + echo " "; set -x ; +# gfsdir=${gfsdir:-${COMROOT}/gfs/prod/gfs.${PDY}} ; + gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}} ; + gfsgfile=gfs.t${cyc}z.pgrb2.0p25.f ; + vit_incr=6 + fcstlen=6 ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + atcfnum=15 ; + atcfname="gfso" ; + atcfout="gfso" ; + atcffreq=600 ; + + rundescr="xxxx" ; + atcfdescr="xxxx" ; + + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + modtyp='global' ; + + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=0 ; + model=1 ;; + +# g2_jpdtn sets the variable that will be used as "JPDTN" for +# the call to getgb2, if gribver=2. jpdtn=1 for ens data, +# jpdtn=0 for deterministic data. + + ngps) set +x; echo " " ; + echo " ++ operational NAVGEM chosen" ; + echo " "; set -x ; +# ngpsdir=${ngpsdir:-${COMROOTp1}/fnmoc/prod/navgem.${PDY}}; + ngpsdir=${ngpsdir:-${DCOM:?}} ; +#J.Peng-05-12-2016 ngpsgfile=navgem_${PDY}${cyc}f ; +# ngemgfile=.grib2 ; + ngpsgfile=US058GMET-OPSbd2.NAVGEM ; + ngemgfile=-${PDY}${cyc}-NOAA-halfdeg.gr2 ; + vit_incr=6 + fcstlen=6 ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + atcfnum=29 ; + atcfname="ngx " ; + atcfout="ngx" ; + atcffreq=600 ; + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + modtyp='global' ; + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=0 ; + model=7 ;; + + cmc) set +x; echo " " ; + echo " ++ operational CMC chosen" ; + echo " "; set -x ; + cmcdir=${cmcdir:-${DCOM:?}} ; +# cmcdir=/gpfs/hps/emc/ensemble/noscrub/Jiayi.Peng/cmc_25km_data ; + cmcgfile=CMC_glb_latlon.24x.24_${PDY}${cyc}_P ; + cmcgfile2=_NCEP.grib2 ; + + vit_incr=6 + fcstlen=6 ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + atcfnum=39 ; + atcfname="cmc " ; + atcfout="cmc" ; + atcffreq=600 ; + mslpthresh=0.0015 ; + v850thresh=1.5000 ; + modtyp='global' ; + file_sequence="onebig" ; + lead_time_units='hours' ; + g2_jpdtn=0 ; + model=15 ;; + + *) msg="Model $cmodel is not recognized." ; + echo "$msg"; postmsg $jlogfile "$msg" ; + err_exit "FAILED ${jobid} -- UNKNOWN cmodel IN TRACKER SCRIPT - ABNORMAL EXIT";; + +esac + +if [ ${PHASEFLAG} = 'y' ]; then + +#J.Peng----2014-10-28--------- + if [ ${cmodel} = "gfs" ]; then + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 UGRD:200 VGRD:850 VGRD:700 VGRD:500 VGRD:200 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET HGT:900 HGT:850 HGT:800 HGT:750 HGT:700 HGT:650 HGT:600 HGT:550 HGT:500 HGT:450 HGT:400 HGT:350 HGT:300 TMP:500 TMP:450 TMP:400 TMP:350 TMP:300 RH:500" + elif [ ${cmodel} = "ngps" ]; then + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 UGRD:200 VGRD:850 VGRD:700 VGRD:500 VGRD:200 SurfaceU SurfaceV PRMSL HGT:925 HGT:850 HGT:700 HGT:500 HGT:400 HGT:300 TMP:500 TMP:400 TMP:300 RH:500" + elif [ ${cmodel} = "cmc" ]; then + wgrib_parmlist="UGRD:850 UGRD:700 UGRD:500 UGRD:200 VGRD:850 VGRD:700 VGRD:500 VGRD:200 SurfaceU SurfaceV ABSV:850 ABSV:700 PRMSL HGT:925 HGT:850 HGT:700 HGT:500 HGT:250 TMP:500 TMP:250 SPFH:500" + fi + + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 U:200 V:850 V:700 V:500 V:200 10U:sfc 10V:sfc MSL:sfc GH:300 GH:400 GH:500 GH:925 T:300 T:400 T:500 R:500" +else + + wgrib_parmlist=" HGT:850 HGT:700 UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET " + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc" + +fi + +#---------------------------------------------------------------# +# +# -------- TC Vitals processing -------- +# +# Check Steve Lord's operational tcvitals file to see if any +# vitals records were processed for this time by his system. +# If there were, then you'll find a file in /com/gfs/prod/gfs.yymmdd +# with the vitals in it. Also check the raw TC Vitals file in +# /com/arch/prod/syndat , since this may contain storms that Steve's +# system ignored (Steve's system will ignore all storms that are +# either over land or very close to land); We still want to track +# these inland storms, AS LONG AS THEY ARE NHC STORMS (don't +# bother trying to track inland storms that are outside of NHC's +# domain of responsibility -- we don't need that info). +# UPDATE 5/12/98 MARCHOK: The script is updated so that for the +# global models, the gfs directory is checked for the error- +# checked vitals file, while for the regional models, the +# nam directory is checked for that file. +# UPDATE 3/27/09 MARCHOK: The SREF is run at off-synoptic times +# (03,09,15,21Z). There are no tcvitals issued at these offtimes, +# so the updating of the "old" tcvitals is critical for running +# the tracker on SREF. For updating the old tcvitals for SREF, +# we need to look 3h back, not 6h as for the other models that +# run at synoptic times. +#--------------------------------------------------------------# + +old_ymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +old_4ymd=` echo ${old_ymdh} | cut -c1-8` +old_ymd=` echo ${old_ymdh} | cut -c3-8` +old_hh=` echo ${old_ymdh} | cut -c9-10` +old_str="${old_ymd} ${old_hh}00" + +future_ymdh=` ${NDATE:?} ${vit_incr} ${PDY}${cyc}` +future_4ymd=` echo ${future_ymdh} | cut -c1-8` +future_ymd=` echo ${future_ymdh} | cut -c3-8` +future_hh=` echo ${future_ymdh} | cut -c9-10` +future_str="${future_ymd} ${future_hh}00" + +if [ ${modtyp} = 'global' ] +then +# synvitdir=${COMROOT}/gfs/prod/gfs.${PDY} + synvitdir=${COMINgfs:?}/${cyc} + synvitfile=gfs.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/gfs/prod/gfs.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd}/${old_hh} + synvitold_file=gfs.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/gfs/prod/gfs.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd}/${future_hh} + synvitfuture_file=gfs.t${future_hh}z.syndata.tcvitals.tm00 +else +# synvitdir=${COMROOT}/nam/prod/nam.${PDY} + synvitdir=${COMINnam:?} + synvitfile=nam.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/nam/prod/nam.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=nam.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/nam/prod/nam.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=nam.t${future_hh}z.syndata.tcvitals.tm00 +fi + +set +x +echo " " +echo " -----------------------------" +echo " " +echo " Now sorting and updating the TC Vitals file. Please wait...." +echo " " +set -x + +current_str="${symd} ${cyc}00" + +if [ -s ${synvitdir}/${synvitfile} -o\ + -s ${synvitold_dir}/${synvitold_file} -o\ + -s ${synvitfuture_dir}/${synvitfuture_file} ] +then + grep "${old_str}" ${synvitold_dir}/${synvitold_file} \ + >${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${synvitdir}/${synvitfile} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${future_str}" ${synvitfuture_dir}/${synvitfuture_file} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} +else + set +x + echo " " + echo " There is no (synthetic) TC vitals file for ${cyc}z in ${synvitdir}," + echo " nor is there a TC vitals file for ${old_hh}z in ${synvitold_dir}." + echo " nor is there a TC vitals file for ${future_hh}z in ${synvitfuture_dir}," + echo " Checking the raw TC Vitals file ....." + echo " " + set -x +fi + +# Take the vitals from Steve Lord's /com/gfs/prod tcvitals file, +# and cat them with the NHC-only vitals from the raw, original +# /com/arch/prod/synda_tcvitals file. Do this because the nwprod +# tcvitals file is the original tcvitals file, and Steve runs a +# program that ignores the vitals for a storm that's over land or +# even just too close to land, and for tracking purposes for the +# US regional models, we need these locations. Only include these +# "inland" storm vitals for NHC (we're not going to track inland +# storms that are outside of NHC's domain of responsibility -- we +# don't need that info). +# UPDATE 5/12/98 MARCHOK: awk logic is added to screen NHC +# vitals such as "91L NAMELESS" or "89E NAMELESS", since TPC +# does not want tracks for such storms. + +grep "${old_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${current_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${future_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-03---for genesis tc vitals-------------------- +#export COMINgenvit=/global/save/wx20jp/genesis_vital_${syyyy} +#grep "${current_str}" ${COMINgenvit}/gen_tc.vitals.gfs.gfso.2012 | \ +# grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ +# >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +#PRODTEST# +#PRODTEST# Use the next couple lines to test the tracker on the SP. +#PRODTEST# These next couple lines use data from a test TC Vitals file that +#PRODTEST# I generate. When you are ready to test this system, call me and +#PRODTEST# I'll create one for the current day, and then uncomment the next +#PRODTEST# couple lines in order to access the test vitals file. +# +#ttrkdir=/global/save/wx20tm/trak/prod/data +#ttrkdir=/global/save/wx20tm/trak/para/scripts +#grep "${current_str}" ${ttrkdir}/tcvit.01l >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + + +# IMPORTANT: When "cat-ing" these files, make sure that the vitals +# files from the "raw" TC vitals files are first in order and Steve's +# TC vitals files second. This is because Steve's vitals file has +# been error-checked, so if we have a duplicate tc vitals record in +# these 2 files (very likely), program supvit.x below will +# only take the last vitals record listed for a particular storm in +# the vitals file (all previous duplicates are ignored, and Steve's +# error-checked vitals records are kept). + +cat ${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} ${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} \ + >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# If we are doing the processing for the GFDL model, then we want +# to further cut down on which vitals we allow into this run of the +# tracker. The reason is that this program will be called from +# each individual run for a storm, so the grib files will be +# specific to each storm. So if 4 storms are being run at a +# particular cycle, then this script is run 4 separate times from +# within the GFDL_POST job. + +#if [ ${cmodel} = 'gfdl' ]; then +## grep -i ${stormid} ${COMIN}/${ATCFNAME}.vitals.${syy}${smm}${sdd}${shh} >${TRKDATA}/tmpvit +## mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#echo " " +#echo "!!! ERROR: TAKE THE COMMENTS OUT OF HERE BEFORE IMPLEMENTATION AAAA" +#echo "!!! AND TAKE OUT THESE NEXT 2 LINES AS WELL............................" +#echo " " +#grep -i ${stormid} ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/tmpvit +#mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#fi + +#--------------------------------------------------------------# +# Now run a fortran program that will read all the TC vitals +# records for the current dtg and the dtg from 6h ago, and +# sort out any duplicates. If the program finds a storm that +# was included in the vitals file 6h ago but not for the current +# dtg, this program updates the 6h-old first guess position +# and puts these updated records as well as the records from +# the current dtg into a temporary vitals file. It is this +# temporary vitals file that is then used as the input for the +# tracking program. +#--------------------------------------------------------------# + +oldymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +oldyy=`echo ${oldymdh} | cut -c3-4` +oldmm=`echo ${oldymdh} | cut -c5-6` +olddd=`echo ${oldymdh} | cut -c7-8` +oldhh=`echo ${oldymdh} | cut -c9-10` +oldymd=${oldyy}${oldmm}${olddd} + +futureymdh=` ${NDATE:?} 6 ${PDY}${cyc}` +futureyy=`echo ${futureymdh} | cut -c3-4` +futuremm=`echo ${futureymdh} | cut -c5-6` +futuredd=`echo ${futureymdh} | cut -c7-8` +futurehh=`echo ${futureymdh} | cut -c9-10` +futureymd=${futureyy}${futuremm}${futuredd} + +echo "&datenowin dnow%yy=${syy}, dnow%mm=${smm}," >${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&dateoldin dold%yy=${oldyy}, dold%mm=${oldmm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dold%dd=${olddd}, dold%hh=${oldhh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&datefuturein dfuture%yy=${futureyy}, dfuture%mm=${futuremm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dfuture%dd=${futuredd}, dfuture%hh=${futurehh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&hourinfo vit_hr_incr=${vit_incr}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period." + echo "!!! File ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} is empty." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi + +fi + +# For tcgen cases, filter to use only vitals from the ocean +# basin of interest.... +#J.Peng----2012-04-13------------------------------------- +#if [ ${trkrtype} = 'tcgen' ] +# then + +# if [ ${numvitrecs} -gt 0 ] +# then + +# fullvitfile=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# cp $fullvitfile ${TRKDATA}/vitals.all_basins.${atcfout}.${PDY}${cyc} +# basin=` echo $regtype | cut -c1-2` + +# if [ ${basin} = 'al' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "L") print $0}' | +# >${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'ep' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "E") print $0}' | +# >${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'wp' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "W") print $0}' | +# >${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi + +# cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# fi + +#fi +#J.Peng----2012-04-13------------------------------------- + +# - - - - - - - - - - - - - +# Before running the program to read, sort and update the vitals, +# first run the vitals through some awk logic, the purpose of +# which is to convert all the 2-digit years into 4-digit years. +# Beginning 4/21/99, NHC and JTWC will begin sending the vitals +# with 4-digit years, however it is unknown when other global +# forecasting centers will begin using 4-digit years, thus we +# need the following logic to ensure that all the vitals going +# into supvit.f have uniform, 4-digit years in their records. +# +# 1/8/2000: sed code added by Tim Marchok due to the fact that +# some of the vitals were getting past the syndata/qctropcy +# error-checking with a colon in them; the colon appeared +# in the character immediately to the left of the date, which +# was messing up the "(length($4) == 8)" statement logic. +# - - - - - - - - - - - - - + +sed -e "s/\:/ /g" ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} > ${TRKDATA}/tempvit +mv ${TRKDATA}/tempvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +awk ' +{ + yycheck = substr($0,20,2) + if ((yycheck == 20 || yycheck == 19) && (length($4) == 8)) { + printf ("%s\n",$0) + } + else { + if (yycheck >= 0 && yycheck <= 50) { + printf ("%s20%s\n",substr($0,1,19),substr($0,20)) + } + else { + printf ("%s19%s\n",substr($0,1,19),substr($0,20)) + } + } +} ' ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 + +mv ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +#Added by J.Peng----2012-04-12---------------------------------------------- +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +#----------------------------------------------------------------- +#J.Peng----2012-05-04--do not need this for TCvitals---- +#numvitrecs=0 + +if [ ${numvitrecs} -gt 0 ] +then + + export pgm=supvit_g2 + . prep_step + + # Input file + export FORT31=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_g2 <${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + suvrcc=$? + + if [ ${suvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit.x, " + echo "!!! which is the program that updates the TC Vitals file." + echo "!!! Return code from supvit.x = ${suvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING supvit_g2 IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else +#Added by J.Peng----2012-04-12---------------------------------------------- + rm -f ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + touch ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#----------------------------------------------------------------- +# In this section, check to see if the user requested the use of +# operational TC vitals records for the initial time only. This +# option might be used for a retrospective medium range forecast +# in which the user wants to initialize with the storms that are +# currently there, but then let the model do its own thing for +# the next 10 or 14 days.... + + +if [ ${USE_OPER_VITALS} = 'INIT_ONLY' ]; then + + if [ ${init_flag} = 'yes' ]; then + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are currently at." + echo " " + set -x + else + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are now *PAST*." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + fi + +elif [ ${USE_OPER_VITALS} = 'NO' ]; then + + set +x + echo " " + echo "NOTE: User has requested that historical vitals not be used...." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#------------------------------------------------------------------# +# Now select all storms to be processed, that is, process every +# storm that's listed in the updated vitals file for the current +# forecast hour. If there are no storms for the current time, +# then exit. +#------------------------------------------------------------------# + +numvitrecs=`cat ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period " + echo "!!! in the UPDATED vitals file." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi +fi + +set +x +echo " " +echo " *--------------------------------*" +echo " | STORM SELECTION |" +echo " *--------------------------------*" +echo " " +set -x + +ict=1 +while [ $ict -le 15 ] +do + stormflag[${ict}]=3 + let ict=ict+1 +done + +dtg_current="${symd} ${cyc}00" +stormmax=` grep "${dtg_current}" ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +if [ ${stormmax} -gt 15 ] +then + stormmax=15 +fi + +sct=1 +while [ ${sct} -le ${stormmax} ] +do + stormflag[${sct}]=1 + let sct=sct+1 +done + + +#---------------------------------------------------------------# +# +# -------- "Genesis" Vitals processing -------- +# +# May 2006: This entire genesis tracking system is being +# upgraded to more comprehensively track and categorize storms. +# One thing that has been missing from the tracking system is +# the ability to keep track of storms from one analysis cycle +# to the next. That is, the current system has been very +# effective at tracking systems within a forecast, but we have +# no methods in place for keeping track of storms across +# difference initial times. For example, if we are running +# the tracker on today's 00z GFS analysis, we will get a +# position for various storms at the analysis time. But then +# if we go ahead and run again at 06z, we have no way of +# telling the tracker that we know about the 00z position of +# this storm. We now address that problem by creating +# "genesis" vitals, that is, when a storm is found at an +# analysis time, we not only produce "atcfunix" output to +# detail the track & intensity of a found storm, but we also +# produce a vitals record that will be used for the next +# run of the tracker script. These "genesis vitals" records +# will be of the format: +# +# YYYYMMDDHH_AAAH_LLLLX_TYP +# +# Where: +# +# YYYYMMDDHH = Date the storm was FIRST identified +# by the tracker. +# AAA = Abs(Latitude) * 10; integer value +# H = 'N' for norther hem, 'S' for southern hem +# LLLL = Abs(Longitude) * 10; integer value +# X = 'E' for eastern hem, 'W' for western hem +# TYP = Tropical cyclone storm id if this is a +# tropical cyclone (e.g., "12L", or "09W", etc). +# If this is one that the tracker instead "Found +# On the Fly (FOF)", we simply put those three +# "FOF" characters in there. +#J.Peng----2012-04-12-------- +genvitfile=${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} +touch ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + +if [ -f $genvitfile ]; then + d6ago_ymdh=` ${NDATE:?} -6 ${PDY}${cyc}` + d6ago_4ymd=` echo ${d6ago_ymdh} | cut -c1-8` + d6ago_ymd=` echo ${d6ago_ymdh} | cut -c3-8` + d6ago_hh=` echo ${d6ago_ymdh} | cut -c9-10` + d6ago_str="${d6ago_ymd} ${d6ago_hh}00" + + d6ahead_ymdh=` ${NDATE:?} 6 ${PDY}${cyc}` + d6ahead_4ymd=` echo ${d6ahead_ymdh} | cut -c1-8` + d6ahead_ymd=` echo ${d6ahead_ymdh} | cut -c3-8` + d6ahead_hh=` echo ${d6ahead_ymdh} | cut -c9-10` + d6ahead_str="${d6ahead_ymd} ${d6ahead_hh}00" + + set +x + echo " " + echo " d6ago_str= --->${d6ago_str}<---" + echo " current_str= --->${current_str}<---" + echo " d6ahead_str= --->${d6ahead_str}<---" + echo " " + #echo " Listing and contents of ${genvitfile} follow " + #echo " for the times 6h ago, current and 6h ahead:" + #echo " " + set -x + + #J.Peng----2012-04-11----- + ##ls -la ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + ##cat ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + #ls -la ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #cat ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + + grep "${d6ago_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${d6ahead_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + num_gen_vits=`cat ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} | wc -l` +else + echo "WARNING: Genesis Vitals from previous run(s) not found!" +fi + +echo "&datenowin dnow%yy=${syyyy}, dnow%mm=${smm}," >${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6agoin d6ago%yy=${syyyy6}, d6ago%mm=${smm6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ago%dd=${sdd6}, d6ago%hh=${shh6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6aheadin d6ahead%yy=${syyyyp6}, d6ahead%mm=${smmp6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ahead%dd=${sddp6}, d6ahead%hh=${shhp6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-04--- need this for TC vitals------------------ +num_gen_vits=0 + +if [ ${num_gen_vits} -gt 0 ] +then + export pgm=supvit_gen + . prep_step + + # Input file + export FORT31=${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_gen <${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + sgvrcc=$? + + if [ ${sgvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit_gen, " + echo "!!! which is the program that updates the genesis vitals file." + echo "!!! Return code from supvit_gen = ${sgvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING SUPVIT_GEN IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + rm -f ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + touch ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +fi + + +#-----------------------------------------------------------------# +# +# ------ CUT APART INPUT GRIB FILES ------- +# +# For the selected model, cut apart the GRIB input files in order +# to pull out only the variables that we need for the tracker. +# Put these selected variables from all forecast hours into 1 big +# GRIB file that we'll use as input for the tracker. +# +# The wgrib utility (/nwprod/util/exec/wgrib) is used to cut out +# the needed parms for the GFS, MRF, GDAS, UKMET and NOGAPS files. +# The utility /nwprod/util/exec/copygb is used to interpolate the +# NGM (polar stereographic) and NAM (Lambert Conformal) data from +# their grids onto lat/lon grids. Note that while the lat/lon +# grid that I specify overlaps into areas that don't have any data +# on the original grid, Mark Iredell wrote the copygb software so +# that it will mask such "no-data" points with a bitmap (just be +# sure to check the lbms in your fortran program after getgb). +#-----------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------" +echo " NOW CUTTING APART INPUT GRIB FILES TO " +echo " CREATE 1 BIG GRIB INPUT FILE " +echo " -----------------------------------------" +echo " " +set -x + +regflag=`grep NHC ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +# ------------------------------ +# Process GFS, if selected +# ------------------------------ + +if [ ${model} -eq 1 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gfsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc} + fi + + rm -f ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f* + rm -f ${TRKDATA}/gfsgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gfsgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gfs wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gfsdir}/${gfsgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GFS File missing: ${gfsdir}/${gfsgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING GFS FILE IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${gfsdir}/${gfsgfile}${fhour} + ${WGRIB2:?} -s $gfile >gfs.ix + export err=$?; err_chk + + for parm in ${wgrib_parmlist} + do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m " gfs.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} ;; + "SurfaceV") + grep "VGRD:10 m " gfs.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} ;; + *) + grep "${parm}" gfs.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} ;; + esac + done + + gfs_master_file=${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + gfs_cat_file=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfs_master_file} ${gfs_master_file}.ix + export err=$?; err_chk + + g1=${gfs_master_file} + x1=${gfs_master_file}.ix + cat ${gfs_master_file} >>${gfs_cat_file} + done + + ${GRB2INDEX:?} ${TRKDATA}/gfsgribfile.${PDY}${cyc} ${TRKDATA}/gfsixfile.${PDY}${cyc} + export err=$?; err_chk + +# Now run through the work to get data for phase checking.... +# -------------------------------------------- + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + + # This next part is commented out for the GFS since we already + # have the necessary GFS height and temperature data at + # 50-mb intervals. However, we then still need to average the + # temperature data to get the 300-500 mb mean temperature, and + # that code is still un-commented below. + +# gparm=7 +# namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z +# . prep_step +# echo "&timein ifcsthour=${fhour}," >${namelist} +# echo " iparm=${gparm}," >>${namelist} +# echo " gribver=${gribver}," >>${namelist} +# echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} +# echo " g2_model=${model}/" >>${namelist} +# ln -s -f ${gfile} fort.11 +# ln -s -f ${FIXens_tracker}/gfs_hgt_levs.txt fort.16 +# ln -s -f ${ifile} fort.31 +# ln -s -f ${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} fort.51 +# ${EXECens_tracker}/vint_g2.x <${namelist} +# rcc=$? + +# gparm=11 +# namelist=${TRKDATA}/vint_input.${PDY}${cyc}.t +# . prep_step +# echo "&timein ifcsthour=${fhour}," >${namelist} +# echo " iparm=${gparm}," >>${namelist} +# echo " gribver=${gribver}," >>${namelist} +# echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} +# echo " g2_model=${model}/" >>${namelist} +# ln -s -f ${gfile} fort.11 +# ln -s -f ${FIXens_tracker}/gfs_tmp_levs.txt fort.16 +# ln -s -f ${ifile} fort.31 +# ln -s -f ${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} fort.51 +# ${EXECens_tracker}/vint_g2.x <${namelist} +# rcc=$? + + gparm=11 +# ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ffile=${gfile} + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${tavefile} +# export FORT92=${TRKDATA}/${cmodel}.tave92.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + +# zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} +# cat ${zfile} ${tavefile} >>${catfile} + cat ${tavefile} >>${catfile} + +#J.Peng----2012-04-18---added for U-shear----- + gparm=33 + ffile=${gfile} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-04-18---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + + done + + cat ${catfile} >>${gfile} + + fi + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gfsixfile.${PDY}${cyc} + +fi + +# ------------------------------ +# Process NAVGEM, if selected +# ------------------------------ + +if [ ${model} -eq 7 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} ] + then + set +x + echo " " + echo "FATAL ERROR: NAVGEM File missing ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" >> ${DATA}/missing_ngps.txt + exit + else + err_exit "MISSING NAVGEM FILE at $0:$LINENO" + fi + fi + + gfile=${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} + ${WGRIB2:?} -s $gfile >ngps.ix + export err=$?; err_chk + + for parm in ${wgrib_parmlist} + do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m above" ngps.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ;; + + "SurfaceV") + grep "VGRD:10 m above" ngps.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ;; + *) + grep "${parm}" ngps.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ;; + esac + done + done + +# Now run through the work to get data for phase checking.... + ${GRB2INDEX:?} ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ${TRKDATA}/ngpsixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for GPH AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for T AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk +fi + +# ------------------------------ +# Process CMC-deterministic forecast, if selected +# ------------------------------ + +if [ ${model} -eq 15 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/cmcgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/cmcgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} ] + then + set +x + echo " " + echo "FATAL ERROR: CMC File missing ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + fi + + gfile=${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} + ${WGRIB2:?} -s $gfile >cmc.ix + export err=$?; err_chk + + for parm in ${wgrib_parmlist} + do + case ${parm} in + "SurfaceU") + grep "UGRD:10 m above" cmc.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cmcgribfile.${PDY}${cyc} ;; + + "SurfaceV") + grep "VGRD:10 m above" cmc.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cmcgribfile.${PDY}${cyc} ;; + *) + grep "${parm}" cmc.ix | ${WGRIB2:?} -i $gfile -append -grib \ + ${TRKDATA}/cmcgribfile.${PDY}${cyc} ;; + esac + done + done + +# J.Peng--04-05-2017--changing grid from 180E to 0E------------ +# wgrib2 junk -small_grib 0:359.76 -90:90 junk2 +# cp ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/junk +# rm -f ${TRKDATA}/cmcgribfile.${PDY}${cyc} +# ${WGRIB2} ${TRKDATA}/junk -small_grib 0:359.76 -90:90 ${TRKDATA}/junk2 +# cp ${TRKDATA}/junk2 ${TRKDATA}/cmcgribfile.${PDY}${cyc} + +# Now run through the work to get data for phase checking.... + ${GRB2INDEX:?} ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/cmcixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} $gfile $ifile + export err=$?; err_chk + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for GPH AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for T AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- +# +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + gparmq=7 + gparmt=11 + + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/rh_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparmq=${gparmq}," >>${namelist} + echo " iparmt=${gparmt}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT16=${FIXens_tracker}/cmc_rh_levs.txt + + # Output file + export FORT51=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/rhum_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to rhum_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -rhum_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + rhfile=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + cat ${rhfile} >>${catfile} +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk +fi + +#------------------------------------------------------------------------# +# Now run the tracker # +#------------------------------------------------------------------------# +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW EXECUTING TRACKER......" +echo " -----------------------------------------------" +echo " " +set -x + +ist=1 +while [ $ist -le 15 ] +do + if [ ${stormflag[${ist}]} -ne 1 ] + then + set +x; echo "Storm number $ist NOT selected for processing"; set -x + else + set +x; echo "Storm number $ist IS selected for processing...."; set -x + fi + let ist=ist+1 +done + +if [ ! -s ${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} ] +then + for fhr in $fcsthrs; do + if [ $fhr -ne 99 ]; then + last_fcst_hour=$fhr + fi + done + echo ${last_fcst_hour} >${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} +fi + +namelist=${TRKDATA}/input.${atcfout}.${PDY}${cyc} +ATCFNAME=` echo "${atcfname}" | tr '[a-z]' '[A-Z]'` + +export atcfymdh=${scc}${syy}${smm}${sdd}${shh} + +contour_interval=100.0 +write_vit=y +want_oci=.TRUE. + +echo "&datein inp%bcc=${scc},inp%byy=${syy},inp%bmm=${smm}," >${namelist} +echo " inp%bdd=${sdd},inp%bhh=${shh},inp%model=${model}," >>${namelist} +echo " inp%modtyp='${modtyp}'," >>${namelist} +echo " inp%lt_units='${lead_time_units}'," >>${namelist} +echo " inp%file_seq='${file_sequence}'," >>${namelist} +echo " inp%nesttyp='${nest_type}'/" >>${namelist} +echo "&atcfinfo atcfnum=${atcfnum},atcfname='${ATCFNAME}'," >>${namelist} +echo " atcfymdh=${atcfymdh},atcffreq=${atcffreq}/" >>${namelist} +echo "&trackerinfo trkrinfo%westbd=${trkrwbd}," >>${namelist} +echo " trkrinfo%eastbd=${trkrebd}," >>${namelist} +echo " trkrinfo%northbd=${trkrnbd}," >>${namelist} +echo " trkrinfo%southbd=${trkrsbd}," >>${namelist} +echo " trkrinfo%type='${trkrtype}'," >>${namelist} +echo " trkrinfo%mslpthresh=${mslpthresh}," >>${namelist} +echo " trkrinfo%v850thresh=${v850thresh}," >>${namelist} +echo " trkrinfo%gridtype='${modtyp}'," >>${namelist} +echo " trkrinfo%contint=${contour_interval}," >>${namelist} +echo " trkrinfo%want_oci=${want_oci}," >>${namelist} +echo " trkrinfo%out_vit='${write_vit}'," >>${namelist} +echo " trkrinfo%gribver=${gribver}," >>${namelist} +echo " trkrinfo%g2_jpdtn=${g2_jpdtn}/" >>${namelist} +echo "&phaseinfo phaseflag='${PHASEFLAG}'," >>${namelist} +echo " phasescheme='${PHASE_SCHEME}'," >>${namelist} +echo " wcore_depth=${WCORE_DEPTH}/" >>${namelist} +echo "&structinfo structflag='${STRUCTFLAG}'," >>${namelist} +echo " ikeflag='${IKEFLAG}'/" >>${namelist} +echo "&fnameinfo gmodname='${atcfname}'," >>${namelist} +echo " rundescr='${rundescr}'," >>${namelist} +echo " atcfdescr='${atcfdescr}'/" >>${namelist} +echo "&verbose verb=3/" >>${namelist} +echo "&waitinfo use_waitfor='n'," >>${namelist} +echo " wait_min_age=10," >>${namelist} +echo " wait_min_size=100," >>${namelist} +echo " wait_max_wait=1800," >>${namelist} +echo " wait_sleeptime=5," >>${namelist} +echo " per_fcst_command=''/" >>${namelist} + +export pgm=gettrk_gen_g2 +. prep_step + +export FORT11=${gribfile} +export FORT12=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} +export FORT14=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +export FORT15=${FIXens_tracker}/${cmodel}.genesis_leadtimes +export FORT31=${ixfile} + +if [ ${trkrtype} = 'tracker' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT61=${TRKDATA}/trak.${atcfout}.all.${stormenv}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${stormenv}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${stormenv}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${stormenv}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${stormenv}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${stormenv}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${stormenv}.${PDY}${cyc} + else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${PDY}${cyc} + fi +else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${regtype}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${regtype}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${regtype}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${regtype}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${regtype}.${PDY}${cyc} +fi + +if [ ${atcfname} = 'aear' ] +then + export FORT65=${TRKDATA}/trak.${atcfout}.initvitl.${PDY}${cyc} +fi + +if [ ${write_vit} = 'y' ] +then + export FORT67=${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} +fi + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${stormenv}.${PDY}${cyc} + else + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${PDY}${cyc} + fi +fi + +if [ ${STRUCTFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${stormenv}.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${stormenv}.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${stormenv}.${PDY}${cyc} + else + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${PDY}${cyc} + fi +fi + +if [ ${IKEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${stormenv}.${PDY}${cyc} + else + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${PDY}${cyc} + fi +fi + +if [ ${trkrtype} = 'midlat' -o ${trkrtype} = 'tcgen' ]; then + export FORT77=${TRKDATA}/trkrmask.${atcfout}.${regtype}.${PDY}${cyc} +fi + +msg="$pgm start for $atcfout at ${cyc}z" +postmsg "$jlogfile" "$msg" + +set +x +echo "+++ TIMING: BEFORE gettrk ---> `date`" +set -x + +ulimit -c unlimited + +${EXECens_tracker}/gettrk_gen_g2 <${namelist} +gettrk_rcc=$? + +set +x +echo "+++ TIMING: AFTER gettrk ---> `date`" +set -x + +#--------------------------------------------------------------# +# Now copy the output track files to different directories +#--------------------------------------------------------------# + +if [ ${gettrk_rcc} -eq 0 ]; then + set +x + echo " " + echo " -----------------------------------------------" + echo " NOW COPYING OUTPUT TRACK FILES TO COM " + echo " -----------------------------------------------" + echo " " + set -x + + if [ -s ${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} ]; then + if [ "$SENDCOM" = 'YES' ]; then + cp ${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} ${COMOUTgenvit:?}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #if [ "$SENDDBN" = 'YES' ]; then + # $DBNROOT/bin/dbn_alert ${cmodel^^} GENESIS $job ${COMOUTgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #fi + fi + fi + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" +else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running gettrk_gen_g2, " + echo "!!! which is the program that actually gets the track." + echo "!!! Return code from gettrk_gen_g2 = ${gettrk_rcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING gettrk_gen_g2 IN TRACKER SCRIPT- ABNORMAL EXIT" +fi diff --git a/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_gfs.sh b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_gfs.sh new file mode 100755 index 0000000000..6345db2ca8 --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_gfs.sh @@ -0,0 +1,1934 @@ +#!/bin/ksh +export PS4=' + extrkr_tcv_gfs.sh line $LINENO: ' + +set +x +############################################################################## +echo " " +echo "------------------------------------------------" +echo "xxxx - Track vortices in model GRIB2 output" +echo "J.Peng---10-06-2014---------------------------- " +echo "Current time is: `date`" +echo " " +############################################################################## +set -x + +loopnum=$1 +cmodel=$2 +pert=$3 +TRKDATA=$4 + +export gribver=2 +#init_flag=$5 +# USE_OPER_VITALS=NO +# USE_OPER_VITALS=INIT_ONLY +USE_OPER_VITALS=YES + +############################################################################## +# +# FLOW OF CONTROL +# +# 1. Define data directories and file names for the input model +# 2. Process input starting date/cycle information +# 3. Update TC Vitals file and select storms to be processed +# 4. Cut apart input GRIB files to select only the needed parms and hours +# 5. Execute the tracker +# 6. Copy the output track files to various locations +# +############################################################################## +msg="has begun for ${cmodel} ${pert} at ${cyc}z" +postmsg "$jlogfile" "$msg" +######################################## + +# This script runs the hurricane tracker using operational GRIB model output. +# This script makes sure that the data files exist, it then pulls all of the +# needed data records out of the various GRIB forecast files and puts them +# into one, consolidated GRIB file, and then runs a program that reads the TC +# Vitals records for the input day and updates the TC Vitals (if necessary). +# It then runs gettrk, which actually does the tracking. +# +# Environmental variable inputs needed for this scripts: +# PDY -- The date for data being processed, in YYYYMMDD format +# cyc -- The numbers for the cycle for data being processed (00, 06, 12, 18) +# cmodel -- Model being processed (gfs, mrf, ukmet, ecmwf, nam, ngm, ngps, +# gdas, gfdl, ens (ncep ensemble)) +# envir -- 'prod' or 'test' +# SENDCOM -- 'YES' or 'NO' +# stormenv -- This is only needed by the tracker run for the GFDL model. +# 'stormenv' contains the name/id that is used in the input +# grib file names. +# pert -- This is only needed by the tracker run for the NCEP ensemble. +# 'pert' contains the ensemble member id (e.g., n2, p4, etc.) +# which is used as part of the grib file names. +# +# For testing script interactively in non-production set following vars: +# archsyndir - Directory with syndir scripts/exec/fix +# + +qid=$$ +#----------------------------------------------# +# Get input date information # +#----------------------------------------------# + +#export PDY=${PDY:-$1} +#export cyc=${cyc:-$2} +#export cmodel=${cmodel:-$3} +export jobid=${jobid:-testjob} +export SENDCOM=${SENDCOM:-NO} +export PHASEFLAG=y +export WCORE_DEPTH=1.0 +#export PHASE_SCHEME=vtt +#export PHASE_SCHEME=cps +export PHASE_SCHEME=both +export STRUCTFLAG=n +export IKEFLAG=n + +if [ ! -d $TRKDATA ] +then + mkdir -p $TRKDATA +fi +cd $TRKDATA + +if [ ${#PDY} -eq 0 -o ${#cyc} -eq 0 -o ${#cmodel} -eq 0 ] +then + set +x + echo " " + echo "FATAL ERROR: Something wrong with input parameters." + echo "PDY= ${PDY}, cyc= ${cyc}, cmodel= ${cmodel}" + set -x + err_exit "FAILED ${jobid} -- BAD INPUTS AT LINE $LINENO IN TRACKER SCRIPT - ABNORMAL EXIT" +else + set +x + echo " " + echo " #-----------------------------------------------------------------#" + echo " At beginning of tracker script, the following imported variables " + echo " are defined: " + echo " PDY ................................... $PDY" + echo " cyc ................................... $cyc" + echo " cmodel ................................ $cmodel" + echo " jobid ................................. $jobid" + echo " envir ................................. $envir" + echo " SENDCOM ............................... $SENDCOM" + echo " " + set -x +fi + +scc=`echo ${PDY} | cut -c1-2` +syy=`echo ${PDY} | cut -c3-4` +smm=`echo ${PDY} | cut -c5-6` +sdd=`echo ${PDY} | cut -c7-8` +shh=${cyc} +symd=`echo ${PDY} | cut -c3-8` +syyyy=`echo ${PDY} | cut -c1-4` +CENT=`echo ${PDY} | cut -c1-2` + +#export archsyndir=${archsyndir:-${COMROOT}/arch/prod/syndat} +archsyndir=${archsyndir:-${COMINsyn:?}} + +#----------------------------------------------------------------# +# +# --- Define data directories and data file names --- +# +# Convert the input model to lowercase letters and check to see +# if it's a valid model, and assign a model ID number to it. +# This model ID number is passed into the Fortran program to +# let the program know what set of forecast hours to use in the +# ifhours array. Also, set the directories for the operational +# input GRIB data files and create templates for the file names. +# While only 1 of these sets of directories and file name +# templates is used during a particular run of this script, +# "gfsvitdir" is used every time, because that is the directory +# that contains the error-checked TC vitals file that Steve Lord +# produces, and so it is included after the case statement. +# +# NOTE: The varible PDY is now defined within the J-Jobs that +# call this script. Therefore there is no reason to do this +# here. +# +# NOTE: The script that processes the ECMWF data defines PDY as +# the current day, and in this script we need PDY to be +# yesterday's date (for the ecmwf ONLY). So instead, the ecmwf +# script will pass the variable PDYm1 to this script, and in the +# case statement below we change that to PDY. +# +# NOTE: Do NOT try to standardize this script by changing all of +# these various data directories' variable names to be all the +# same, such as "datadir". As you'll see in the data cutting +# part of this script below, different methods are used to cut +# apart different models, thus it is important to know the +# difference between them.... +#----------------------------------------------------------------# + +cmodel=`echo ${cmodel} | tr "[A-Z]" "[a-z]"` + +#---- tracking variable list------------------------------------ +user_wants_to_track_zeta850='y' +user_wants_to_track_zeta700='y' +user_wants_to_track_wcirc850='y' +user_wants_to_track_wcirc700='y' +user_wants_to_track_gph850='y' +user_wants_to_track_gph700='y' +user_wants_to_track_mslp='y' +user_wants_to_track_wcircsfc='n' +user_wants_to_track_zetasfc='n' +user_wants_to_track_thick500850='n' +user_wants_to_track_thick200850='n' +user_wants_to_track_thick200500='n' + +case ${cmodel} in + + gfs) set +x; echo " " ; + echo " ++ operational FV3-GFS chosen" ; + echo " "; set -x ; + gfsdir=${gfsdir:-${COMINgfs:?}/${cyc}/${COMPONENT}} ; + gfsgfile=gfs.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-6} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=1 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=15 ; + atcffreq=$((vit_incr*100)) ; + +# trkrwbd=0 ; +# trkrebd=360 ; +# trkrnbd=90 ; +# trkrsbd=-90 ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="gfso" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="gfso" ;; + +# g2_jpdtn sets the variable that will be used as "JPDTN" for +# the call to getgb2, if gribver=2. jpdtn=1 for ens data, +# jpdtn=0 for deterministic data. + + ngps) set +x; echo " " ; + echo " ++ operational NAVGEM chosen" ; + echo " "; set -x ; + ngpsdir=${ngpsdir:-${DCOM:?}} ; + ngpsgfile=US058GMET-OPSbd2.NAVGEM ; + ngemgfile=-${PDY}${cyc}-NOAA-halfdeg.gr2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-6} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=7 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=29 ; + atcffreq=$((vit_incr*100)) ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="ngx " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="ngx" ;; + + cmc) set +x; echo " " ; + echo " ++ operational CMC chosen" ; + echo " "; set -x ; + cmcdir=${cmcdir:-${DCOM:?}} ; + cmcgfile=CMC_glb_latlon.24x.24_${PDY}${cyc}_P ; + cmcgfile2=_NCEP.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-6} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=15 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=39 ; + atcffreq=$((vit_incr*100)) ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="cmc " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="cmc" ;; + + *) msg="Model $cmodel is not recognized." ; + echo "$msg"; postmsg $jlogfile "$msg" ; + err_exit "FAILED ${jobid} -- UNKNOWN cmodel IN TRACKER SCRIPT - ABNORMAL EXIT";; + +esac + +if [ ${PHASEFLAG} = 'y' ]; then + +#J.Peng----2014-10-28--------- + if [ ${cmodel} = "gfs" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300|RH:500)" + elif [ ${cmodel} = "ngps" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:400|HGT:300|TMP:500|TMP:400|TMP:300|RH:500)" + elif [ ${cmodel} = "cmc" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250|SPFH:500)" + fi + + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 U:200 V:850 V:700 V:500 V:200 10U:sfc 10V:sfc MSL:sfc GH:300 GH:400 GH:500 GH:925 T:300 T:400 T:500 R:500" +else + + wgrib_parmlist=" HGT:850 HGT:700 UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET " + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc" + +fi + +#---------------------------------------------------------------# +# +# -------- TC Vitals processing -------- +# +# Check Steve Lord's operational tcvitals file to see if any +# vitals records were processed for this time by his system. +# If there were, then you'll find a file in /com/gfs/prod/gfs.yymmdd +# with the vitals in it. Also check the raw TC Vitals file in +# /com/arch/prod/syndat , since this may contain storms that Steve's +# system ignored (Steve's system will ignore all storms that are +# either over land or very close to land); We still want to track +# these inland storms, AS LONG AS THEY ARE NHC STORMS (don't +# bother trying to track inland storms that are outside of NHC's +# domain of responsibility -- we don't need that info). +# UPDATE 5/12/98 MARCHOK: The script is updated so that for the +# global models, the gfs directory is checked for the error- +# checked vitals file, while for the regional models, the +# nam directory is checked for that file. +# UPDATE 3/27/09 MARCHOK: The SREF is run at off-synoptic times +# (03,09,15,21Z). There are no tcvitals issued at these offtimes, +# so the updating of the "old" tcvitals is critical for running +# the tracker on SREF. For updating the old tcvitals for SREF, +# we need to look 3h back, not 6h as for the other models that +# run at synoptic times. +#--------------------------------------------------------------# + +old_ymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +old_4ymd=` echo ${old_ymdh} | cut -c1-8` +old_ymd=` echo ${old_ymdh} | cut -c3-8` +old_hh=` echo ${old_ymdh} | cut -c9-10` +old_str="${old_ymd} ${old_hh}00" + +future_ymdh=` ${NDATE:?} ${vit_incr} ${PDY}${cyc}` +future_4ymd=` echo ${future_ymdh} | cut -c1-8` +future_ymd=` echo ${future_ymdh} | cut -c3-8` +future_hh=` echo ${future_ymdh} | cut -c9-10` +future_str="${future_ymd} ${future_hh}00" + +if [ ${modtyp} = 'global' ] +then + synvitdir=${COMINgfs:?}/${cyc}/${COMPONENT} + synvitfile=gfs.t${cyc}z.syndata.tcvitals.tm00 + synvitold_dir=${synvitdir%.*}.${old_4ymd}/${old_hh}/${COMPONENT} + synvitold_file=gfs.t${old_hh}z.syndata.tcvitals.tm00 + synvitfuture_dir=${synvitdir%.*}.${future_4ymd}/${future_hh}/${COMPONENT} + synvitfuture_file=gfs.t${future_hh}z.syndata.tcvitals.tm00 +else +# synvitdir=${COMROOT}/nam/prod/nam.${PDY} + synvitdir=${COMINnam:?} + synvitfile=nam.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/nam/prod/nam.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=nam.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/nam/prod/nam.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=nam.t${future_hh}z.syndata.tcvitals.tm00 +fi + +set +x +echo " " +echo " -----------------------------" +echo " " +echo " Now sorting and updating the TC Vitals file. Please wait...." +echo " " +set -x + +current_str="${symd} ${cyc}00" + +if [ -s ${synvitdir}/${synvitfile} -o\ + -s ${synvitold_dir}/${synvitold_file} -o\ + -s ${synvitfuture_dir}/${synvitfuture_file} ] +then + grep "${old_str}" ${synvitold_dir}/${synvitold_file} \ + >${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${synvitdir}/${synvitfile} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${future_str}" ${synvitfuture_dir}/${synvitfuture_file} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} +else + set +x + echo " " + echo " There is no (synthetic) TC vitals file for ${cyc}z in ${synvitdir}," + echo " nor is there a TC vitals file for ${old_hh}z in ${synvitold_dir}." + echo " nor is there a TC vitals file for ${future_hh}z in ${synvitfuture_dir}," + echo " Checking the raw TC Vitals file ....." + echo " " + set -x +fi + +# Take the vitals from Steve Lord's /com/gfs/prod tcvitals file, +# and cat them with the NHC-only vitals from the raw, original +# /com/arch/prod/synda_tcvitals file. Do this because the nwprod +# tcvitals file is the original tcvitals file, and Steve runs a +# program that ignores the vitals for a storm that's over land or +# even just too close to land, and for tracking purposes for the +# US regional models, we need these locations. Only include these +# "inland" storm vitals for NHC (we're not going to track inland +# storms that are outside of NHC's domain of responsibility -- we +# don't need that info). +# UPDATE 5/12/98 MARCHOK: awk logic is added to screen NHC +# vitals such as "91L NAMELESS" or "89E NAMELESS", since TPC +# does not want tracks for such storms. + +grep "${old_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${current_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${future_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-03---for genesis tc vitals-------------------- +#export COMINgenvit=/global/save/wx20jp/genesis_vital_${syyyy} +#grep "${current_str}" ${COMINgenvit}/gen_tc.vitals.gfs.gfso.2012 | \ +# grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ +# >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +#PRODTEST# +#PRODTEST# Use the next couple lines to test the tracker on the SP. +#PRODTEST# These next couple lines use data from a test TC Vitals file that +#PRODTEST# I generate. When you are ready to test this system, call me and +#PRODTEST# I'll create one for the current day, and then uncomment the next +#PRODTEST# couple lines in order to access the test vitals file. +# +#ttrkdir=/global/save/wx20tm/trak/prod/data +#ttrkdir=/global/save/wx20tm/trak/para/scripts +#grep "${current_str}" ${ttrkdir}/tcvit.01l >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + + +# IMPORTANT: When "cat-ing" these files, make sure that the vitals +# files from the "raw" TC vitals files are first in order and Steve's +# TC vitals files second. This is because Steve's vitals file has +# been error-checked, so if we have a duplicate tc vitals record in +# these 2 files (very likely), program supvit.x below will +# only take the last vitals record listed for a particular storm in +# the vitals file (all previous duplicates are ignored, and Steve's +# error-checked vitals records are kept). + +cat ${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} ${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} \ + >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# If we are doing the processing for the GFDL model, then we want +# to further cut down on which vitals we allow into this run of the +# tracker. The reason is that this program will be called from +# each individual run for a storm, so the grib files will be +# specific to each storm. So if 4 storms are being run at a +# particular cycle, then this script is run 4 separate times from +# within the GFDL_POST job. + +#if [ ${cmodel} = 'gfdl' ]; then +## grep -i ${stormid} ${COMIN}/${ATCFNAME}.vitals.${syy}${smm}${sdd}${shh} >${TRKDATA}/tmpvit +## mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#echo " " +#echo "!!! ERROR: TAKE THE COMMENTS OUT OF HERE BEFORE IMPLEMENTATION AAAA" +#echo "!!! AND TAKE OUT THESE NEXT 2 LINES AS WELL............................" +#echo " " +#grep -i ${stormid} ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/tmpvit +#mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#fi + +#--------------------------------------------------------------# +# Now run a fortran program that will read all the TC vitals +# records for the current dtg and the dtg from 6h ago, and +# sort out any duplicates. If the program finds a storm that +# was included in the vitals file 6h ago but not for the current +# dtg, this program updates the 6h-old first guess position +# and puts these updated records as well as the records from +# the current dtg into a temporary vitals file. It is this +# temporary vitals file that is then used as the input for the +# tracking program. +#--------------------------------------------------------------# + +oldymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +oldyy=`echo ${oldymdh} | cut -c3-4` +oldmm=`echo ${oldymdh} | cut -c5-6` +olddd=`echo ${oldymdh} | cut -c7-8` +oldhh=`echo ${oldymdh} | cut -c9-10` +oldymd=${oldyy}${oldmm}${olddd} + +futureymdh=` ${NDATE:?} 6 ${PDY}${cyc}` +futureyy=`echo ${futureymdh} | cut -c3-4` +futuremm=`echo ${futureymdh} | cut -c5-6` +futuredd=`echo ${futureymdh} | cut -c7-8` +futurehh=`echo ${futureymdh} | cut -c9-10` +futureymd=${futureyy}${futuremm}${futuredd} + +echo "&datenowin dnow%yy=${syy}, dnow%mm=${smm}," >${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&dateoldin dold%yy=${oldyy}, dold%mm=${oldmm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dold%dd=${olddd}, dold%hh=${oldhh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&datefuturein dfuture%yy=${futureyy}, dfuture%mm=${futuremm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dfuture%dd=${futuredd}, dfuture%hh=${futurehh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&hourinfo vit_hr_incr=${vit_incr}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period." + echo "!!! File ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} is empty." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi + +fi + +# For tcgen cases, filter to use only vitals from the ocean +# basin of interest.... +#J.Peng----2012-04-13------------------------------------- +#if [ ${trkrtype} = 'tcgen' ] +# then + +# if [ ${numvitrecs} -gt 0 ] +# then + +# fullvitfile=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# cp $fullvitfile ${TRKDATA}/vitals.all_basins.${atcfout}.${PDY}${cyc} +# basin=` echo $regtype | cut -c1-2` + +# if [ ${basin} = 'al' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "L") print $0}' | +# >${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'ep' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "E") print $0}' | +# >${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'wp' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "W") print $0}' | +# >${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi + +# cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# fi + +#fi +#J.Peng----2012-04-13------------------------------------- + +# - - - - - - - - - - - - - +# Before running the program to read, sort and update the vitals, +# first run the vitals through some awk logic, the purpose of +# which is to convert all the 2-digit years into 4-digit years. +# Beginning 4/21/99, NHC and JTWC will begin sending the vitals +# with 4-digit years, however it is unknown when other global +# forecasting centers will begin using 4-digit years, thus we +# need the following logic to ensure that all the vitals going +# into supvit.f have uniform, 4-digit years in their records. +# +# 1/8/2000: sed code added by Tim Marchok due to the fact that +# some of the vitals were getting past the syndata/qctropcy +# error-checking with a colon in them; the colon appeared +# in the character immediately to the left of the date, which +# was messing up the "(length($4) == 8)" statement logic. +# - - - - - - - - - - - - - + +sed -e "s/\:/ /g" ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} > ${TRKDATA}/tempvit +mv ${TRKDATA}/tempvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +awk ' +{ + yycheck = substr($0,20,2) + if ((yycheck == 20 || yycheck == 19) && (length($4) == 8)) { + printf ("%s\n",$0) + } + else { + if (yycheck >= 0 && yycheck <= 50) { + printf ("%s20%s\n",substr($0,1,19),substr($0,20)) + } + else { + printf ("%s19%s\n",substr($0,1,19),substr($0,20)) + } + } +} ' ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 + +mv ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +#Added by J.Peng----2012-04-12---------------------------------------------- +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +#----------------------------------------------------------------- +#J.Peng----2012-05-04--do not need this for TCvitals---- +#numvitrecs=0 + +if [ ${numvitrecs} -gt 0 ] +then + + export pgm=supvit_g2 + . prep_step + + # Input file + export FORT31=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_g2 <${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + suvrcc=$? + + if [ ${suvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit.x, " + echo "!!! which is the program that updates the TC Vitals file." + echo "!!! Return code from supvit.x = ${suvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING supvit_g2 IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else +#Added by J.Peng----2012-04-12---------------------------------------------- + rm -f ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + touch ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#----------------------------------------------------------------- +# In this section, check to see if the user requested the use of +# operational TC vitals records for the initial time only. This +# option might be used for a retrospective medium range forecast +# in which the user wants to initialize with the storms that are +# currently there, but then let the model do its own thing for +# the next 10 or 14 days.... + + +if [ ${USE_OPER_VITALS} = 'INIT_ONLY' ]; then + + if [ ${init_flag} = 'yes' ]; then + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are currently at." + echo " " + set -x + else + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are now *PAST*." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + fi + +elif [ ${USE_OPER_VITALS} = 'NO' ]; then + + set +x + echo " " + echo "NOTE: User has requested that historical vitals not be used...." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#------------------------------------------------------------------# +# Now select all storms to be processed, that is, process every +# storm that's listed in the updated vitals file for the current +# forecast hour. If there are no storms for the current time, +# then exit. +#------------------------------------------------------------------# + +numvitrecs=`cat ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period " + echo "!!! in the UPDATED vitals file." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi +fi + +set +x +echo " " +echo " *--------------------------------*" +echo " | STORM SELECTION |" +echo " *--------------------------------*" +echo " " +set -x + +ict=1 +while [ $ict -le 15 ] +do + stormflag[${ict}]=3 + let ict=ict+1 +done + +dtg_current="${symd} ${cyc}00" +stormmax=` grep "${dtg_current}" ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +if [ ${stormmax} -gt 15 ] +then + stormmax=15 +fi + +sct=1 +while [ ${sct} -le ${stormmax} ] +do + stormflag[${sct}]=1 + let sct=sct+1 +done + + +#---------------------------------------------------------------# +# +# -------- "Genesis" Vitals processing -------- +# +# May 2006: This entire genesis tracking system is being +# upgraded to more comprehensively track and categorize storms. +# One thing that has been missing from the tracking system is +# the ability to keep track of storms from one analysis cycle +# to the next. That is, the current system has been very +# effective at tracking systems within a forecast, but we have +# no methods in place for keeping track of storms across +# difference initial times. For example, if we are running +# the tracker on today's 00z GFS analysis, we will get a +# position for various storms at the analysis time. But then +# if we go ahead and run again at 06z, we have no way of +# telling the tracker that we know about the 00z position of +# this storm. We now address that problem by creating +# "genesis" vitals, that is, when a storm is found at an +# analysis time, we not only produce "atcfunix" output to +# detail the track & intensity of a found storm, but we also +# produce a vitals record that will be used for the next +# run of the tracker script. These "genesis vitals" records +# will be of the format: +# +# YYYYMMDDHH_AAAH_LLLLX_TYP +# +# Where: +# +# YYYYMMDDHH = Date the storm was FIRST identified +# by the tracker. +# AAA = Abs(Latitude) * 10; integer value +# H = 'N' for norther hem, 'S' for southern hem +# LLLL = Abs(Longitude) * 10; integer value +# X = 'E' for eastern hem, 'W' for western hem +# TYP = Tropical cyclone storm id if this is a +# tropical cyclone (e.g., "12L", or "09W", etc). +# If this is one that the tracker instead "Found +# On the Fly (FOF)", we simply put those three +# "FOF" characters in there. +#J.Peng----2012-04-12-------- +genvitfile=${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} +touch ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + +if [ -f $genvitfile ]; then + d6ago_ymdh=` ${NDATE:?} -6 ${PDY}${cyc}` + d6ago_4ymd=` echo ${d6ago_ymdh} | cut -c1-8` + d6ago_ymd=` echo ${d6ago_ymdh} | cut -c3-8` + d6ago_hh=` echo ${d6ago_ymdh} | cut -c9-10` + d6ago_str="${d6ago_ymd} ${d6ago_hh}00" + + d6ahead_ymdh=` ${NDATE:?} 6 ${PDY}${cyc}` + d6ahead_4ymd=` echo ${d6ahead_ymdh} | cut -c1-8` + d6ahead_ymd=` echo ${d6ahead_ymdh} | cut -c3-8` + d6ahead_hh=` echo ${d6ahead_ymdh} | cut -c9-10` + d6ahead_str="${d6ahead_ymd} ${d6ahead_hh}00" + + set +x + echo " " + echo " d6ago_str= --->${d6ago_str}<---" + echo " current_str= --->${current_str}<---" + echo " d6ahead_str= --->${d6ahead_str}<---" + echo " " + #echo " Listing and contents of ${genvitfile} follow " + #echo " for the times 6h ago, current and 6h ahead:" + #echo " " + set -x + + #J.Peng----2012-04-11----- + ##ls -la ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + ##cat ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + #ls -la ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #cat ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + + grep "${d6ago_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${d6ahead_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + num_gen_vits=`cat ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} | wc -l` +else + echo "WARNING: Genesis Vitals from previous run(s) not found!" +fi + +echo "&datenowin dnow%yy=${syyyy}, dnow%mm=${smm}," >${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6agoin d6ago%yy=${syyyy6}, d6ago%mm=${smm6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ago%dd=${sdd6}, d6ago%hh=${shh6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6aheadin d6ahead%yy=${syyyyp6}, d6ahead%mm=${smmp6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ahead%dd=${sddp6}, d6ahead%hh=${shhp6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-04--- need this for TC vitals------------------ +num_gen_vits=0 + +if [ ${num_gen_vits} -gt 0 ] +then + export pgm=supvit_gen + . prep_step + + # Input file + export FORT31=${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_gen <${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + sgvrcc=$? + + if [ ${sgvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit_gen, " + echo "!!! which is the program that updates the genesis vitals file." + echo "!!! Return code from supvit_gen = ${sgvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING SUPVIT_GEN IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + rm -f ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + touch ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +fi + + +#-----------------------------------------------------------------# +# +# ------ CUT APART INPUT GRIB FILES ------- +# +# For the selected model, cut apart the GRIB input files in order +# to pull out only the variables that we need for the tracker. +# Put these selected variables from all forecast hours into 1 big +# GRIB file that we'll use as input for the tracker. +# +# The wgrib utility (/nwprod/util/exec/wgrib) is used to cut out +# the needed parms for the GFS, MRF, GDAS, UKMET and NOGAPS files. +# The utility /nwprod/util/exec/copygb is used to interpolate the +# NGM (polar stereographic) and NAM (Lambert Conformal) data from +# their grids onto lat/lon grids. Note that while the lat/lon +# grid that I specify overlaps into areas that don't have any data +# on the original grid, Mark Iredell wrote the copygb software so +# that it will mask such "no-data" points with a bitmap (just be +# sure to check the lbms in your fortran program after getgb). +#-----------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------" +echo " NOW CUTTING APART INPUT GRIB FILES TO " +echo " CREATE 1 BIG GRIB INPUT FILE " +echo " -----------------------------------------" +echo " " +set -x + +regflag=`grep NHC ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +# ------------------------------ +# Process GFS, if selected +# ------------------------------ + +if [ ${model} -eq 1 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gfsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc} + fi + + rm -f ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f* + rm -f ${TRKDATA}/gfsgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gfsgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gfs wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gfsdir}/${gfsgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GFS File missing: ${gfsdir}/${gfsgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING GFS FILE IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${gfsdir}/${gfsgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + + gfs_master_file=${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + gfs_cat_file=${TRKDATA}/gfsgribfile.${PDY}${cyc} + cat ${gfs_master_file} >>${gfs_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gfsgribfile.${PDY}${cyc} ${TRKDATA}/gfsixfile.${PDY}${cyc} + export err=$?; err_chk + +# Now run through the work to get data for phase checking.... +# -------------------------------------------- + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "BEGIN: 300-500hPa T-mean and 200-850hPa U-shear" + echo " for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + +#J.Peng----2012-04-18---added for U-shear----- + gparm=33 + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-04-18---added for U-shear----- + + set +x + echo " " + echo "END: 300-500hPa T-mean and 200-850hPa U-shear" + echo " for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + cat ${catfile} >>${ffile} + + fi + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gfsixfile.${PDY}${cyc} + +fi + +# ------------------------------ +# Process NAVGEM, if selected +# ------------------------------ + +if [ ${model} -eq 7 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} ] + then + set +x + echo " " + echo "FATAL ERROR: NAVGEM File missing ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" >> ${DATA}/missing_ngps.txt + exit + else + err_exit "MISSING NAVGEM FILE at $0:$LINENO" + fi + fi + + gfile=${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + + done + +# Now run through the work to get data for phase checking.... + ${GRB2INDEX:?} ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ${TRKDATA}/ngpsixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for GPH AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for T AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk +fi + +# ------------------------------ +# Process CMC-deterministic forecast, if selected +# ------------------------------ + +if [ ${model} -eq 15 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/cmcgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/cmcgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} ] + then + set +x + echo " " + echo "FATAL ERROR: CMC File missing ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + fi + + gfile=${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + done +# J.Peng--04-05-2017--changing grid from 180E to 0E------------ +# wgrib2 junk -small_grib 0:359.76 -90:90 junk2 +# J.Peng--10-15-2019----uncomment the next 4 lines ------- + cp ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/junk + rm -f ${TRKDATA}/cmcgribfile.${PDY}${cyc} + ${WGRIB2} ${TRKDATA}/junk -small_grib 0:359.76 -90:90 ${TRKDATA}/junk2 + cp ${TRKDATA}/junk2 ${TRKDATA}/cmcgribfile.${PDY}${cyc} + +# Now run through the work to get data for phase checking.... + + ${GRB2INDEX:?} ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/cmcixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for GPH AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for T AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- +# +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + gparmq=7 + gparmt=11 + + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/rh_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparmq=${gparmq}," >>${namelist} + echo " iparmt=${gparmt}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT16=${FIXens_tracker}/cmc_rh_levs.txt + + # Output file + export FORT51=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/rhum_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to rhum_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -rhum_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + rhfile=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + cat ${rhfile} >>${catfile} +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk +fi + +#------------------------------------------------------------------------# +# Now run the tracker # +#------------------------------------------------------------------------# +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW EXECUTING TRACKER......" +echo " -----------------------------------------------" +echo " " +set -x + +ist=1 +while [ $ist -le 15 ] +do + if [ ${stormflag[${ist}]} -ne 1 ] + then + set +x; echo "Storm number $ist NOT selected for processing"; set -x + else + set +x; echo "Storm number $ist IS selected for processing...."; set -x + fi + let ist=ist+1 +done + +if [ ! -s ${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} ] +then + for fhr in $fcsthrs; do + if [ $fhr -ne 99 ]; then + last_fcst_hour=$fhr + fi + done + echo ${last_fcst_hour} >${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} +fi + +namelist=${TRKDATA}/input.${atcfout}.${PDY}${cyc} +ATCFNAME=` echo "${atcfname}" | tr '[a-z]' '[A-Z]'` + +export atcfymdh=${scc}${syy}${smm}${sdd}${shh} + +#------07-26-2019----creating the fix-file for GFS tracking ----- +if [ ${model} -eq 1 ]; then +. prep_step + +# Input files +namelist_4_fix=${TRKDATA}/namelist_4_fix_input.${PDY}${cyc} +echo "&timein maxhrs=${fcstlen}," >${namelist_4_fix} +echo " dthrs=${vit_incr}/" >>${namelist_4_fix} + +# Output file +export FORT10=${TRKDATA}/${cmodel}.genesis_leadtimes + +${EXECens_tracker}/leadtime <${namelist_4_fix} +rcc=$? + +if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to leadtime at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "leadtime - ERROR AT extrkr_tcv_gfs.sh LINE $LINENO" +fi + +fi +#------07-26-2019----creating the fix-file for GFS tracking ----- + +#contour_interval=100.0 +#write_vit=y +#want_oci=.TRUE. + +echo "&datein inp%bcc=${scc},inp%byy=${syy},inp%bmm=${smm}," >${namelist} +echo " inp%bdd=${sdd},inp%bhh=${shh},inp%model=${model}," >>${namelist} +echo " inp%modtyp='${modtyp}'," >>${namelist} +echo " inp%lt_units='${lead_time_units}'," >>${namelist} +echo " inp%file_seq='${file_sequence}'," >>${namelist} +echo " inp%nesttyp='${nest_type}'/" >>${namelist} + +echo "&atcfinfo atcfnum=${atcfnum},atcfname='${ATCFNAME}'," >>${namelist} +echo " atcfymdh=${atcfymdh},atcffreq=${atcffreq}/" >>${namelist} + +echo "&trackerinfo trkrinfo%westbd=${trkrwbd}," >>${namelist} +echo " trkrinfo%eastbd=${trkrebd}," >>${namelist} +echo " trkrinfo%northbd=${trkrnbd}," >>${namelist} +echo " trkrinfo%southbd=${trkrsbd}," >>${namelist} +echo " trkrinfo%type='${trkrtype}'," >>${namelist} +echo " trkrinfo%mslpthresh=${mslpthresh}," >>${namelist} +echo " trkrinfo%use_backup_mslp_grad_check='${use_backup_mslp_grad_check}'," >>${namelist} +echo " trkrinfo%max_mslp_850=${max_mslp_850}," >>${namelist} +echo " trkrinfo%v850thresh=${v850thresh}," >>${namelist} +echo " trkrinfo%use_backup_850_vt_check='${use_backup_850_vt_check}'," >>${namelist} +echo " trkrinfo%gridtype='${modtyp}'," >>${namelist} +echo " trkrinfo%enable_timing=1," >>${namelist} +echo " trkrinfo%contint=${contour_interval}," >>${namelist} +echo " trkrinfo%want_oci=${want_oci}," >>${namelist} +echo " trkrinfo%out_vit='${write_vit}'," >>${namelist} +echo " trkrinfo%use_land_mask='${use_land_mask}'," >>${namelist} +echo " trkrinfo%inp_data_type='${inp_data_type}'," >>${namelist} +echo " trkrinfo%gribver=${gribver}," >>${namelist} +echo " trkrinfo%g2_jpdtn=${g2_jpdtn}," >>${namelist} +echo " trkrinfo%g2_mslp_parm_id=${g2_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_mslp_parm_id=${g1_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_typ=${g1_sfcwind_lev_typ}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_val=${g1_sfcwind_lev_val}/" >>${namelist} + +echo "&phaseinfo phaseflag='${PHASEFLAG}'," >>${namelist} +echo " phasescheme='${PHASE_SCHEME}'," >>${namelist} +echo " wcore_depth=${WCORE_DEPTH}/" >>${namelist} + +echo "&structinfo structflag='${STRUCTFLAG}'," >>${namelist} +echo " ikeflag='${IKEFLAG}'/" >>${namelist} + +echo "&fnameinfo gmodname='${atcfname}'," >>${namelist} +echo " rundescr='${rundescr}'," >>${namelist} +echo " atcfdescr='${atcfdescr}'/" >>${namelist} + +echo "&waitinfo use_waitfor='n'," >>${namelist} +echo " wait_min_age=10," >>${namelist} +echo " wait_min_size=100," >>${namelist} +echo " wait_max_wait=1800," >>${namelist} +echo " wait_sleeptime=5," >>${namelist} +echo " per_fcst_command=''/" >>${namelist} + +echo "&netcdflist netcdfinfo%num_netcdf_vars=${ncdf_num_netcdf_vars}," >>${namelist} +echo " netcdfinfo%netcdf_filename='${netcdffile}'," >>${namelist} +echo " netcdfinfo%rv850name='${ncdf_rv850name}'," >>${namelist} +echo " netcdfinfo%rv700name='${ncdf_rv700name}'," >>${namelist} +echo " netcdfinfo%u850name='${ncdf_u850name}'," >>${namelist} +echo " netcdfinfo%v850name='${ncdf_v850name}'," >>${namelist} +echo " netcdfinfo%u700name='${ncdf_u700name}'," >>${namelist} +echo " netcdfinfo%v700name='${ncdf_v700name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%mslpname='${ncdf_mslpname}'," >>${namelist} +echo " netcdfinfo%usfcname='${ncdf_usfcname}'," >>${namelist} +echo " netcdfinfo%vsfcname='${ncdf_vsfcname}'," >>${namelist} +echo " netcdfinfo%u500name='${ncdf_u500name}'," >>${namelist} +echo " netcdfinfo%v500name='${ncdf_v500name}'," >>${namelist} +echo " netcdfinfo%tmean_300_500_name='${ncdf_tmean_300_500_name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z200name='${ncdf_z200name}'," >>${namelist} +echo " netcdfinfo%lmaskname='${ncdf_lmaskname}'," >>${namelist} +echo " netcdfinfo%z900name='${ncdf_z900name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z800name='${ncdf_z800name}'," >>${namelist} +echo " netcdfinfo%z750name='${ncdf_z750name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%z650name='${ncdf_z650name}'," >>${namelist} +echo " netcdfinfo%z600name='${ncdf_z600name}'," >>${namelist} +echo " netcdfinfo%z550name='${ncdf_z550name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z450name='${ncdf_z450name}'," >>${namelist} +echo " netcdfinfo%z400name='${ncdf_z400name}'," >>${namelist} +echo " netcdfinfo%z350name='${ncdf_z350name}'," >>${namelist} +echo " netcdfinfo%z300name='${ncdf_z300name}'," >>${namelist} +echo " netcdfinfo%time_name='${ncdf_time_name}'," >>${namelist} +echo " netcdfinfo%lon_name='${ncdf_lon_name}'," >>${namelist} +echo " netcdfinfo%lat_name='${ncdf_lat_name}'," >>${namelist} +echo " netcdfinfo%time_units='${ncdf_time_units}'/" >>${namelist} + +echo "&parmpreflist user_wants_to_track_zeta850='${user_wants_to_track_zeta850}'," >>${namelist} +echo " user_wants_to_track_zeta700='${user_wants_to_track_zeta700}'," >>${namelist} +echo " user_wants_to_track_wcirc850='${user_wants_to_track_wcirc850}'," >>${namelist} +echo " user_wants_to_track_wcirc700='${user_wants_to_track_wcirc700}'," >>${namelist} +echo " user_wants_to_track_gph850='${user_wants_to_track_gph850}'," >>${namelist} +echo " user_wants_to_track_gph700='${user_wants_to_track_gph700}'," >>${namelist} +echo " user_wants_to_track_mslp='${user_wants_to_track_mslp}'," >>${namelist} +echo " user_wants_to_track_wcircsfc='${user_wants_to_track_wcircsfc}'," >>${namelist} +echo " user_wants_to_track_zetasfc='${user_wants_to_track_zetasfc}'," >>${namelist} +echo " user_wants_to_track_thick500850='${user_wants_to_track_thick500850}'," >>${namelist} +echo " user_wants_to_track_thick200500='${user_wants_to_track_thick200500}'," >>${namelist} +echo " user_wants_to_track_thick200850='${user_wants_to_track_thick200850}'/" >>${namelist} + +echo "&verbose verb=3,verb_g2=0/" >>${namelist} + +export pgm=gettrk_gen_gfs +. prep_step + +export FORT11=${gribfile} +export FORT12=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} +export FORT14=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +if [ ${model} -eq 1 ]; then + export FORT15=${TRKDATA}/${cmodel}.genesis_leadtimes +else + export FORT15=${FIXens_tracker}/${cmodel}.genesis_leadtimes +fi + +export FORT31=${ixfile} + +if [ ${trkrtype} = 'tracker' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT61=${TRKDATA}/trak.${atcfout}.all.${stormenv}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${stormenv}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${stormenv}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${stormenv}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${stormenv}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${stormenv}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${stormenv}.${PDY}${cyc} + else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${PDY}${cyc} + fi +else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${regtype}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${regtype}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${regtype}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${regtype}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${regtype}.${PDY}${cyc} +fi + +if [ ${atcfname} = 'aear' ] +then + export FORT65=${TRKDATA}/trak.${atcfout}.initvitl.${PDY}${cyc} +fi + +if [ ${write_vit} = 'y' ] +then + export FORT67=${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} +fi + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${stormenv}.${PDY}${cyc} + else + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${PDY}${cyc} + fi +fi + +if [ ${STRUCTFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${stormenv}.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${stormenv}.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${stormenv}.${PDY}${cyc} + else + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${PDY}${cyc} + fi +fi + +if [ ${IKEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${stormenv}.${PDY}${cyc} + else + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${PDY}${cyc} + fi +fi + +if [ ${trkrtype} = 'midlat' -o ${trkrtype} = 'tcgen' ]; then + export FORT77=${TRKDATA}/trkrmask.${atcfout}.${regtype}.${PDY}${cyc} +fi + +msg="$pgm start for $atcfout at ${cyc}z" +postmsg "$jlogfile" "$msg" + +set +x +echo "+++ TIMING: BEFORE gettrk ---> `date`" +set -x + +ulimit -c unlimited + +${EXECens_tracker}/gettrk_gen_gfs <${namelist} +gettrk_rcc=$? + +set +x +echo "+++ TIMING: AFTER gettrk ---> `date`" +set -x + +#--------------------------------------------------------------# +# Now copy the output track files to different directories +#--------------------------------------------------------------# + +if [ ${gettrk_rcc} -eq 0 ]; then + set +x + echo " " + echo " -----------------------------------------------" + echo " NOW COPYING OUTPUT TRACK FILES TO COM " + echo " -----------------------------------------------" + echo " " + set -x + + if [ -s ${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} ]; then + if [ "$SENDCOM" = 'YES' ]; then + cp ${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} ${COMOUTgenvit:?}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #if [ "$SENDDBN" = 'YES' ]; then + # $DBNROOT/bin/dbn_alert ${cmodel^^} GENESIS $job ${COMOUTgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #fi + fi + fi + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" +else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running gettrk_gen_gfs, " + echo "!!! which is the program that actually gets the track." + echo "!!! Return code from gettrk_gen_gfs = ${gettrk_rcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING gettrk_gen_gfs IN TRACKER SCRIPT- ABNORMAL EXIT" +fi diff --git a/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_gfs.sh_06032020 b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_gfs.sh_06032020 new file mode 100755 index 0000000000..932bcd6cda --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_gfs.sh_06032020 @@ -0,0 +1,1937 @@ +#!/bin/ksh +export PS4=' + extrkr_tcv_gfs.sh line $LINENO: ' + +set +x +############################################################################## +echo " " +echo "------------------------------------------------" +echo "xxxx - Track vortices in model GRIB2 output" +echo "J.Peng---10-06-2014---------------------------- " +echo "Current time is: `date`" +echo " " +############################################################################## +set -x + +loopnum=$1 +cmodel=$2 +pert=$3 +TRKDATA=$4 + +export gribver=2 +#init_flag=$5 +# USE_OPER_VITALS=NO +# USE_OPER_VITALS=INIT_ONLY +USE_OPER_VITALS=YES + +############################################################################## +# +# FLOW OF CONTROL +# +# 1. Define data directories and file names for the input model +# 2. Process input starting date/cycle information +# 3. Update TC Vitals file and select storms to be processed +# 4. Cut apart input GRIB files to select only the needed parms and hours +# 5. Execute the tracker +# 6. Copy the output track files to various locations +# +############################################################################## +msg="has begun for ${cmodel} ${pert} at ${cyc}z" +postmsg "$jlogfile" "$msg" +######################################## + +# This script runs the hurricane tracker using operational GRIB model output. +# This script makes sure that the data files exist, it then pulls all of the +# needed data records out of the various GRIB forecast files and puts them +# into one, consolidated GRIB file, and then runs a program that reads the TC +# Vitals records for the input day and updates the TC Vitals (if necessary). +# It then runs gettrk, which actually does the tracking. +# +# Environmental variable inputs needed for this scripts: +# PDY -- The date for data being processed, in YYYYMMDD format +# cyc -- The numbers for the cycle for data being processed (00, 06, 12, 18) +# cmodel -- Model being processed (gfs, mrf, ukmet, ecmwf, nam, ngm, ngps, +# gdas, gfdl, ens (ncep ensemble)) +# envir -- 'prod' or 'test' +# SENDCOM -- 'YES' or 'NO' +# stormenv -- This is only needed by the tracker run for the GFDL model. +# 'stormenv' contains the name/id that is used in the input +# grib file names. +# pert -- This is only needed by the tracker run for the NCEP ensemble. +# 'pert' contains the ensemble member id (e.g., n2, p4, etc.) +# which is used as part of the grib file names. +# +# For testing script interactively in non-production set following vars: +# archsyndir - Directory with syndir scripts/exec/fix +# + +qid=$$ +#----------------------------------------------# +# Get input date information # +#----------------------------------------------# + +#export PDY=${PDY:-$1} +#export cyc=${cyc:-$2} +#export cmodel=${cmodel:-$3} +export jobid=${jobid:-testjob} +export SENDCOM=${SENDCOM:-NO} +export PHASEFLAG=y +export WCORE_DEPTH=1.0 +#export PHASE_SCHEME=vtt +#export PHASE_SCHEME=cps +export PHASE_SCHEME=both +export STRUCTFLAG=n +export IKEFLAG=n + +if [ ! -d $TRKDATA ] +then + mkdir -p $TRKDATA +fi +cd $TRKDATA + +if [ ${#PDY} -eq 0 -o ${#cyc} -eq 0 -o ${#cmodel} -eq 0 ] +then + set +x + echo " " + echo "FATAL ERROR: Something wrong with input parameters." + echo "PDY= ${PDY}, cyc= ${cyc}, cmodel= ${cmodel}" + set -x + err_exit "FAILED ${jobid} -- BAD INPUTS AT LINE $LINENO IN TRACKER SCRIPT - ABNORMAL EXIT" +else + set +x + echo " " + echo " #-----------------------------------------------------------------#" + echo " At beginning of tracker script, the following imported variables " + echo " are defined: " + echo " PDY ................................... $PDY" + echo " cyc ................................... $cyc" + echo " cmodel ................................ $cmodel" + echo " jobid ................................. $jobid" + echo " envir ................................. $envir" + echo " SENDCOM ............................... $SENDCOM" + echo " " + set -x +fi + +scc=`echo ${PDY} | cut -c1-2` +syy=`echo ${PDY} | cut -c3-4` +smm=`echo ${PDY} | cut -c5-6` +sdd=`echo ${PDY} | cut -c7-8` +shh=${cyc} +symd=`echo ${PDY} | cut -c3-8` +syyyy=`echo ${PDY} | cut -c1-4` +CENT=`echo ${PDY} | cut -c1-2` + +#export archsyndir=${archsyndir:-${COMROOT}/arch/prod/syndat} +archsyndir=${archsyndir:-${COMINsyn:?}} + +#----------------------------------------------------------------# +# +# --- Define data directories and data file names --- +# +# Convert the input model to lowercase letters and check to see +# if it's a valid model, and assign a model ID number to it. +# This model ID number is passed into the Fortran program to +# let the program know what set of forecast hours to use in the +# ifhours array. Also, set the directories for the operational +# input GRIB data files and create templates for the file names. +# While only 1 of these sets of directories and file name +# templates is used during a particular run of this script, +# "gfsvitdir" is used every time, because that is the directory +# that contains the error-checked TC vitals file that Steve Lord +# produces, and so it is included after the case statement. +# +# NOTE: The varible PDY is now defined within the J-Jobs that +# call this script. Therefore there is no reason to do this +# here. +# +# NOTE: The script that processes the ECMWF data defines PDY as +# the current day, and in this script we need PDY to be +# yesterday's date (for the ecmwf ONLY). So instead, the ecmwf +# script will pass the variable PDYm1 to this script, and in the +# case statement below we change that to PDY. +# +# NOTE: Do NOT try to standardize this script by changing all of +# these various data directories' variable names to be all the +# same, such as "datadir". As you'll see in the data cutting +# part of this script below, different methods are used to cut +# apart different models, thus it is important to know the +# difference between them.... +#----------------------------------------------------------------# + +cmodel=`echo ${cmodel} | tr "[A-Z]" "[a-z]"` + +#---- tracking variable list------------------------------------ +user_wants_to_track_zeta850='y' +user_wants_to_track_zeta700='y' +user_wants_to_track_wcirc850='y' +user_wants_to_track_wcirc700='y' +user_wants_to_track_gph850='y' +user_wants_to_track_gph700='y' +user_wants_to_track_mslp='y' +user_wants_to_track_wcircsfc='n' +user_wants_to_track_zetasfc='n' +user_wants_to_track_thick500850='n' +user_wants_to_track_thick200850='n' +user_wants_to_track_thick200500='n' + +case ${cmodel} in + + gfs) set +x; echo " " ; + echo " ++ operational FV3-GFS chosen" ; + echo " "; set -x ; + gfsdir=${gfsdir:-${COMINgfs:?}} ; + gfsgfile=gfs.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-6} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=1 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=15 ; + atcffreq=$((vit_incr*100)) ; + +# trkrwbd=0 ; +# trkrebd=360 ; +# trkrnbd=90 ; +# trkrsbd=-90 ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="gfso" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="gfso" ;; + +# g2_jpdtn sets the variable that will be used as "JPDTN" for +# the call to getgb2, if gribver=2. jpdtn=1 for ens data, +# jpdtn=0 for deterministic data. + + ngps) set +x; echo " " ; + echo " ++ operational NAVGEM chosen" ; + echo " "; set -x ; + ngpsdir=${ngpsdir:-${DCOM:?}} ; + ngpsgfile=US058GMET-OPSbd2.NAVGEM ; + ngemgfile=-${PDY}${cyc}-NOAA-halfdeg.gr2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-6} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=7 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=29 ; + atcffreq=$((vit_incr*100)) ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="ngx " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="ngx" ;; + + cmc) set +x; echo " " ; + echo " ++ operational CMC chosen" ; + echo " "; set -x ; + cmcdir=${cmcdir:-${DCOM:?}} ; + cmcgfile=CMC_glb_latlon.24x.24_${PDY}${cyc}_P ; + cmcgfile2=_NCEP.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-6} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=15 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=39 ; + atcffreq=$((vit_incr*100)) ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="cmc " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="cmc" ;; + + *) msg="Model $cmodel is not recognized." ; + echo "$msg"; postmsg $jlogfile "$msg" ; + err_exit "FAILED ${jobid} -- UNKNOWN cmodel IN TRACKER SCRIPT - ABNORMAL EXIT";; + +esac + +if [ ${PHASEFLAG} = 'y' ]; then + +#J.Peng----2014-10-28--------- + if [ ${cmodel} = "gfs" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300|RH:500)" + elif [ ${cmodel} = "ngps" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:400|HGT:300|TMP:500|TMP:400|TMP:300|RH:500)" + elif [ ${cmodel} = "cmc" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250|SPFH:500)" + fi + + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 U:200 V:850 V:700 V:500 V:200 10U:sfc 10V:sfc MSL:sfc GH:300 GH:400 GH:500 GH:925 T:300 T:400 T:500 R:500" +else + + wgrib_parmlist=" HGT:850 HGT:700 UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET " + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc" + +fi + +#---------------------------------------------------------------# +# +# -------- TC Vitals processing -------- +# +# Check Steve Lord's operational tcvitals file to see if any +# vitals records were processed for this time by his system. +# If there were, then you'll find a file in /com/gfs/prod/gfs.yymmdd +# with the vitals in it. Also check the raw TC Vitals file in +# /com/arch/prod/syndat , since this may contain storms that Steve's +# system ignored (Steve's system will ignore all storms that are +# either over land or very close to land); We still want to track +# these inland storms, AS LONG AS THEY ARE NHC STORMS (don't +# bother trying to track inland storms that are outside of NHC's +# domain of responsibility -- we don't need that info). +# UPDATE 5/12/98 MARCHOK: The script is updated so that for the +# global models, the gfs directory is checked for the error- +# checked vitals file, while for the regional models, the +# nam directory is checked for that file. +# UPDATE 3/27/09 MARCHOK: The SREF is run at off-synoptic times +# (03,09,15,21Z). There are no tcvitals issued at these offtimes, +# so the updating of the "old" tcvitals is critical for running +# the tracker on SREF. For updating the old tcvitals for SREF, +# we need to look 3h back, not 6h as for the other models that +# run at synoptic times. +#--------------------------------------------------------------# + +old_ymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +old_4ymd=` echo ${old_ymdh} | cut -c1-8` +old_ymd=` echo ${old_ymdh} | cut -c3-8` +old_hh=` echo ${old_ymdh} | cut -c9-10` +old_str="${old_ymd} ${old_hh}00" + +future_ymdh=` ${NDATE:?} ${vit_incr} ${PDY}${cyc}` +future_4ymd=` echo ${future_ymdh} | cut -c1-8` +future_ymd=` echo ${future_ymdh} | cut -c3-8` +future_hh=` echo ${future_ymdh} | cut -c9-10` +future_str="${future_ymd} ${future_hh}00" + +if [ ${modtyp} = 'global' ] +then +# synvitdir=${COMROOT}/gfs/prod/gfs.${PDY} + synvitdir=${COMINgfs:?} + synvitfile=gfs.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/gfs/prod/gfs.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=gfs.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/gfs/prod/gfs.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=gfs.t${future_hh}z.syndata.tcvitals.tm00 +else +# synvitdir=${COMROOT}/nam/prod/nam.${PDY} + synvitdir=${COMINnam:?} + synvitfile=nam.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/nam/prod/nam.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=nam.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/nam/prod/nam.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=nam.t${future_hh}z.syndata.tcvitals.tm00 +fi + +set +x +echo " " +echo " -----------------------------" +echo " " +echo " Now sorting and updating the TC Vitals file. Please wait...." +echo " " +set -x + +current_str="${symd} ${cyc}00" + +if [ -s ${synvitdir}/${synvitfile} -o\ + -s ${synvitold_dir}/${synvitold_file} -o\ + -s ${synvitfuture_dir}/${synvitfuture_file} ] +then + grep "${old_str}" ${synvitold_dir}/${synvitold_file} \ + >${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${synvitdir}/${synvitfile} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${future_str}" ${synvitfuture_dir}/${synvitfuture_file} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} +else + set +x + echo " " + echo " There is no (synthetic) TC vitals file for ${cyc}z in ${synvitdir}," + echo " nor is there a TC vitals file for ${old_hh}z in ${synvitold_dir}." + echo " nor is there a TC vitals file for ${future_hh}z in ${synvitfuture_dir}," + echo " Checking the raw TC Vitals file ....." + echo " " + set -x +fi + +# Take the vitals from Steve Lord's /com/gfs/prod tcvitals file, +# and cat them with the NHC-only vitals from the raw, original +# /com/arch/prod/synda_tcvitals file. Do this because the nwprod +# tcvitals file is the original tcvitals file, and Steve runs a +# program that ignores the vitals for a storm that's over land or +# even just too close to land, and for tracking purposes for the +# US regional models, we need these locations. Only include these +# "inland" storm vitals for NHC (we're not going to track inland +# storms that are outside of NHC's domain of responsibility -- we +# don't need that info). +# UPDATE 5/12/98 MARCHOK: awk logic is added to screen NHC +# vitals such as "91L NAMELESS" or "89E NAMELESS", since TPC +# does not want tracks for such storms. + +grep "${old_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${current_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${future_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-03---for genesis tc vitals-------------------- +#export COMINgenvit=/global/save/wx20jp/genesis_vital_${syyyy} +#grep "${current_str}" ${COMINgenvit}/gen_tc.vitals.gfs.gfso.2012 | \ +# grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ +# >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +#PRODTEST# +#PRODTEST# Use the next couple lines to test the tracker on the SP. +#PRODTEST# These next couple lines use data from a test TC Vitals file that +#PRODTEST# I generate. When you are ready to test this system, call me and +#PRODTEST# I'll create one for the current day, and then uncomment the next +#PRODTEST# couple lines in order to access the test vitals file. +# +#ttrkdir=/global/save/wx20tm/trak/prod/data +#ttrkdir=/global/save/wx20tm/trak/para/scripts +#grep "${current_str}" ${ttrkdir}/tcvit.01l >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + + +# IMPORTANT: When "cat-ing" these files, make sure that the vitals +# files from the "raw" TC vitals files are first in order and Steve's +# TC vitals files second. This is because Steve's vitals file has +# been error-checked, so if we have a duplicate tc vitals record in +# these 2 files (very likely), program supvit.x below will +# only take the last vitals record listed for a particular storm in +# the vitals file (all previous duplicates are ignored, and Steve's +# error-checked vitals records are kept). + +cat ${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} ${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} \ + >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# If we are doing the processing for the GFDL model, then we want +# to further cut down on which vitals we allow into this run of the +# tracker. The reason is that this program will be called from +# each individual run for a storm, so the grib files will be +# specific to each storm. So if 4 storms are being run at a +# particular cycle, then this script is run 4 separate times from +# within the GFDL_POST job. + +#if [ ${cmodel} = 'gfdl' ]; then +## grep -i ${stormid} ${COMIN}/${ATCFNAME}.vitals.${syy}${smm}${sdd}${shh} >${TRKDATA}/tmpvit +## mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#echo " " +#echo "!!! ERROR: TAKE THE COMMENTS OUT OF HERE BEFORE IMPLEMENTATION AAAA" +#echo "!!! AND TAKE OUT THESE NEXT 2 LINES AS WELL............................" +#echo " " +#grep -i ${stormid} ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/tmpvit +#mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#fi + +#--------------------------------------------------------------# +# Now run a fortran program that will read all the TC vitals +# records for the current dtg and the dtg from 6h ago, and +# sort out any duplicates. If the program finds a storm that +# was included in the vitals file 6h ago but not for the current +# dtg, this program updates the 6h-old first guess position +# and puts these updated records as well as the records from +# the current dtg into a temporary vitals file. It is this +# temporary vitals file that is then used as the input for the +# tracking program. +#--------------------------------------------------------------# + +oldymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +oldyy=`echo ${oldymdh} | cut -c3-4` +oldmm=`echo ${oldymdh} | cut -c5-6` +olddd=`echo ${oldymdh} | cut -c7-8` +oldhh=`echo ${oldymdh} | cut -c9-10` +oldymd=${oldyy}${oldmm}${olddd} + +futureymdh=` ${NDATE:?} 6 ${PDY}${cyc}` +futureyy=`echo ${futureymdh} | cut -c3-4` +futuremm=`echo ${futureymdh} | cut -c5-6` +futuredd=`echo ${futureymdh} | cut -c7-8` +futurehh=`echo ${futureymdh} | cut -c9-10` +futureymd=${futureyy}${futuremm}${futuredd} + +echo "&datenowin dnow%yy=${syy}, dnow%mm=${smm}," >${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&dateoldin dold%yy=${oldyy}, dold%mm=${oldmm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dold%dd=${olddd}, dold%hh=${oldhh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&datefuturein dfuture%yy=${futureyy}, dfuture%mm=${futuremm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dfuture%dd=${futuredd}, dfuture%hh=${futurehh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&hourinfo vit_hr_incr=${vit_incr}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period." + echo "!!! File ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} is empty." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi + +fi + +# For tcgen cases, filter to use only vitals from the ocean +# basin of interest.... +#J.Peng----2012-04-13------------------------------------- +#if [ ${trkrtype} = 'tcgen' ] +# then + +# if [ ${numvitrecs} -gt 0 ] +# then + +# fullvitfile=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# cp $fullvitfile ${TRKDATA}/vitals.all_basins.${atcfout}.${PDY}${cyc} +# basin=` echo $regtype | cut -c1-2` + +# if [ ${basin} = 'al' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "L") print $0}' | +# >${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'ep' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "E") print $0}' | +# >${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'wp' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "W") print $0}' | +# >${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi + +# cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# fi + +#fi +#J.Peng----2012-04-13------------------------------------- + +# - - - - - - - - - - - - - +# Before running the program to read, sort and update the vitals, +# first run the vitals through some awk logic, the purpose of +# which is to convert all the 2-digit years into 4-digit years. +# Beginning 4/21/99, NHC and JTWC will begin sending the vitals +# with 4-digit years, however it is unknown when other global +# forecasting centers will begin using 4-digit years, thus we +# need the following logic to ensure that all the vitals going +# into supvit.f have uniform, 4-digit years in their records. +# +# 1/8/2000: sed code added by Tim Marchok due to the fact that +# some of the vitals were getting past the syndata/qctropcy +# error-checking with a colon in them; the colon appeared +# in the character immediately to the left of the date, which +# was messing up the "(length($4) == 8)" statement logic. +# - - - - - - - - - - - - - + +sed -e "s/\:/ /g" ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} > ${TRKDATA}/tempvit +mv ${TRKDATA}/tempvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +awk ' +{ + yycheck = substr($0,20,2) + if ((yycheck == 20 || yycheck == 19) && (length($4) == 8)) { + printf ("%s\n",$0) + } + else { + if (yycheck >= 0 && yycheck <= 50) { + printf ("%s20%s\n",substr($0,1,19),substr($0,20)) + } + else { + printf ("%s19%s\n",substr($0,1,19),substr($0,20)) + } + } +} ' ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 + +mv ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +#Added by J.Peng----2012-04-12---------------------------------------------- +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +#----------------------------------------------------------------- +#J.Peng----2012-05-04--do not need this for TCvitals---- +#numvitrecs=0 + +if [ ${numvitrecs} -gt 0 ] +then + + export pgm=supvit_g2 + . prep_step + + # Input file + export FORT31=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_g2 <${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + suvrcc=$? + + if [ ${suvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit.x, " + echo "!!! which is the program that updates the TC Vitals file." + echo "!!! Return code from supvit.x = ${suvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING supvit_g2 IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else +#Added by J.Peng----2012-04-12---------------------------------------------- + rm -f ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + touch ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#----------------------------------------------------------------- +# In this section, check to see if the user requested the use of +# operational TC vitals records for the initial time only. This +# option might be used for a retrospective medium range forecast +# in which the user wants to initialize with the storms that are +# currently there, but then let the model do its own thing for +# the next 10 or 14 days.... + + +if [ ${USE_OPER_VITALS} = 'INIT_ONLY' ]; then + + if [ ${init_flag} = 'yes' ]; then + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are currently at." + echo " " + set -x + else + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are now *PAST*." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + fi + +elif [ ${USE_OPER_VITALS} = 'NO' ]; then + + set +x + echo " " + echo "NOTE: User has requested that historical vitals not be used...." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#------------------------------------------------------------------# +# Now select all storms to be processed, that is, process every +# storm that's listed in the updated vitals file for the current +# forecast hour. If there are no storms for the current time, +# then exit. +#------------------------------------------------------------------# + +numvitrecs=`cat ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period " + echo "!!! in the UPDATED vitals file." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi +fi + +set +x +echo " " +echo " *--------------------------------*" +echo " | STORM SELECTION |" +echo " *--------------------------------*" +echo " " +set -x + +ict=1 +while [ $ict -le 15 ] +do + stormflag[${ict}]=3 + let ict=ict+1 +done + +dtg_current="${symd} ${cyc}00" +stormmax=` grep "${dtg_current}" ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +if [ ${stormmax} -gt 15 ] +then + stormmax=15 +fi + +sct=1 +while [ ${sct} -le ${stormmax} ] +do + stormflag[${sct}]=1 + let sct=sct+1 +done + + +#---------------------------------------------------------------# +# +# -------- "Genesis" Vitals processing -------- +# +# May 2006: This entire genesis tracking system is being +# upgraded to more comprehensively track and categorize storms. +# One thing that has been missing from the tracking system is +# the ability to keep track of storms from one analysis cycle +# to the next. That is, the current system has been very +# effective at tracking systems within a forecast, but we have +# no methods in place for keeping track of storms across +# difference initial times. For example, if we are running +# the tracker on today's 00z GFS analysis, we will get a +# position for various storms at the analysis time. But then +# if we go ahead and run again at 06z, we have no way of +# telling the tracker that we know about the 00z position of +# this storm. We now address that problem by creating +# "genesis" vitals, that is, when a storm is found at an +# analysis time, we not only produce "atcfunix" output to +# detail the track & intensity of a found storm, but we also +# produce a vitals record that will be used for the next +# run of the tracker script. These "genesis vitals" records +# will be of the format: +# +# YYYYMMDDHH_AAAH_LLLLX_TYP +# +# Where: +# +# YYYYMMDDHH = Date the storm was FIRST identified +# by the tracker. +# AAA = Abs(Latitude) * 10; integer value +# H = 'N' for norther hem, 'S' for southern hem +# LLLL = Abs(Longitude) * 10; integer value +# X = 'E' for eastern hem, 'W' for western hem +# TYP = Tropical cyclone storm id if this is a +# tropical cyclone (e.g., "12L", or "09W", etc). +# If this is one that the tracker instead "Found +# On the Fly (FOF)", we simply put those three +# "FOF" characters in there. +#J.Peng----2012-04-12-------- +genvitfile=${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} +touch ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + +if [ -f $genvitfile ]; then + d6ago_ymdh=` ${NDATE:?} -6 ${PDY}${cyc}` + d6ago_4ymd=` echo ${d6ago_ymdh} | cut -c1-8` + d6ago_ymd=` echo ${d6ago_ymdh} | cut -c3-8` + d6ago_hh=` echo ${d6ago_ymdh} | cut -c9-10` + d6ago_str="${d6ago_ymd} ${d6ago_hh}00" + + d6ahead_ymdh=` ${NDATE:?} 6 ${PDY}${cyc}` + d6ahead_4ymd=` echo ${d6ahead_ymdh} | cut -c1-8` + d6ahead_ymd=` echo ${d6ahead_ymdh} | cut -c3-8` + d6ahead_hh=` echo ${d6ahead_ymdh} | cut -c9-10` + d6ahead_str="${d6ahead_ymd} ${d6ahead_hh}00" + + set +x + echo " " + echo " d6ago_str= --->${d6ago_str}<---" + echo " current_str= --->${current_str}<---" + echo " d6ahead_str= --->${d6ahead_str}<---" + echo " " + #echo " Listing and contents of ${genvitfile} follow " + #echo " for the times 6h ago, current and 6h ahead:" + #echo " " + set -x + + #J.Peng----2012-04-11----- + ##ls -la ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + ##cat ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + #ls -la ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #cat ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + + grep "${d6ago_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${d6ahead_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + num_gen_vits=`cat ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} | wc -l` +else + echo "WARNING: Genesis Vitals from previous run(s) not found!" +fi + +echo "&datenowin dnow%yy=${syyyy}, dnow%mm=${smm}," >${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6agoin d6ago%yy=${syyyy6}, d6ago%mm=${smm6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ago%dd=${sdd6}, d6ago%hh=${shh6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6aheadin d6ahead%yy=${syyyyp6}, d6ahead%mm=${smmp6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ahead%dd=${sddp6}, d6ahead%hh=${shhp6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-04--- need this for TC vitals------------------ +num_gen_vits=0 + +if [ ${num_gen_vits} -gt 0 ] +then + export pgm=supvit_gen + . prep_step + + # Input file + export FORT31=${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_gen <${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + sgvrcc=$? + + if [ ${sgvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit_gen, " + echo "!!! which is the program that updates the genesis vitals file." + echo "!!! Return code from supvit_gen = ${sgvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING SUPVIT_GEN IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + rm -f ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + touch ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +fi + + +#-----------------------------------------------------------------# +# +# ------ CUT APART INPUT GRIB FILES ------- +# +# For the selected model, cut apart the GRIB input files in order +# to pull out only the variables that we need for the tracker. +# Put these selected variables from all forecast hours into 1 big +# GRIB file that we'll use as input for the tracker. +# +# The wgrib utility (/nwprod/util/exec/wgrib) is used to cut out +# the needed parms for the GFS, MRF, GDAS, UKMET and NOGAPS files. +# The utility /nwprod/util/exec/copygb is used to interpolate the +# NGM (polar stereographic) and NAM (Lambert Conformal) data from +# their grids onto lat/lon grids. Note that while the lat/lon +# grid that I specify overlaps into areas that don't have any data +# on the original grid, Mark Iredell wrote the copygb software so +# that it will mask such "no-data" points with a bitmap (just be +# sure to check the lbms in your fortran program after getgb). +#-----------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------" +echo " NOW CUTTING APART INPUT GRIB FILES TO " +echo " CREATE 1 BIG GRIB INPUT FILE " +echo " -----------------------------------------" +echo " " +set -x + +regflag=`grep NHC ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +# ------------------------------ +# Process GFS, if selected +# ------------------------------ + +if [ ${model} -eq 1 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gfsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc} + fi + + rm -f ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f* + rm -f ${TRKDATA}/gfsgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gfsgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gfs wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gfsdir}/${gfsgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GFS File missing: ${gfsdir}/${gfsgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING GFS FILE IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${gfsdir}/${gfsgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + + gfs_master_file=${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + gfs_cat_file=${TRKDATA}/gfsgribfile.${PDY}${cyc} + cat ${gfs_master_file} >>${gfs_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gfsgribfile.${PDY}${cyc} ${TRKDATA}/gfsixfile.${PDY}${cyc} + export err=$?; err_chk + +# Now run through the work to get data for phase checking.... +# -------------------------------------------- + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "BEGIN: 300-500hPa T-mean and 200-850hPa U-shear" + echo " for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + +#J.Peng----2012-04-18---added for U-shear----- + gparm=33 + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-04-18---added for U-shear----- + + set +x + echo " " + echo "END: 300-500hPa T-mean and 200-850hPa U-shear" + echo " for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + cat ${catfile} >>${ffile} + + fi + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gfsixfile.${PDY}${cyc} + +fi + +# ------------------------------ +# Process NAVGEM, if selected +# ------------------------------ + +if [ ${model} -eq 7 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} ] + then + set +x + echo " " + echo "FATAL ERROR: NAVGEM File missing ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" >> ${DATA}/missing_ngps.txt + exit + else + err_exit "MISSING NAVGEM FILE at $0:$LINENO" + fi + fi + + gfile=${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + + done + +# Now run through the work to get data for phase checking.... + ${GRB2INDEX:?} ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ${TRKDATA}/ngpsixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for GPH AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for T AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk +fi + +# ------------------------------ +# Process CMC-deterministic forecast, if selected +# ------------------------------ + +if [ ${model} -eq 15 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/cmcgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/cmcgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} ] + then + set +x + echo " " + echo "FATAL ERROR: CMC File missing ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + fi + + gfile=${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + done +# J.Peng--04-05-2017--changing grid from 180E to 0E------------ +# wgrib2 junk -small_grib 0:359.76 -90:90 junk2 +# J.Peng--10-15-2019----uncomment the next 4 lines ------- + cp ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/junk + rm -f ${TRKDATA}/cmcgribfile.${PDY}${cyc} + ${WGRIB2} ${TRKDATA}/junk -small_grib 0:359.76 -90:90 ${TRKDATA}/junk2 + cp ${TRKDATA}/junk2 ${TRKDATA}/cmcgribfile.${PDY}${cyc} + +# Now run through the work to get data for phase checking.... + + ${GRB2INDEX:?} ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/cmcixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for GPH AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for T AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- +# +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + gparmq=7 + gparmt=11 + + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/rh_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparmq=${gparmq}," >>${namelist} + echo " iparmt=${gparmt}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT16=${FIXens_tracker}/cmc_rh_levs.txt + + # Output file + export FORT51=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/rhum_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to rhum_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -rhum_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + rhfile=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + cat ${rhfile} >>${catfile} +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk +fi + +#------------------------------------------------------------------------# +# Now run the tracker # +#------------------------------------------------------------------------# +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW EXECUTING TRACKER......" +echo " -----------------------------------------------" +echo " " +set -x + +ist=1 +while [ $ist -le 15 ] +do + if [ ${stormflag[${ist}]} -ne 1 ] + then + set +x; echo "Storm number $ist NOT selected for processing"; set -x + else + set +x; echo "Storm number $ist IS selected for processing...."; set -x + fi + let ist=ist+1 +done + +if [ ! -s ${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} ] +then + for fhr in $fcsthrs; do + if [ $fhr -ne 99 ]; then + last_fcst_hour=$fhr + fi + done + echo ${last_fcst_hour} >${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} +fi + +namelist=${TRKDATA}/input.${atcfout}.${PDY}${cyc} +ATCFNAME=` echo "${atcfname}" | tr '[a-z]' '[A-Z]'` + +export atcfymdh=${scc}${syy}${smm}${sdd}${shh} + +#------07-26-2019----creating the fix-file for GFS tracking ----- +if [ ${model} -eq 1 ]; then +. prep_step + +# Input files +namelist_4_fix=${TRKDATA}/namelist_4_fix_input.${PDY}${cyc} +echo "&timein maxhrs=${fcstlen}," >${namelist_4_fix} +echo " dthrs=${vit_incr}/" >>${namelist_4_fix} + +# Output file +export FORT10=${TRKDATA}/${cmodel}.genesis_leadtimes + +${EXECens_tracker}/leadtime <${namelist_4_fix} +rcc=$? + +if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to leadtime at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "leadtime - ERROR AT extrkr_tcv_gfs.sh LINE $LINENO" +fi + +fi +#------07-26-2019----creating the fix-file for GFS tracking ----- + +#contour_interval=100.0 +#write_vit=y +#want_oci=.TRUE. + +echo "&datein inp%bcc=${scc},inp%byy=${syy},inp%bmm=${smm}," >${namelist} +echo " inp%bdd=${sdd},inp%bhh=${shh},inp%model=${model}," >>${namelist} +echo " inp%modtyp='${modtyp}'," >>${namelist} +echo " inp%lt_units='${lead_time_units}'," >>${namelist} +echo " inp%file_seq='${file_sequence}'," >>${namelist} +echo " inp%nesttyp='${nest_type}'/" >>${namelist} + +echo "&atcfinfo atcfnum=${atcfnum},atcfname='${ATCFNAME}'," >>${namelist} +echo " atcfymdh=${atcfymdh},atcffreq=${atcffreq}/" >>${namelist} + +echo "&trackerinfo trkrinfo%westbd=${trkrwbd}," >>${namelist} +echo " trkrinfo%eastbd=${trkrebd}," >>${namelist} +echo " trkrinfo%northbd=${trkrnbd}," >>${namelist} +echo " trkrinfo%southbd=${trkrsbd}," >>${namelist} +echo " trkrinfo%type='${trkrtype}'," >>${namelist} +echo " trkrinfo%mslpthresh=${mslpthresh}," >>${namelist} +echo " trkrinfo%use_backup_mslp_grad_check='${use_backup_mslp_grad_check}'," >>${namelist} +echo " trkrinfo%max_mslp_850=${max_mslp_850}," >>${namelist} +echo " trkrinfo%v850thresh=${v850thresh}," >>${namelist} +echo " trkrinfo%use_backup_850_vt_check='${use_backup_850_vt_check}'," >>${namelist} +echo " trkrinfo%gridtype='${modtyp}'," >>${namelist} +echo " trkrinfo%enable_timing=1," >>${namelist} +echo " trkrinfo%contint=${contour_interval}," >>${namelist} +echo " trkrinfo%want_oci=${want_oci}," >>${namelist} +echo " trkrinfo%out_vit='${write_vit}'," >>${namelist} +echo " trkrinfo%use_land_mask='${use_land_mask}'," >>${namelist} +echo " trkrinfo%inp_data_type='${inp_data_type}'," >>${namelist} +echo " trkrinfo%gribver=${gribver}," >>${namelist} +echo " trkrinfo%g2_jpdtn=${g2_jpdtn}," >>${namelist} +echo " trkrinfo%g2_mslp_parm_id=${g2_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_mslp_parm_id=${g1_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_typ=${g1_sfcwind_lev_typ}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_val=${g1_sfcwind_lev_val}/" >>${namelist} + +echo "&phaseinfo phaseflag='${PHASEFLAG}'," >>${namelist} +echo " phasescheme='${PHASE_SCHEME}'," >>${namelist} +echo " wcore_depth=${WCORE_DEPTH}/" >>${namelist} + +echo "&structinfo structflag='${STRUCTFLAG}'," >>${namelist} +echo " ikeflag='${IKEFLAG}'/" >>${namelist} + +echo "&fnameinfo gmodname='${atcfname}'," >>${namelist} +echo " rundescr='${rundescr}'," >>${namelist} +echo " atcfdescr='${atcfdescr}'/" >>${namelist} + +echo "&waitinfo use_waitfor='n'," >>${namelist} +echo " wait_min_age=10," >>${namelist} +echo " wait_min_size=100," >>${namelist} +echo " wait_max_wait=1800," >>${namelist} +echo " wait_sleeptime=5," >>${namelist} +echo " per_fcst_command=''/" >>${namelist} + +echo "&netcdflist netcdfinfo%num_netcdf_vars=${ncdf_num_netcdf_vars}," >>${namelist} +echo " netcdfinfo%netcdf_filename='${netcdffile}'," >>${namelist} +echo " netcdfinfo%rv850name='${ncdf_rv850name}'," >>${namelist} +echo " netcdfinfo%rv700name='${ncdf_rv700name}'," >>${namelist} +echo " netcdfinfo%u850name='${ncdf_u850name}'," >>${namelist} +echo " netcdfinfo%v850name='${ncdf_v850name}'," >>${namelist} +echo " netcdfinfo%u700name='${ncdf_u700name}'," >>${namelist} +echo " netcdfinfo%v700name='${ncdf_v700name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%mslpname='${ncdf_mslpname}'," >>${namelist} +echo " netcdfinfo%usfcname='${ncdf_usfcname}'," >>${namelist} +echo " netcdfinfo%vsfcname='${ncdf_vsfcname}'," >>${namelist} +echo " netcdfinfo%u500name='${ncdf_u500name}'," >>${namelist} +echo " netcdfinfo%v500name='${ncdf_v500name}'," >>${namelist} +echo " netcdfinfo%tmean_300_500_name='${ncdf_tmean_300_500_name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z200name='${ncdf_z200name}'," >>${namelist} +echo " netcdfinfo%lmaskname='${ncdf_lmaskname}'," >>${namelist} +echo " netcdfinfo%z900name='${ncdf_z900name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z800name='${ncdf_z800name}'," >>${namelist} +echo " netcdfinfo%z750name='${ncdf_z750name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%z650name='${ncdf_z650name}'," >>${namelist} +echo " netcdfinfo%z600name='${ncdf_z600name}'," >>${namelist} +echo " netcdfinfo%z550name='${ncdf_z550name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z450name='${ncdf_z450name}'," >>${namelist} +echo " netcdfinfo%z400name='${ncdf_z400name}'," >>${namelist} +echo " netcdfinfo%z350name='${ncdf_z350name}'," >>${namelist} +echo " netcdfinfo%z300name='${ncdf_z300name}'," >>${namelist} +echo " netcdfinfo%time_name='${ncdf_time_name}'," >>${namelist} +echo " netcdfinfo%lon_name='${ncdf_lon_name}'," >>${namelist} +echo " netcdfinfo%lat_name='${ncdf_lat_name}'," >>${namelist} +echo " netcdfinfo%time_units='${ncdf_time_units}'/" >>${namelist} + +echo "&parmpreflist user_wants_to_track_zeta850='${user_wants_to_track_zeta850}'," >>${namelist} +echo " user_wants_to_track_zeta700='${user_wants_to_track_zeta700}'," >>${namelist} +echo " user_wants_to_track_wcirc850='${user_wants_to_track_wcirc850}'," >>${namelist} +echo " user_wants_to_track_wcirc700='${user_wants_to_track_wcirc700}'," >>${namelist} +echo " user_wants_to_track_gph850='${user_wants_to_track_gph850}'," >>${namelist} +echo " user_wants_to_track_gph700='${user_wants_to_track_gph700}'," >>${namelist} +echo " user_wants_to_track_mslp='${user_wants_to_track_mslp}'," >>${namelist} +echo " user_wants_to_track_wcircsfc='${user_wants_to_track_wcircsfc}'," >>${namelist} +echo " user_wants_to_track_zetasfc='${user_wants_to_track_zetasfc}'," >>${namelist} +echo " user_wants_to_track_thick500850='${user_wants_to_track_thick500850}'," >>${namelist} +echo " user_wants_to_track_thick200500='${user_wants_to_track_thick200500}'," >>${namelist} +echo " user_wants_to_track_thick200850='${user_wants_to_track_thick200850}'/" >>${namelist} + +echo "&verbose verb=3,verb_g2=0/" >>${namelist} + +export pgm=gettrk_gen_gfs +. prep_step + +export FORT11=${gribfile} +export FORT12=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} +export FORT14=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +if [ ${model} -eq 1 ]; then + export FORT15=${TRKDATA}/${cmodel}.genesis_leadtimes +else + export FORT15=${FIXens_tracker}/${cmodel}.genesis_leadtimes +fi + +export FORT31=${ixfile} + +if [ ${trkrtype} = 'tracker' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT61=${TRKDATA}/trak.${atcfout}.all.${stormenv}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${stormenv}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${stormenv}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${stormenv}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${stormenv}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${stormenv}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${stormenv}.${PDY}${cyc} + else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${PDY}${cyc} + fi +else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${regtype}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${regtype}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${regtype}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${regtype}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${regtype}.${PDY}${cyc} +fi + +if [ ${atcfname} = 'aear' ] +then + export FORT65=${TRKDATA}/trak.${atcfout}.initvitl.${PDY}${cyc} +fi + +if [ ${write_vit} = 'y' ] +then + export FORT67=${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} +fi + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${stormenv}.${PDY}${cyc} + else + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${PDY}${cyc} + fi +fi + +if [ ${STRUCTFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${stormenv}.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${stormenv}.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${stormenv}.${PDY}${cyc} + else + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${PDY}${cyc} + fi +fi + +if [ ${IKEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${stormenv}.${PDY}${cyc} + else + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${PDY}${cyc} + fi +fi + +if [ ${trkrtype} = 'midlat' -o ${trkrtype} = 'tcgen' ]; then + export FORT77=${TRKDATA}/trkrmask.${atcfout}.${regtype}.${PDY}${cyc} +fi + +msg="$pgm start for $atcfout at ${cyc}z" +postmsg "$jlogfile" "$msg" + +set +x +echo "+++ TIMING: BEFORE gettrk ---> `date`" +set -x + +ulimit -c unlimited + +${EXECens_tracker}/gettrk_gen_gfs <${namelist} +gettrk_rcc=$? + +set +x +echo "+++ TIMING: AFTER gettrk ---> `date`" +set -x + +#--------------------------------------------------------------# +# Now copy the output track files to different directories +#--------------------------------------------------------------# + +if [ ${gettrk_rcc} -eq 0 ]; then + set +x + echo " " + echo " -----------------------------------------------" + echo " NOW COPYING OUTPUT TRACK FILES TO COM " + echo " -----------------------------------------------" + echo " " + set -x + + if [ -s ${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} ]; then + if [ "$SENDCOM" = 'YES' ]; then + cp ${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} ${COMOUTgenvit:?}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #if [ "$SENDDBN" = 'YES' ]; then + # $DBNROOT/bin/dbn_alert ${cmodel^^} GENESIS $job ${COMOUTgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #fi + fi + fi + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" +else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running gettrk_gen_gfs, " + echo "!!! which is the program that actually gets the track." + echo "!!! Return code from gettrk_gen_gfs = ${gettrk_rcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING gettrk_gen_gfs IN TRACKER SCRIPT- ABNORMAL EXIT" +fi diff --git a/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_gfs.sh_10152019 b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_gfs.sh_10152019 new file mode 100755 index 0000000000..22b1a6161c --- /dev/null +++ b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_gfs.sh_10152019 @@ -0,0 +1,1936 @@ +#!/bin/ksh +export PS4=' + extrkr_tcv_gfs.sh line $LINENO: ' + +set +x +############################################################################## +echo " " +echo "------------------------------------------------" +echo "xxxx - Track vortices in model GRIB2 output" +echo "J.Peng---10-06-2014---------------------------- " +echo "Current time is: `date`" +echo " " +############################################################################## +set -x + +loopnum=$1 +cmodel=$2 +pert=$3 +TRKDATA=$4 + +export gribver=2 +#init_flag=$5 +# USE_OPER_VITALS=NO +# USE_OPER_VITALS=INIT_ONLY +USE_OPER_VITALS=YES + +############################################################################## +# +# FLOW OF CONTROL +# +# 1. Define data directories and file names for the input model +# 2. Process input starting date/cycle information +# 3. Update TC Vitals file and select storms to be processed +# 4. Cut apart input GRIB files to select only the needed parms and hours +# 5. Execute the tracker +# 6. Copy the output track files to various locations +# +############################################################################## +msg="has begun for ${cmodel} ${pert} at ${cyc}z" +postmsg "$jlogfile" "$msg" +######################################## + +# This script runs the hurricane tracker using operational GRIB model output. +# This script makes sure that the data files exist, it then pulls all of the +# needed data records out of the various GRIB forecast files and puts them +# into one, consolidated GRIB file, and then runs a program that reads the TC +# Vitals records for the input day and updates the TC Vitals (if necessary). +# It then runs gettrk, which actually does the tracking. +# +# Environmental variable inputs needed for this scripts: +# PDY -- The date for data being processed, in YYYYMMDD format +# cyc -- The numbers for the cycle for data being processed (00, 06, 12, 18) +# cmodel -- Model being processed (gfs, mrf, ukmet, ecmwf, nam, ngm, ngps, +# gdas, gfdl, ens (ncep ensemble)) +# envir -- 'prod' or 'test' +# SENDCOM -- 'YES' or 'NO' +# stormenv -- This is only needed by the tracker run for the GFDL model. +# 'stormenv' contains the name/id that is used in the input +# grib file names. +# pert -- This is only needed by the tracker run for the NCEP ensemble. +# 'pert' contains the ensemble member id (e.g., n2, p4, etc.) +# which is used as part of the grib file names. +# +# For testing script interactively in non-production set following vars: +# archsyndir - Directory with syndir scripts/exec/fix +# + +qid=$$ +#----------------------------------------------# +# Get input date information # +#----------------------------------------------# + +#export PDY=${PDY:-$1} +#export cyc=${cyc:-$2} +#export cmodel=${cmodel:-$3} +export jobid=${jobid:-testjob} +export SENDCOM=${SENDCOM:-NO} +export PHASEFLAG=y +export WCORE_DEPTH=1.0 +#export PHASE_SCHEME=vtt +#export PHASE_SCHEME=cps +export PHASE_SCHEME=both +export STRUCTFLAG=n +export IKEFLAG=n + +if [ ! -d $TRKDATA ] +then + mkdir -p $TRKDATA +fi +cd $TRKDATA + +if [ ${#PDY} -eq 0 -o ${#cyc} -eq 0 -o ${#cmodel} -eq 0 ] +then + set +x + echo " " + echo "FATAL ERROR: Something wrong with input parameters." + echo "PDY= ${PDY}, cyc= ${cyc}, cmodel= ${cmodel}" + set -x + err_exit "FAILED ${jobid} -- BAD INPUTS AT LINE $LINENO IN TRACKER SCRIPT - ABNORMAL EXIT" +else + set +x + echo " " + echo " #-----------------------------------------------------------------#" + echo " At beginning of tracker script, the following imported variables " + echo " are defined: " + echo " PDY ................................... $PDY" + echo " cyc ................................... $cyc" + echo " cmodel ................................ $cmodel" + echo " jobid ................................. $jobid" + echo " envir ................................. $envir" + echo " SENDCOM ............................... $SENDCOM" + echo " " + set -x +fi + +scc=`echo ${PDY} | cut -c1-2` +syy=`echo ${PDY} | cut -c3-4` +smm=`echo ${PDY} | cut -c5-6` +sdd=`echo ${PDY} | cut -c7-8` +shh=${cyc} +symd=`echo ${PDY} | cut -c3-8` +syyyy=`echo ${PDY} | cut -c1-4` +CENT=`echo ${PDY} | cut -c1-2` + +#export archsyndir=${archsyndir:-${COMROOT}/arch/prod/syndat} +archsyndir=${archsyndir:-${COMINsyn:?}} + +#----------------------------------------------------------------# +# +# --- Define data directories and data file names --- +# +# Convert the input model to lowercase letters and check to see +# if it's a valid model, and assign a model ID number to it. +# This model ID number is passed into the Fortran program to +# let the program know what set of forecast hours to use in the +# ifhours array. Also, set the directories for the operational +# input GRIB data files and create templates for the file names. +# While only 1 of these sets of directories and file name +# templates is used during a particular run of this script, +# "gfsvitdir" is used every time, because that is the directory +# that contains the error-checked TC vitals file that Steve Lord +# produces, and so it is included after the case statement. +# +# NOTE: The varible PDY is now defined within the J-Jobs that +# call this script. Therefore there is no reason to do this +# here. +# +# NOTE: The script that processes the ECMWF data defines PDY as +# the current day, and in this script we need PDY to be +# yesterday's date (for the ecmwf ONLY). So instead, the ecmwf +# script will pass the variable PDYm1 to this script, and in the +# case statement below we change that to PDY. +# +# NOTE: Do NOT try to standardize this script by changing all of +# these various data directories' variable names to be all the +# same, such as "datadir". As you'll see in the data cutting +# part of this script below, different methods are used to cut +# apart different models, thus it is important to know the +# difference between them.... +#----------------------------------------------------------------# + +cmodel=`echo ${cmodel} | tr "[A-Z]" "[a-z]"` + +#---- tracking variable list------------------------------------ +user_wants_to_track_zeta850='y' +user_wants_to_track_zeta700='y' +user_wants_to_track_wcirc850='y' +user_wants_to_track_wcirc700='y' +user_wants_to_track_gph850='y' +user_wants_to_track_gph700='y' +user_wants_to_track_mslp='y' +user_wants_to_track_wcircsfc='n' +user_wants_to_track_zetasfc='n' +user_wants_to_track_thick500850='n' +user_wants_to_track_thick200850='n' +user_wants_to_track_thick200500='n' + +case ${cmodel} in + + gfs) set +x; echo " " ; + echo " ++ operational FV3-GFS chosen" ; + echo " "; set -x ; + gfsdir=${gfsdir:-${COMINgfs:?}} ; + gfsgfile=gfs.t${cyc}z.pgrb2.0p25.f ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-6} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=1 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=15 ; + atcffreq=$((vit_incr*100)) ; + +# trkrwbd=0 ; +# trkrebd=360 ; +# trkrnbd=90 ; +# trkrsbd=-90 ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=192 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="gfso" ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="gfso" ;; + +# g2_jpdtn sets the variable that will be used as "JPDTN" for +# the call to getgb2, if gribver=2. jpdtn=1 for ens data, +# jpdtn=0 for deterministic data. + + ngps) set +x; echo " " ; + echo " ++ operational NAVGEM chosen" ; + echo " "; set -x ; + ngpsdir=${ngpsdir:-${DCOM:?}} ; + ngpsgfile=US058GMET-OPSbd2.NAVGEM ; + ngemgfile=-${PDY}${cyc}-NOAA-halfdeg.gr2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-6} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=7 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=29 ; + atcffreq=$((vit_incr*100)) ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="ngx " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="ngx" ;; + + cmc) set +x; echo " " ; + echo " ++ operational CMC chosen" ; + echo " "; set -x ; + cmcdir=${cmcdir:-${DCOM:?}} ; + cmcgfile=CMC_glb_latlon.24x.24_${PDY}${cyc}_P ; + cmcgfile2=_NCEP.grib2 ; + + vit_incr=${FHOUT_CYCLONE:-6} ; + fcstlen=${FHMAX_CYCLONE:-6} ; + fcsthrs=$(seq -f%03g -s' ' 0 $vit_incr $fcstlen) ; + + model=15 ; + modtyp='global' ; + lead_time_units='hours' ; + file_sequence="onebig" ; + nest_type=" " ; + + atcfnum=39 ; + atcffreq=$((vit_incr*100)) ; + trkrwbd=105 ; + trkrebd=350 ; + trkrnbd=30 ; + trkrsbd=5 ; +# regtype=altg ; + trkrtype='tcgen' ; + mslpthresh=0.0015 ; + use_backup_mslp_grad_check='y' ; + max_mslp_850=400.0 ; + v850thresh=1.5000 ; + use_backup_850_vt_check='y' ; + + contour_interval=100.0 ; + want_oci=.TRUE. ; + write_vit='y' ; + use_land_mask='n' ; + inp_data_type='grib' ; + + gribver=2 ; + g2_jpdtn=0 ; + g2_mslp_parm_id=1 ; + g1_mslp_parm_id=130 ; + g1_sfcwind_lev_typ=105 ; + g1_sfcwind_lev_val=10 ; + + PHASEFLAG='y' ; + PHASE_SCHEME='both' ; + WCORE_DEPTH=1.0 ; + + STRUCTFLAG='n' ; + IKEFLAG='n' ; + atcfname="cmc " ; + rundescr='xxxx' ; + atcfdescr='xxxx' ; + atcfout="cmc" ;; + + *) msg="Model $cmodel is not recognized." ; + echo "$msg"; postmsg $jlogfile "$msg" ; + err_exit "FAILED ${jobid} -- UNKNOWN cmodel IN TRACKER SCRIPT - ABNORMAL EXIT";; + +esac + +if [ ${PHASEFLAG} = 'y' ]; then + +#J.Peng----2014-10-28--------- + if [ ${cmodel} = "gfs" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|MSLET|HGT:900|HGT:850|HGT:800|HGT:750|HGT:700|HGT:650|HGT:600|HGT:550|HGT:500|HGT:450|HGT:400|HGT:350|HGT:300|TMP:500|TMP:450|TMP:400|TMP:350|TMP:300|RH:500)" + elif [ ${cmodel} = "ngps" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:400|HGT:300|TMP:500|TMP:400|TMP:300|RH:500)" + elif [ ${cmodel} = "cmc" ]; then + PARMlist="(UGRD:850|UGRD:700|UGRD:500|UGRD:200|VGRD:850|VGRD:700|VGRD:500|VGRD:200|UGRD:10 m a|VGRD:10 m a|ABSV:850|ABSV:700|PRMSL|HGT:925|HGT:850|HGT:700|HGT:500|HGT:250|TMP:500|TMP:250|SPFH:500)" + fi + + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 U:200 V:850 V:700 V:500 V:200 10U:sfc 10V:sfc MSL:sfc GH:300 GH:400 GH:500 GH:925 T:300 T:400 T:500 R:500" +else + + wgrib_parmlist=" HGT:850 HGT:700 UGRD:850 UGRD:700 UGRD:500 VGRD:850 VGRD:700 VGRD:500 SurfaceU SurfaceV ABSV:850 ABSV:700 MSLET " + wgrib_ec_hires_parmlist=" GH:850 GH:700 U:850 U:700 U:500 V:850 V:700 V:500 10U:sfc 10V:sfc MSL:sfc" + +fi + +#---------------------------------------------------------------# +# +# -------- TC Vitals processing -------- +# +# Check Steve Lord's operational tcvitals file to see if any +# vitals records were processed for this time by his system. +# If there were, then you'll find a file in /com/gfs/prod/gfs.yymmdd +# with the vitals in it. Also check the raw TC Vitals file in +# /com/arch/prod/syndat , since this may contain storms that Steve's +# system ignored (Steve's system will ignore all storms that are +# either over land or very close to land); We still want to track +# these inland storms, AS LONG AS THEY ARE NHC STORMS (don't +# bother trying to track inland storms that are outside of NHC's +# domain of responsibility -- we don't need that info). +# UPDATE 5/12/98 MARCHOK: The script is updated so that for the +# global models, the gfs directory is checked for the error- +# checked vitals file, while for the regional models, the +# nam directory is checked for that file. +# UPDATE 3/27/09 MARCHOK: The SREF is run at off-synoptic times +# (03,09,15,21Z). There are no tcvitals issued at these offtimes, +# so the updating of the "old" tcvitals is critical for running +# the tracker on SREF. For updating the old tcvitals for SREF, +# we need to look 3h back, not 6h as for the other models that +# run at synoptic times. +#--------------------------------------------------------------# + +old_ymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +old_4ymd=` echo ${old_ymdh} | cut -c1-8` +old_ymd=` echo ${old_ymdh} | cut -c3-8` +old_hh=` echo ${old_ymdh} | cut -c9-10` +old_str="${old_ymd} ${old_hh}00" + +future_ymdh=` ${NDATE:?} ${vit_incr} ${PDY}${cyc}` +future_4ymd=` echo ${future_ymdh} | cut -c1-8` +future_ymd=` echo ${future_ymdh} | cut -c3-8` +future_hh=` echo ${future_ymdh} | cut -c9-10` +future_str="${future_ymd} ${future_hh}00" + +if [ ${modtyp} = 'global' ] +then +# synvitdir=${COMROOT}/gfs/prod/gfs.${PDY} + synvitdir=${COMINgfs:?} + synvitfile=gfs.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/gfs/prod/gfs.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=gfs.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/gfs/prod/gfs.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=gfs.t${future_hh}z.syndata.tcvitals.tm00 +else +# synvitdir=${COMROOT}/nam/prod/nam.${PDY} + synvitdir=${COMINnam:?} + synvitfile=nam.t${cyc}z.syndata.tcvitals.tm00 +# synvitold_dir=${COMROOT}/nam/prod/nam.${old_4ymd} + synvitold_dir=${synvitdir%.*}.${old_4ymd} + synvitold_file=nam.t${old_hh}z.syndata.tcvitals.tm00 +# synvitfuture_dir=${COMROOT}/nam/prod/nam.${future_4ymd} + synvitfuture_dir=${synvitdir%.*}.${future_4ymd} + synvitfuture_file=nam.t${future_hh}z.syndata.tcvitals.tm00 +fi + +set +x +echo " " +echo " -----------------------------" +echo " " +echo " Now sorting and updating the TC Vitals file. Please wait...." +echo " " +set -x + +current_str="${symd} ${cyc}00" + +if [ -s ${synvitdir}/${synvitfile} -o\ + -s ${synvitold_dir}/${synvitold_file} -o\ + -s ${synvitfuture_dir}/${synvitfuture_file} ] +then + grep "${old_str}" ${synvitold_dir}/${synvitold_file} \ + >${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${synvitdir}/${synvitfile} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} + grep "${future_str}" ${synvitfuture_dir}/${synvitfuture_file} \ + >>${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} +else + set +x + echo " " + echo " There is no (synthetic) TC vitals file for ${cyc}z in ${synvitdir}," + echo " nor is there a TC vitals file for ${old_hh}z in ${synvitold_dir}." + echo " nor is there a TC vitals file for ${future_hh}z in ${synvitfuture_dir}," + echo " Checking the raw TC Vitals file ....." + echo " " + set -x +fi + +# Take the vitals from Steve Lord's /com/gfs/prod tcvitals file, +# and cat them with the NHC-only vitals from the raw, original +# /com/arch/prod/synda_tcvitals file. Do this because the nwprod +# tcvitals file is the original tcvitals file, and Steve runs a +# program that ignores the vitals for a storm that's over land or +# even just too close to land, and for tracking purposes for the +# US regional models, we need these locations. Only include these +# "inland" storm vitals for NHC (we're not going to track inland +# storms that are outside of NHC's domain of responsibility -- we +# don't need that info). +# UPDATE 5/12/98 MARCHOK: awk logic is added to screen NHC +# vitals such as "91L NAMELESS" or "89E NAMELESS", since TPC +# does not want tracks for such storms. + +grep "${old_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${current_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} +grep "${future_str}" ${archsyndir}/syndat_tcvitals.${CENT}${syy} | \ + grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ + >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-03---for genesis tc vitals-------------------- +#export COMINgenvit=/global/save/wx20jp/genesis_vital_${syyyy} +#grep "${current_str}" ${COMINgenvit}/gen_tc.vitals.gfs.gfso.2012 | \ +# grep -v TEST | awk 'substr($0,6,1) !~ /8/ {print $0}' \ +# >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + +#PRODTEST# +#PRODTEST# Use the next couple lines to test the tracker on the SP. +#PRODTEST# These next couple lines use data from a test TC Vitals file that +#PRODTEST# I generate. When you are ready to test this system, call me and +#PRODTEST# I'll create one for the current day, and then uncomment the next +#PRODTEST# couple lines in order to access the test vitals file. +# +#ttrkdir=/global/save/wx20tm/trak/prod/data +#ttrkdir=/global/save/wx20tm/trak/para/scripts +#grep "${current_str}" ${ttrkdir}/tcvit.01l >>${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} + + +# IMPORTANT: When "cat-ing" these files, make sure that the vitals +# files from the "raw" TC vitals files are first in order and Steve's +# TC vitals files second. This is because Steve's vitals file has +# been error-checked, so if we have a duplicate tc vitals record in +# these 2 files (very likely), program supvit.x below will +# only take the last vitals record listed for a particular storm in +# the vitals file (all previous duplicates are ignored, and Steve's +# error-checked vitals records are kept). + +cat ${TRKDATA}/tmprawvit.${atcfout}.${PDY}${cyc} ${TRKDATA}/tmpsynvit.${atcfout}.${PDY}${cyc} \ + >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# If we are doing the processing for the GFDL model, then we want +# to further cut down on which vitals we allow into this run of the +# tracker. The reason is that this program will be called from +# each individual run for a storm, so the grib files will be +# specific to each storm. So if 4 storms are being run at a +# particular cycle, then this script is run 4 separate times from +# within the GFDL_POST job. + +#if [ ${cmodel} = 'gfdl' ]; then +## grep -i ${stormid} ${COMIN}/${ATCFNAME}.vitals.${syy}${smm}${sdd}${shh} >${TRKDATA}/tmpvit +## mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#echo " " +#echo "!!! ERROR: TAKE THE COMMENTS OUT OF HERE BEFORE IMPLEMENTATION AAAA" +#echo "!!! AND TAKE OUT THESE NEXT 2 LINES AS WELL............................" +#echo " " +#grep -i ${stormid} ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/tmpvit +#mv ${TRKDATA}/tmpvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +#fi + +#--------------------------------------------------------------# +# Now run a fortran program that will read all the TC vitals +# records for the current dtg and the dtg from 6h ago, and +# sort out any duplicates. If the program finds a storm that +# was included in the vitals file 6h ago but not for the current +# dtg, this program updates the 6h-old first guess position +# and puts these updated records as well as the records from +# the current dtg into a temporary vitals file. It is this +# temporary vitals file that is then used as the input for the +# tracking program. +#--------------------------------------------------------------# + +oldymdh=` ${NDATE:?} -${vit_incr} ${PDY}${cyc}` +oldyy=`echo ${oldymdh} | cut -c3-4` +oldmm=`echo ${oldymdh} | cut -c5-6` +olddd=`echo ${oldymdh} | cut -c7-8` +oldhh=`echo ${oldymdh} | cut -c9-10` +oldymd=${oldyy}${oldmm}${olddd} + +futureymdh=` ${NDATE:?} 6 ${PDY}${cyc}` +futureyy=`echo ${futureymdh} | cut -c3-4` +futuremm=`echo ${futureymdh} | cut -c5-6` +futuredd=`echo ${futureymdh} | cut -c7-8` +futurehh=`echo ${futureymdh} | cut -c9-10` +futureymd=${futureyy}${futuremm}${futuredd} + +echo "&datenowin dnow%yy=${syy}, dnow%mm=${smm}," >${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&dateoldin dold%yy=${oldyy}, dold%mm=${oldmm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dold%dd=${olddd}, dold%hh=${oldhh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&datefuturein dfuture%yy=${futureyy}, dfuture%mm=${futuremm}," >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo " dfuture%dd=${futuredd}, dfuture%hh=${futurehh}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} +echo "&hourinfo vit_hr_incr=${vit_incr}/" >>${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period." + echo "!!! File ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} is empty." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi + +fi + +# For tcgen cases, filter to use only vitals from the ocean +# basin of interest.... +#J.Peng----2012-04-13------------------------------------- +#if [ ${trkrtype} = 'tcgen' ] +# then + +# if [ ${numvitrecs} -gt 0 ] +# then + +# fullvitfile=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# cp $fullvitfile ${TRKDATA}/vitals.all_basins.${atcfout}.${PDY}${cyc} +# basin=` echo $regtype | cut -c1-2` + +# if [ ${basin} = 'al' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "L") print $0}' | +# >${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_al_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'ep' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "E") print $0}' | +# >${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_ep_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi +# if [ ${basin} = 'wp' ]; then +# cat $fullvitfile | awk '{if (substr($0,8,1) == "W") print $0}' | +# >${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} +# cp ${TRKDATA}/vitals.tcgen_wp_only.${atcfout}.${PDY}${cyc} \ +# ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} +# fi + +# cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +# fi + +#fi +#J.Peng----2012-04-13------------------------------------- + +# - - - - - - - - - - - - - +# Before running the program to read, sort and update the vitals, +# first run the vitals through some awk logic, the purpose of +# which is to convert all the 2-digit years into 4-digit years. +# Beginning 4/21/99, NHC and JTWC will begin sending the vitals +# with 4-digit years, however it is unknown when other global +# forecasting centers will begin using 4-digit years, thus we +# need the following logic to ensure that all the vitals going +# into supvit.f have uniform, 4-digit years in their records. +# +# 1/8/2000: sed code added by Tim Marchok due to the fact that +# some of the vitals were getting past the syndata/qctropcy +# error-checking with a colon in them; the colon appeared +# in the character immediately to the left of the date, which +# was messing up the "(length($4) == 8)" statement logic. +# - - - - - - - - - - - - - + +sed -e "s/\:/ /g" ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} > ${TRKDATA}/tempvit +mv ${TRKDATA}/tempvit ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +awk ' +{ + yycheck = substr($0,20,2) + if ((yycheck == 20 || yycheck == 19) && (length($4) == 8)) { + printf ("%s\n",$0) + } + else { + if (yycheck >= 0 && yycheck <= 50) { + printf ("%s20%s\n",substr($0,1,19),substr($0,20)) + } + else { + printf ("%s19%s\n",substr($0,1,19),substr($0,20)) + } + } +} ' ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} >${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 + +mv ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc}.y4 ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + +#Added by J.Peng----2012-04-12---------------------------------------------- +numvitrecs=`cat ${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} | wc -l` +#----------------------------------------------------------------- +#J.Peng----2012-05-04--do not need this for TCvitals---- +#numvitrecs=0 + +if [ ${numvitrecs} -gt 0 ] +then + + export pgm=supvit_g2 + . prep_step + + # Input file + export FORT31=${TRKDATA}/vitals.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_g2 <${TRKDATA}/suv_input.${atcfout}.${PDY}${cyc} + suvrcc=$? + + if [ ${suvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit.x, " + echo "!!! which is the program that updates the TC Vitals file." + echo "!!! Return code from supvit.x = ${suvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING supvit_g2 IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else +#Added by J.Peng----2012-04-12---------------------------------------------- + rm -f ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + + touch ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#----------------------------------------------------------------- +# In this section, check to see if the user requested the use of +# operational TC vitals records for the initial time only. This +# option might be used for a retrospective medium range forecast +# in which the user wants to initialize with the storms that are +# currently there, but then let the model do its own thing for +# the next 10 or 14 days.... + + +if [ ${USE_OPER_VITALS} = 'INIT_ONLY' ]; then + + if [ ${init_flag} = 'yes' ]; then + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are currently at." + echo " " + set -x + else + set +x + echo " " + echo "NOTE: User has requested that operational historical TC vitals be used," + echo " but only for the initial time, which we are now *PAST*." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + fi + +elif [ ${USE_OPER_VITALS} = 'NO' ]; then + + set +x + echo " " + echo "NOTE: User has requested that historical vitals not be used...." + echo " " + set -x + >${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} + +fi + +#------------------------------------------------------------------# +# Now select all storms to be processed, that is, process every +# storm that's listed in the updated vitals file for the current +# forecast hour. If there are no storms for the current time, +# then exit. +#------------------------------------------------------------------# + +numvitrecs=`cat ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` +if [ ${numvitrecs} -eq 0 ] +then + if [ ${trkrtype} = 'tracker' ] + then + set +x + echo " " + echo "!!! WARNING -- There are no vitals records for this time period " + echo "!!! in the UPDATED vitals file." + echo "!!! It could just be that there are no storms for the current" + echo "!!! time. You may wish to check the date and submit this job again..." + echo " " + set -x + exit 0 + fi +fi + +set +x +echo " " +echo " *--------------------------------*" +echo " | STORM SELECTION |" +echo " *--------------------------------*" +echo " " +set -x + +ict=1 +while [ $ict -le 15 ] +do + stormflag[${ict}]=3 + let ict=ict+1 +done + +dtg_current="${symd} ${cyc}00" +stormmax=` grep "${dtg_current}" ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +if [ ${stormmax} -gt 15 ] +then + stormmax=15 +fi + +sct=1 +while [ ${sct} -le ${stormmax} ] +do + stormflag[${sct}]=1 + let sct=sct+1 +done + + +#---------------------------------------------------------------# +# +# -------- "Genesis" Vitals processing -------- +# +# May 2006: This entire genesis tracking system is being +# upgraded to more comprehensively track and categorize storms. +# One thing that has been missing from the tracking system is +# the ability to keep track of storms from one analysis cycle +# to the next. That is, the current system has been very +# effective at tracking systems within a forecast, but we have +# no methods in place for keeping track of storms across +# difference initial times. For example, if we are running +# the tracker on today's 00z GFS analysis, we will get a +# position for various storms at the analysis time. But then +# if we go ahead and run again at 06z, we have no way of +# telling the tracker that we know about the 00z position of +# this storm. We now address that problem by creating +# "genesis" vitals, that is, when a storm is found at an +# analysis time, we not only produce "atcfunix" output to +# detail the track & intensity of a found storm, but we also +# produce a vitals record that will be used for the next +# run of the tracker script. These "genesis vitals" records +# will be of the format: +# +# YYYYMMDDHH_AAAH_LLLLX_TYP +# +# Where: +# +# YYYYMMDDHH = Date the storm was FIRST identified +# by the tracker. +# AAA = Abs(Latitude) * 10; integer value +# H = 'N' for norther hem, 'S' for southern hem +# LLLL = Abs(Longitude) * 10; integer value +# X = 'E' for eastern hem, 'W' for western hem +# TYP = Tropical cyclone storm id if this is a +# tropical cyclone (e.g., "12L", or "09W", etc). +# If this is one that the tracker instead "Found +# On the Fly (FOF)", we simply put those three +# "FOF" characters in there. +#J.Peng----2012-04-12-------- +genvitfile=${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} +touch ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + +if [ -f $genvitfile ]; then + d6ago_ymdh=` ${NDATE:?} -6 ${PDY}${cyc}` + d6ago_4ymd=` echo ${d6ago_ymdh} | cut -c1-8` + d6ago_ymd=` echo ${d6ago_ymdh} | cut -c3-8` + d6ago_hh=` echo ${d6ago_ymdh} | cut -c9-10` + d6ago_str="${d6ago_ymd} ${d6ago_hh}00" + + d6ahead_ymdh=` ${NDATE:?} 6 ${PDY}${cyc}` + d6ahead_4ymd=` echo ${d6ahead_ymdh} | cut -c1-8` + d6ahead_ymd=` echo ${d6ahead_ymdh} | cut -c3-8` + d6ahead_hh=` echo ${d6ahead_ymdh} | cut -c9-10` + d6ahead_str="${d6ahead_ymd} ${d6ahead_hh}00" + + set +x + echo " " + echo " d6ago_str= --->${d6ago_str}<---" + echo " current_str= --->${current_str}<---" + echo " d6ahead_str= --->${d6ahead_str}<---" + echo " " + #echo " Listing and contents of ${genvitfile} follow " + #echo " for the times 6h ago, current and 6h ahead:" + #echo " " + set -x + + #J.Peng----2012-04-11----- + ##ls -la ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + ##cat ${COMINgenvit}/genesis.vitals.${atcfout}.${CENT}${syy} + #ls -la ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #cat ${COMINgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + + grep "${d6ago_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${current_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + grep "${d6ahead_str}" ${genvitfile} >>${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + num_gen_vits=`cat ${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} | wc -l` +else + echo "WARNING: Genesis Vitals from previous run(s) not found!" +fi + +echo "&datenowin dnow%yy=${syyyy}, dnow%mm=${smm}," >${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " dnow%dd=${sdd}, dnow%hh=${cyc}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6agoin d6ago%yy=${syyyy6}, d6ago%mm=${smm6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ago%dd=${sdd6}, d6ago%hh=${shh6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo "&date6aheadin d6ahead%yy=${syyyyp6}, d6ahead%mm=${smmp6}," >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} +echo " d6ahead%dd=${sddp6}, d6ahead%hh=${shhp6}/" >>${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + +#J.Peng----2012-05-04--- need this for TC vitals------------------ +num_gen_vits=0 + +if [ ${num_gen_vits} -gt 0 ] +then + export pgm=supvit_gen + . prep_step + + # Input file + export FORT31=${TRKDATA}/genvitals.${cmodel}.${atcfout}.${PDY}${cyc} + + # Output file + export FORT51=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + + msg="$pgm start for $atcfout at ${cyc}z" + postmsg "$jlogfile" "$msg" + + ${EXECens_tracker}/supvit_gen <${TRKDATA}/sgv_input.${atcfout}.${PDY}${cyc} + sgvrcc=$? + + if [ ${sgvrcc} -eq 0 ] + then + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" + else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running supvit_gen, " + echo "!!! which is the program that updates the genesis vitals file." + echo "!!! Return code from supvit_gen = ${sgvrcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING SUPVIT_GEN IN TRACKER SCRIPT- ABNORMAL EXIT" + fi + +else + rm -f ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} + touch ${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +fi + + +#-----------------------------------------------------------------# +# +# ------ CUT APART INPUT GRIB FILES ------- +# +# For the selected model, cut apart the GRIB input files in order +# to pull out only the variables that we need for the tracker. +# Put these selected variables from all forecast hours into 1 big +# GRIB file that we'll use as input for the tracker. +# +# The wgrib utility (/nwprod/util/exec/wgrib) is used to cut out +# the needed parms for the GFS, MRF, GDAS, UKMET and NOGAPS files. +# The utility /nwprod/util/exec/copygb is used to interpolate the +# NGM (polar stereographic) and NAM (Lambert Conformal) data from +# their grids onto lat/lon grids. Note that while the lat/lon +# grid that I specify overlaps into areas that don't have any data +# on the original grid, Mark Iredell wrote the copygb software so +# that it will mask such "no-data" points with a bitmap (just be +# sure to check the lbms in your fortran program after getgb). +#-----------------------------------------------------------------# + +set +x +echo " " +echo " -----------------------------------------" +echo " NOW CUTTING APART INPUT GRIB FILES TO " +echo " CREATE 1 BIG GRIB INPUT FILE " +echo " -----------------------------------------" +echo " " +set -x + +regflag=`grep NHC ${TRKDATA}/vitals.upd.${atcfout}.${PDY}${cyc} | wc -l` + +# ------------------------------ +# Process GFS, if selected +# ------------------------------ + +if [ ${model} -eq 1 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/gfsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/gfsgribfile.${PDY}${cyc} + fi + + rm -f ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f* + rm -f ${TRKDATA}/gfsgribfile.${PDY}${cyc}.f* + >${TRKDATA}/gfsgribfile.${PDY}${cyc} + + set +x + echo " " + echo "Time before gfs wgrib loop is `date`" + echo " " + set -x + + for fhour in ${fcsthrs} + do + + if [ ! -s ${gfsdir}/${gfsgfile}${fhour} ] + then + set +x + echo " " + echo "FATAL ERROR: GFS File missing: ${gfsdir}/${gfsgfile}${fhour}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + err_exit "FAILED ${jobid} - MISSING GFS FILE IN TRACKER SCRIPT - ABNORMAL EXIT" + fi + + gfile=${gfsdir}/${gfsgfile}${fhour} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + + gfs_master_file=${TRKDATA}/master.gfsgribfile.${PDY}${cyc}.f${fhour} + gfs_cat_file=${TRKDATA}/gfsgribfile.${PDY}${cyc} + cat ${gfs_master_file} >>${gfs_cat_file} + + done + + ${GRB2INDEX:?} ${TRKDATA}/gfsgribfile.${PDY}${cyc} ${TRKDATA}/gfsixfile.${PDY}${cyc} + export err=$?; err_chk + +# Now run through the work to get data for phase checking.... +# -------------------------------------------- + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "BEGIN: 300-500hPa T-mean and 200-850hPa U-shear" + echo " for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + ffile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + + gparm=11 + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + tavefile=${TRKDATA}/${cmodel}.tave.${PDY}${cyc}.f${fhour} + cat ${tavefile} >>${catfile} + +#J.Peng----2012-04-18---added for U-shear----- + gparm=33 + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}.ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-04-18---added for U-shear----- + + set +x + echo " " + echo "END: 300-500hPa T-mean and 200-850hPa U-shear" + echo " for cmodel= $cmodel and fhour= $fhour after = `date`" + echo " " + set -x + + done + cat ${catfile} >>${ffile} + + fi + + gfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/gfsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gfile} ${ifile} + export err=$?; err_chk + + gribfile=${TRKDATA}/gfsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/gfsixfile.${PDY}${cyc} + +fi + +# ------------------------------ +# Process NAVGEM, if selected +# ------------------------------ + +if [ ${model} -eq 7 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} ] + then + set +x + echo " " + echo "FATAL ERROR: NAVGEM File missing ${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + if [ "$DCOM_STATUS" = "data of opportunity" ]; then + echo "${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile}" >> ${DATA}/missing_ngps.txt + exit + else + err_exit "MISSING NAVGEM FILE at $0:$LINENO" + fi + fi + + gfile=${ngpsdir}/${ngpsgfile}${fhour}${ngemgfile} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/ngpsgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/ngpsgribfile.${PDY}${cyc} + + done + +# Now run through the work to get data for phase checking.... + ${GRB2INDEX:?} ${TRKDATA}/ngpsgribfile.${PDY}${cyc} ${TRKDATA}/ngpsixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for GPH AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/ngps_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for T AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/ngpsgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/ngpsixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk +fi + +# ------------------------------ +# Process CMC-deterministic forecast, if selected +# ------------------------------ + +if [ ${model} -eq 15 ] +then + + if [ $loopnum -eq 1 ] + then + + if [ -s ${TRKDATA}/cmcgribfile.${PDY}${cyc} ] + then + rm ${TRKDATA}/cmcgribfile.${PDY}${cyc} + fi + + for fhour in ${fcsthrs} + do + + if [ ! -s ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} ] + then + set +x + echo " " + echo "FATAL ERROR: CMC File missing ${cmcdir}/${cmcgfile}${fhour}${cmcgfile2}" + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo " " + set -x + fi + + gfile=${cmcdir}/${cmcgfile}${fhour}${cmcgfile2} + ${WGRIB2:?} $gfile -match "$PARMlist" -grib ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} + cat ${TRKDATA}/cmcgribfile.${PDY}${cyc}.f${fhour} >> ${TRKDATA}/cmcgribfile.${PDY}${cyc} + + done +# J.Peng--04-05-2017--changing grid from 180E to 0E------------ +# wgrib2 junk -small_grib 0:359.76 -90:90 junk2 +# cp ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/junk +# rm -f ${TRKDATA}/cmcgribfile.${PDY}${cyc} +# ${WGRIB2} ${TRKDATA}/junk -small_grib 0:359.76 -90:90 ${TRKDATA}/junk2 +# cp ${TRKDATA}/junk2 ${TRKDATA}/cmcgribfile.${PDY}${cyc} + +# Now run through the work to get data for phase checking.... + + ${GRB2INDEX:?} ${TRKDATA}/cmcgribfile.${PDY}${cyc} ${TRKDATA}/cmcixfile.${PDY}${cyc} + export err=$?; err_chk + + if [ ${PHASEFLAG} = 'y' ]; then + catfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.catfile + >${catfile} + + for fhour in ${fcsthrs} + do + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour before = `date`" + echo " " + set -x + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + +# ---------------------------------------------------- +# First, interpolate height data to get data from +# 300 to 900 mb, every 50 mb.... + + gparm=7 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc}.z + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_hgt_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for GPH at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for GPH AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now interpolate temperature data to get data from +# 300 to 500 mb, every 50 mb.... + + gparm=11 + + . prep_step + + # Input files + namelist=${TRKDATA}/vint_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${gfile} + export FORT16=${FIXens_tracker}/cmc_tmp_levs.txt + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ${EXECens_tracker}/vint_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to vint_g2.x for T at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -vint_g2.x- for T AT LINE $LINENO - ABNORMAL EXIT" + fi + +# ---------------------------------------------------- +# Now average the temperature data that we just +# interpolated to get the mean 300-500 mb temperature... + + gparm=11 + ffile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour} + ifile=${TRKDATA}/${cmodel}.${PDY}${cyc}.t.f${fhour}.i + ${GRB2INDEX:?} ${ffile} ${ifile} + export err=$?; err_chk + + . prep_step + + # Input files + namelist=${TRKDATA}/tave_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + + # Output file + export FORT51=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/tave_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to tave_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -tave_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + zfile=${TRKDATA}/${cmodel}.${PDY}${cyc}.z.f${fhour} + tavefile=${TRKDATA}/${cmodel}_tave.${PDY}${cyc}.f${fhour} + cat ${zfile} ${tavefile} >>${catfile} + +#J.Peng----2012-06-04---added for U-shear----- + gparm=33 + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/ushear_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparm=${gparm}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT51=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + + ${EXECens_tracker}/ushear_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to ushear_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -ushear_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + + ushearfile=${TRKDATA}/${cmodel}_ushear.${PDY}${cyc}.f${fhour} + cat ${ushearfile} >>${catfile} +#J.Peng----2012-06-04---added for U-shear----- +# +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + gparmq=7 + gparmt=11 + + ffile=${gfile} + ifile=${TRKDATA}/cmcixfile.${PDY}${cyc} + + . prep_step + + # Input files + namelist=${TRKDATA}/rh_input.${PDY}${cyc} + echo "&timein ifcsthour=${fhour}," >${namelist} + echo " iparmq=${gparmq}," >>${namelist} + echo " iparmt=${gparmt}," >>${namelist} + echo " gribver=${gribver}," >>${namelist} + echo " g2_jpdtn=${g2_jpdtn}," >>${namelist} + echo " g2_model=${model}/" >>${namelist} + export FORT11=${ffile} + export FORT31=${ifile} + export FORT16=${FIXens_tracker}/cmc_rh_levs.txt + + # Output file + export FORT51=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + ${EXECens_tracker}/rhum_g2.x <${namelist} + rcc=$? + + if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to rhum_g2.x at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "FAILED ${jobid} -rhum_g2.x- AT LINE $LINENO - ABNORMAL EXIT" + fi + rhfile=${TRKDATA}/${cmodel}_rh.${PDY}${cyc}.f${fhour} + cat ${rhfile} >>${catfile} +#J.Peng----2017-04-12---added for RH calculation with CMC-SPFH + + set +x + echo " " + echo "Date in interpolation for fhour= $fhour after = `date`" + echo " " + set -x + done + fi + fi + + gfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + if [ ${PHASEFLAG} = 'y' ]; then + cat ${catfile} >>${gfile} + fi + + gribfile=${TRKDATA}/cmcgribfile.${PDY}${cyc} + ixfile=${TRKDATA}/cmcixfile.${PDY}${cyc} + ${GRB2INDEX:?} ${gribfile} ${ixfile} + export err=$?; err_chk +fi + +#------------------------------------------------------------------------# +# Now run the tracker # +#------------------------------------------------------------------------# +set +x +echo " " +echo " -----------------------------------------------" +echo " NOW EXECUTING TRACKER......" +echo " -----------------------------------------------" +echo " " +set -x + +ist=1 +while [ $ist -le 15 ] +do + if [ ${stormflag[${ist}]} -ne 1 ] + then + set +x; echo "Storm number $ist NOT selected for processing"; set -x + else + set +x; echo "Storm number $ist IS selected for processing...."; set -x + fi + let ist=ist+1 +done + +if [ ! -s ${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} ] +then + for fhr in $fcsthrs; do + if [ $fhr -ne 99 ]; then + last_fcst_hour=$fhr + fi + done + echo ${last_fcst_hour} >${TRKDATA}/last_fcst_hour.${atcfout}.${PDY}${cyc} +fi + +namelist=${TRKDATA}/input.${atcfout}.${PDY}${cyc} +ATCFNAME=` echo "${atcfname}" | tr '[a-z]' '[A-Z]'` + +export atcfymdh=${scc}${syy}${smm}${sdd}${shh} + +#------07-26-2019----creating the fix-file for GFS tracking ----- +if [ ${model} -eq 1 ]; then +. prep_step + +# Input files +namelist_4_fix=${TRKDATA}/namelist_4_fix_input.${PDY}${cyc} +echo "&timein maxhrs=${fcstlen}," >${namelist_4_fix} +echo " dthrs=${vit_incr}/" >>${namelist_4_fix} + +# Output file +export FORT10=${TRKDATA}/${cmodel}.genesis_leadtimes + +${EXECens_tracker}/leadtime <${namelist_4_fix} +rcc=$? + +if [ $rcc -ne 0 ]; then + set +x + echo " " + echo "FATAL ERROR in call to leadtime at fhour= $fhour" + echo "rcc= $rcc EXITING.... " + echo " " + set -x + err_exit "leadtime - ERROR AT extrkr_tcv_gfs.sh LINE $LINENO" +fi + +fi +#------07-26-2019----creating the fix-file for GFS tracking ----- + +#contour_interval=100.0 +#write_vit=y +#want_oci=.TRUE. + +echo "&datein inp%bcc=${scc},inp%byy=${syy},inp%bmm=${smm}," >${namelist} +echo " inp%bdd=${sdd},inp%bhh=${shh},inp%model=${model}," >>${namelist} +echo " inp%modtyp='${modtyp}'," >>${namelist} +echo " inp%lt_units='${lead_time_units}'," >>${namelist} +echo " inp%file_seq='${file_sequence}'," >>${namelist} +echo " inp%nesttyp='${nest_type}'/" >>${namelist} + +echo "&atcfinfo atcfnum=${atcfnum},atcfname='${ATCFNAME}'," >>${namelist} +echo " atcfymdh=${atcfymdh},atcffreq=${atcffreq}/" >>${namelist} + +echo "&trackerinfo trkrinfo%westbd=${trkrwbd}," >>${namelist} +echo " trkrinfo%eastbd=${trkrebd}," >>${namelist} +echo " trkrinfo%northbd=${trkrnbd}," >>${namelist} +echo " trkrinfo%southbd=${trkrsbd}," >>${namelist} +echo " trkrinfo%type='${trkrtype}'," >>${namelist} +echo " trkrinfo%mslpthresh=${mslpthresh}," >>${namelist} +echo " trkrinfo%use_backup_mslp_grad_check='${use_backup_mslp_grad_check}'," >>${namelist} +echo " trkrinfo%max_mslp_850=${max_mslp_850}," >>${namelist} +echo " trkrinfo%v850thresh=${v850thresh}," >>${namelist} +echo " trkrinfo%use_backup_850_vt_check='${use_backup_850_vt_check}'," >>${namelist} +echo " trkrinfo%gridtype='${modtyp}'," >>${namelist} +echo " trkrinfo%enable_timing=1," >>${namelist} +echo " trkrinfo%contint=${contour_interval}," >>${namelist} +echo " trkrinfo%want_oci=${want_oci}," >>${namelist} +echo " trkrinfo%out_vit='${write_vit}'," >>${namelist} +echo " trkrinfo%use_land_mask='${use_land_mask}'," >>${namelist} +echo " trkrinfo%inp_data_type='${inp_data_type}'," >>${namelist} +echo " trkrinfo%gribver=${gribver}," >>${namelist} +echo " trkrinfo%g2_jpdtn=${g2_jpdtn}," >>${namelist} +echo " trkrinfo%g2_mslp_parm_id=${g2_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_mslp_parm_id=${g1_mslp_parm_id}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_typ=${g1_sfcwind_lev_typ}," >>${namelist} +echo " trkrinfo%g1_sfcwind_lev_val=${g1_sfcwind_lev_val}/" >>${namelist} + +echo "&phaseinfo phaseflag='${PHASEFLAG}'," >>${namelist} +echo " phasescheme='${PHASE_SCHEME}'," >>${namelist} +echo " wcore_depth=${WCORE_DEPTH}/" >>${namelist} + +echo "&structinfo structflag='${STRUCTFLAG}'," >>${namelist} +echo " ikeflag='${IKEFLAG}'/" >>${namelist} + +echo "&fnameinfo gmodname='${atcfname}'," >>${namelist} +echo " rundescr='${rundescr}'," >>${namelist} +echo " atcfdescr='${atcfdescr}'/" >>${namelist} + +echo "&waitinfo use_waitfor='n'," >>${namelist} +echo " wait_min_age=10," >>${namelist} +echo " wait_min_size=100," >>${namelist} +echo " wait_max_wait=1800," >>${namelist} +echo " wait_sleeptime=5," >>${namelist} +echo " per_fcst_command=''/" >>${namelist} + +echo "&netcdflist netcdfinfo%num_netcdf_vars=${ncdf_num_netcdf_vars}," >>${namelist} +echo " netcdfinfo%netcdf_filename='${netcdffile}'," >>${namelist} +echo " netcdfinfo%rv850name='${ncdf_rv850name}'," >>${namelist} +echo " netcdfinfo%rv700name='${ncdf_rv700name}'," >>${namelist} +echo " netcdfinfo%u850name='${ncdf_u850name}'," >>${namelist} +echo " netcdfinfo%v850name='${ncdf_v850name}'," >>${namelist} +echo " netcdfinfo%u700name='${ncdf_u700name}'," >>${namelist} +echo " netcdfinfo%v700name='${ncdf_v700name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%mslpname='${ncdf_mslpname}'," >>${namelist} +echo " netcdfinfo%usfcname='${ncdf_usfcname}'," >>${namelist} +echo " netcdfinfo%vsfcname='${ncdf_vsfcname}'," >>${namelist} +echo " netcdfinfo%u500name='${ncdf_u500name}'," >>${namelist} +echo " netcdfinfo%v500name='${ncdf_v500name}'," >>${namelist} +echo " netcdfinfo%tmean_300_500_name='${ncdf_tmean_300_500_name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z200name='${ncdf_z200name}'," >>${namelist} +echo " netcdfinfo%lmaskname='${ncdf_lmaskname}'," >>${namelist} +echo " netcdfinfo%z900name='${ncdf_z900name}'," >>${namelist} +echo " netcdfinfo%z850name='${ncdf_z850name}'," >>${namelist} +echo " netcdfinfo%z800name='${ncdf_z800name}'," >>${namelist} +echo " netcdfinfo%z750name='${ncdf_z750name}'," >>${namelist} +echo " netcdfinfo%z700name='${ncdf_z700name}'," >>${namelist} +echo " netcdfinfo%z650name='${ncdf_z650name}'," >>${namelist} +echo " netcdfinfo%z600name='${ncdf_z600name}'," >>${namelist} +echo " netcdfinfo%z550name='${ncdf_z550name}'," >>${namelist} +echo " netcdfinfo%z500name='${ncdf_z500name}'," >>${namelist} +echo " netcdfinfo%z450name='${ncdf_z450name}'," >>${namelist} +echo " netcdfinfo%z400name='${ncdf_z400name}'," >>${namelist} +echo " netcdfinfo%z350name='${ncdf_z350name}'," >>${namelist} +echo " netcdfinfo%z300name='${ncdf_z300name}'," >>${namelist} +echo " netcdfinfo%time_name='${ncdf_time_name}'," >>${namelist} +echo " netcdfinfo%lon_name='${ncdf_lon_name}'," >>${namelist} +echo " netcdfinfo%lat_name='${ncdf_lat_name}'," >>${namelist} +echo " netcdfinfo%time_units='${ncdf_time_units}'/" >>${namelist} + +echo "&parmpreflist user_wants_to_track_zeta850='${user_wants_to_track_zeta850}'," >>${namelist} +echo " user_wants_to_track_zeta700='${user_wants_to_track_zeta700}'," >>${namelist} +echo " user_wants_to_track_wcirc850='${user_wants_to_track_wcirc850}'," >>${namelist} +echo " user_wants_to_track_wcirc700='${user_wants_to_track_wcirc700}'," >>${namelist} +echo " user_wants_to_track_gph850='${user_wants_to_track_gph850}'," >>${namelist} +echo " user_wants_to_track_gph700='${user_wants_to_track_gph700}'," >>${namelist} +echo " user_wants_to_track_mslp='${user_wants_to_track_mslp}'," >>${namelist} +echo " user_wants_to_track_wcircsfc='${user_wants_to_track_wcircsfc}'," >>${namelist} +echo " user_wants_to_track_zetasfc='${user_wants_to_track_zetasfc}'," >>${namelist} +echo " user_wants_to_track_thick500850='${user_wants_to_track_thick500850}'," >>${namelist} +echo " user_wants_to_track_thick200500='${user_wants_to_track_thick200500}'," >>${namelist} +echo " user_wants_to_track_thick200850='${user_wants_to_track_thick200850}'/" >>${namelist} + +echo "&verbose verb=3,verb_g2=0/" >>${namelist} + +export pgm=gettrk_gen_gfs +. prep_step + +export FORT11=${gribfile} +export FORT12=${TRKDATA}/vitals.upd.${atcfout}.${PDY}${shh} +export FORT14=${TRKDATA}/genvitals.upd.${cmodel}.${atcfout}.${PDY}${cyc} +if [ ${model} -eq 1 ]; then + export FORT15=${TRKDATA}/${cmodel}.genesis_leadtimes +else + export FORT15=${FIXens_tracker}/${cmodel}.genesis_leadtimes +fi + +export FORT31=${ixfile} + +if [ ${trkrtype} = 'tracker' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT61=${TRKDATA}/trak.${atcfout}.all.${stormenv}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${stormenv}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${stormenv}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${stormenv}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${stormenv}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${stormenv}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${stormenv}.${PDY}${cyc} + else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${PDY}${cyc} + fi +else + export FORT61=${TRKDATA}/trak.${atcfout}.all.${regtype}.${PDY}${cyc} + export FORT62=${TRKDATA}/trak.${atcfout}.atcf.${regtype}.${PDY}${cyc} + export FORT63=${TRKDATA}/trak.${atcfout}.radii.${regtype}.${PDY}${cyc} + export FORT64=${TRKDATA}/trak.${atcfout}.atcfunix.${regtype}.${PDY}${cyc} + export FORT66=${TRKDATA}/trak.${atcfout}.atcf_gen.${regtype}.${PDY}${cyc} + export FORT68=${TRKDATA}/trak.${atcfout}.atcf_sink.${regtype}.${PDY}${cyc} + export FORT69=${TRKDATA}/trak.${atcfout}.atcf_hfip.${regtype}.${PDY}${cyc} +fi + +if [ ${atcfname} = 'aear' ] +then + export FORT65=${TRKDATA}/trak.${atcfout}.initvitl.${PDY}${cyc} +fi + +if [ ${write_vit} = 'y' ] +then + export FORT67=${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} +fi + +if [ ${PHASEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${stormenv}.${PDY}${cyc} + else + export FORT71=${TRKDATA}/trak.${atcfout}.cps_parms.${PDY}${cyc} + fi +fi + +if [ ${STRUCTFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${stormenv}.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${stormenv}.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${stormenv}.${PDY}${cyc} + else + export FORT72=${TRKDATA}/trak.${atcfout}.structure.${PDY}${cyc} + export FORT73=${TRKDATA}/trak.${atcfout}.fractwind.${PDY}${cyc} + export FORT76=${TRKDATA}/trak.${atcfout}.pdfwind.${PDY}${cyc} + fi +fi + +if [ ${IKEFLAG} = 'y' ]; then + if [ ${atcfout} = 'gfdt' -o ${atcfout} = 'gfdl' -o \ + ${atcfout} = 'hwrf' -o ${atcfout} = 'hwft' ]; then + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${stormenv}.${PDY}${cyc} + else + export FORT74=${TRKDATA}/trak.${atcfout}.ike.${PDY}${cyc} + fi +fi + +if [ ${trkrtype} = 'midlat' -o ${trkrtype} = 'tcgen' ]; then + export FORT77=${TRKDATA}/trkrmask.${atcfout}.${regtype}.${PDY}${cyc} +fi + +msg="$pgm start for $atcfout at ${cyc}z" +postmsg "$jlogfile" "$msg" + +set +x +echo "+++ TIMING: BEFORE gettrk ---> `date`" +set -x + +ulimit -c unlimited + +${EXECens_tracker}/gettrk_gen_gfs <${namelist} +gettrk_rcc=$? + +set +x +echo "+++ TIMING: AFTER gettrk ---> `date`" +set -x + +#--------------------------------------------------------------# +# Now copy the output track files to different directories +#--------------------------------------------------------------# + +if [ ${gettrk_rcc} -eq 0 ]; then + set +x + echo " " + echo " -----------------------------------------------" + echo " NOW COPYING OUTPUT TRACK FILES TO COM " + echo " -----------------------------------------------" + echo " " + set -x + + if [ -s ${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} ]; then + if [ "$SENDCOM" = 'YES' ]; then + cp ${TRKDATA}/output_genvitals.${atcfout}.${PDY}${shh} ${COMOUTgenvit:?}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #if [ "$SENDDBN" = 'YES' ]; then + # $DBNROOT/bin/dbn_alert ${cmodel^^} GENESIS $job ${COMOUTgenvit}/genesis.vitals.${cmodel}.${atcfout}.${CENT}${syy} + #fi + fi + fi + msg="$pgm end for $atcfout at ${cyc}z completed normally" + postmsg "$jlogfile" "$msg" +else + set +x + echo " " + echo "FATAL ERROR: An error occurred while running gettrk_gen_gfs, " + echo "!!! which is the program that actually gets the track." + echo "!!! Return code from gettrk_gen_gfs = ${gettrk_rcc}" + echo "!!! model= ${atcfout}, forecast initial time = ${PDY}${cyc}" + echo " " + set -x + err_exit "FAILED ${jobid} - ERROR RUNNING gettrk_gen_gfs IN TRACKER SCRIPT- ABNORMAL EXIT" +fi diff --git a/sorc/ens_tracker.v1.1.15.2/ush/extrkr_tcv_gfs.sh b/sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_gfs.sh_20200806 similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/extrkr_tcv_gfs.sh rename to sorc/ens_tracker.v1.1.15.4/ush/extrkr_tcv_gfs.sh_20200806 diff --git a/sorc/ens_tracker.v1.1.15.2/ush/sort_tracks.gen.sh b/sorc/ens_tracker.v1.1.15.4/ush/sort_tracks.gen.sh similarity index 100% rename from sorc/ens_tracker.v1.1.15.2/ush/sort_tracks.gen.sh rename to sorc/ens_tracker.v1.1.15.4/ush/sort_tracks.gen.sh diff --git a/sorc/gfs_post.fd_gsl/parm/postcntrl_gfs.xml b/sorc/gfs_post.fd_gsl/parm/postcntrl_gfs.xml index eb0c709b03..63a80c0771 100755 --- a/sorc/gfs_post.fd_gsl/parm/postcntrl_gfs.xml +++ b/sorc/gfs_post.fd_gsl/parm/postcntrl_gfs.xml @@ -91,7 +91,7 @@ - CLWMR_ON_ISOBARIC_SFC + CLMR_ON_ISOBARIC_SFC 5000. 7000. 10000. 12500. 15000. 17500. 20000. 22500. 25000. 27500. 30000. 32500. 35000. 37500. 40000. 42500. 45000. 47500. 50000. 52500. 55000. 57500. 60000. 62500. 65000. 67500. 70000. 72500. 75000. 77500. 80000. 82500. 85000. 87500. 90000. 92500. 95000. 97500. 100000. 6.0 @@ -1096,7 +1096,7 @@ - CLWMR_ON_HYBRID_LVL + CLMR_ON_HYBRID_LVL 1. 6.0 @@ -1194,6 +1194,11 @@ 4.0 + + LAND_FRAC + 3.0 + + diff --git a/sorc/gfs_post.fd_gsl/parm/postcntrl_gfs_two.xml b/sorc/gfs_post.fd_gsl/parm/postcntrl_gfs_two.xml index 9350b14bcd..092c0825a6 100755 --- a/sorc/gfs_post.fd_gsl/parm/postcntrl_gfs_two.xml +++ b/sorc/gfs_post.fd_gsl/parm/postcntrl_gfs_two.xml @@ -1255,6 +1255,11 @@ 4.0 + + FDNSSTMP_ON_SURFACE + 4.0 + + TSOIL_ON_DEPTH_BEL_LAND_SFC 2 2 2 2 diff --git a/sorc/gfs_post.fd_gsl/parm/postxconfig-NT-GFS-TWO.txt b/sorc/gfs_post.fd_gsl/parm/postxconfig-NT-GFS-TWO.txt index a74acdcc00..a87b89c9b5 100644 --- a/sorc/gfs_post.fd_gsl/parm/postxconfig-NT-GFS-TWO.txt +++ b/sorc/gfs_post.fd_gsl/parm/postxconfig-NT-GFS-TWO.txt @@ -1,5 +1,5 @@ 2 -111 +112 204 GFSPRS 0 @@ -7840,6 +7840,43 @@ surface ? ? ? +549 +FDNSSTMP_ON_SURFACE +? +1 +tmpl4_0 +FDNSSTMP +? +? +surface +0 +? +0 +? +? +0 +? +0 +? +? +? +0 +0.0 +0 +0.0 +? +0 +0.0 +0 +0.0 +1 +4.0 +0 +0 +0 +? +? +? 116 TSOIL_ON_DEPTH_BEL_LAND_SFC ? diff --git a/sorc/gfs_post.fd_gsl/parm/postxconfig-NT-GFS.txt b/sorc/gfs_post.fd_gsl/parm/postxconfig-NT-GFS.txt index 270ec7b62e..156ba9a0a1 100644 --- a/sorc/gfs_post.fd_gsl/parm/postxconfig-NT-GFS.txt +++ b/sorc/gfs_post.fd_gsl/parm/postxconfig-NT-GFS.txt @@ -1,5 +1,5 @@ 1 -203 +204 GFSPRS 0 ncep_nco @@ -387,11 +387,11 @@ isobaric_sfc ? ? 153 -CLWMR_ON_ISOBARIC_SFC +CLMR_ON_ISOBARIC_SFC ? 1 tmpl4_0 -CLWMR +CLMR ? ? isobaric_sfc @@ -579,7 +579,7 @@ tmpl4_0 REFC NCEP ? -entire_atmos +entire_atmos_single_lyr 0 ? 0 @@ -2577,7 +2577,7 @@ tmpl4_8 TCDC ? AVE -entire_atmos +entire_atmos_single_lyr 0 ? 0 @@ -6936,11 +6936,11 @@ hybrid_lvl ? ? 124 -CLWMR_ON_HYBRID_LVL +CLMR_ON_HYBRID_LVL ? 1 tmpl4_0 -CLWMR +CLMR ? ? hybrid_lvl @@ -7239,7 +7239,7 @@ tmpl4_0 TCDC ? ? -entire_atmos +entire_atmos_single_lyr 0 ? 0 @@ -7527,3 +7527,40 @@ spec_pres_above_grnd ? ? ? +996 +LAND_FRAC +? +1 +tmpl4_0 +LANDFRC +NCEP +? +surface +0 +? +0 +? +? +0 +? +0 +? +? +? +0 +0.0 +0 +0.0 +? +0 +0.0 +0 +0.0 +1 +3.0 +0 +0 +0 +? +? +? diff --git a/sorc/gfs_post.fd_gsl/ush/fv3gfs_downstream_nems.sh b/sorc/gfs_post.fd_gsl/ush/fv3gfs_downstream_nems.sh index bfd49aa7d4..b2f625de32 100755 --- a/sorc/gfs_post.fd_gsl/ush/fv3gfs_downstream_nems.sh +++ b/sorc/gfs_post.fd_gsl/ush/fv3gfs_downstream_nems.sh @@ -99,7 +99,7 @@ fi #----------------------------------------------------- #----------------------------------------------------- -if [ $machine = WCOSS -o $machine = WCOSS_C -o $machine = WCOSS_DELL_P3 -o $machine = HERA -o $machine = ORION -o $machine = JET ]; then +if [ $machine = WCOSS -o $machine = WCOSS_C -o $machine = WCOSS_DELL_P3 -o $machine = HERA -o $machine = ORION -o $machine = JET -o $machine = S4 ]; then #----------------------------------------------------- #----------------------------------------------------- export nset=1 @@ -173,7 +173,7 @@ date launcher=${APRUN_DWN:-"aprun -j 1 -n 24 -N 24 -d 1 cfp"} if [ $machine = WCOSS_C -o $machine = WCOSS_DELL_P3 ] ; then $launcher $MP_CMDFILE - elif [ $machine = HERA -o $machine = ORION ] ; then + elif [ $machine = HERA -o $machine = ORION -o $machine = JET -o $machine = S4 ] ; then if [ -s $DATA/poescript_srun ]; then rm -f $DATA/poescript_srun; fi touch $DATA/poescript_srun nm=0 diff --git a/sorc/ufs_model.fd_gsl/FV3/ccpp/physics/physics/mynnedmf_wrapper.F90 b/sorc/ufs_model.fd_gsl/FV3/ccpp/physics/physics/mynnedmf_wrapper.F90 new file mode 100644 index 0000000000..146b53bcf8 --- /dev/null +++ b/sorc/ufs_model.fd_gsl/FV3/ccpp/physics/physics/mynnedmf_wrapper.F90 @@ -0,0 +1,1110 @@ +!> \file mynnedmf_wrapper.F90 +!! This file contains all of the code related to running the MYNN +!! eddy-diffusivity mass-flux scheme. + +!>\ingroup gsd_mynn_edmf +!> The following references best describe the code within +!! Olson et al. (2019, NOAA Technical Memorandum) +!! Nakanishi and Niino (2009) \cite NAKANISHI_2009 + MODULE mynnedmf_wrapper + + contains + +!> \section arg_table_mynnedmf_wrapper_init Argument Table +!! \htmlinclude mynnedmf_wrapper_init.html +!! + subroutine mynnedmf_wrapper_init ( & + & con_cp, con_grav, con_rd, con_rv, & + & con_cpv, con_cliq, con_cice, con_rcp, & + & con_XLV, con_XLF, con_p608, con_ep2, & + & con_karman, con_t0c, & + & do_mynnedmf, lheatstrg, & + & errmsg, errflg ) + + use machine, only : kind_phys + use bl_mynn_common + + implicit none + + logical, intent(in) :: do_mynnedmf + logical, intent(in) :: lheatstrg + character(len=*), intent(out) :: errmsg + integer, intent(out) :: errflg + + real(kind=kind_phys),intent(in) :: con_xlv + real(kind=kind_phys),intent(in) :: con_xlf + real(kind=kind_phys),intent(in) :: con_rv + real(kind=kind_phys),intent(in) :: con_rd + real(kind=kind_phys),intent(in) :: con_ep2 + real(kind=kind_phys),intent(in) :: con_grav + real(kind=kind_phys),intent(in) :: con_cp + real(kind=kind_phys),intent(in) :: con_cpv + real(kind=kind_phys),intent(in) :: con_rcp + real(kind=kind_phys),intent(in) :: con_p608 + real(kind=kind_phys),intent(in) :: con_cliq + real(kind=kind_phys),intent(in) :: con_cice + real(kind=kind_phys),intent(in) :: con_karman + real(kind=kind_phys),intent(in) :: con_t0c + + ! Initialize CCPP error handling variables + errmsg = '' + errflg = 0 + + xlv = con_xlv + xlf = con_xlf + r_v = con_rv + r_d = con_rd + ep_2 = con_ep2 + grav = con_grav + cp = con_cp + cpv = con_cpv + rcp = con_rcp + p608 = con_p608 + cliq = con_cliq + cice = con_cice + karman = con_karman + t0c = con_t0c + + xls = xlv+xlf != 2.85E6 (J/kg) sublimation + rvovrd = r_v/r_d != 1.608 + ep_3 = 1.-ep_2 != 0.378 + gtr = grav/tref + rk = cp/r_d + tv0 = p608*tref + tv1 = (1.+p608)*tref + xlscp = (xlv+xlf)/cp + xlvcp = xlv/cp + g_inv = 1./grav + + ! Consistency checks + if (.not. do_mynnedmf) then + errmsg = 'Logic error: do_mynnedmf = .false.' + errflg = 1 + return + end if + + ! if (lheatstrg) then + ! errmsg = 'Logic error: lheatstrg not implemented for MYNN PBL' + ! errflg = 1 + ! return + ! end if + + end subroutine mynnedmf_wrapper_init + + subroutine mynnedmf_wrapper_finalize () + end subroutine mynnedmf_wrapper_finalize + +! \brief This scheme (1) performs pre-mynnedmf work, (2) runs the mynnedmf, and (3) performs post-mynnedmf work +!> \section arg_table_mynnedmf_wrapper_run Argument Table +!! \htmlinclude mynnedmf_wrapper_run.html +!! +SUBROUTINE mynnedmf_wrapper_run( & + & im,levs, & + & flag_init,flag_restart, & + & lssav, ldiag3d, qdiag3d, & + & lsidea, cplflx, & + & delt,dtf,dx,zorl, & + & phii,u,v,omega,t3d, & + & qgrs_water_vapor, & + & qgrs_liquid_cloud, & + & qgrs_ice_cloud, & + & qgrs_cloud_droplet_num_conc, & + & qgrs_cloud_ice_num_conc, & + & qgrs_ozone, & + & qgrs_water_aer_num_conc, & + & qgrs_ice_aer_num_conc, & + & qgrs_cccn, & + & prsl,exner, & + & slmsk,tsurf,qsfc,ps, & + & ust,ch,hflx,qflx,wspd,rb, & + & dtsfc1,dqsfc1, & + & dusfc1,dvsfc1, & + & dusfci_diag,dvsfci_diag, & + & dtsfci_diag,dqsfci_diag, & + & dusfc_diag,dvsfc_diag, & + & dtsfc_diag,dqsfc_diag, & + & dusfc_cice,dvsfc_cice, & + & dtsfc_cice,dqsfc_cice, & + & hflx_wat,qflx_wat,stress_wat, & + & oceanfrac,fice,wet,icy,dry, & + & dusfci_cpl,dvsfci_cpl, & + & dtsfci_cpl,dqsfci_cpl, & + & dusfc_cpl,dvsfc_cpl, & + & dtsfc_cpl,dqsfc_cpl, & + & recmol, & + & qke,qke_adv,Tsq,Qsq,Cov, & + & el_pbl,sh3d,sm3d,exch_h,exch_m, & + & dqke,qwt,qshear,qbuoy,qdiss, & + & Pblh,kpbl, & + & qc_bl,qi_bl,cldfra_bl, & + & edmf_a,edmf_w,edmf_qt, & + & edmf_thl,edmf_ent,edmf_qc, & + & sub_thl,sub_sqv,det_thl,det_sqv,& + & nupdraft,maxMF,ktop_plume, & + & dudt, dvdt, dtdt, & + & dqdt_water_vapor, dqdt_liquid_cloud, & ! <=== ntqv, ntcw + & dqdt_ice_cloud, dqdt_ozone, & ! <=== ntiw, ntoz + & dqdt_cloud_droplet_num_conc, dqdt_ice_num_conc, & ! <=== ntlnc, ntinc + & dqdt_water_aer_num_conc, dqdt_ice_aer_num_conc,& ! <=== ntwa, ntia + & dqdt_cccn, & ! <=== ntccn + & flag_for_pbl_generic_tend, & + & dtend, dtidx, index_of_temperature, & + & index_of_x_wind, index_of_y_wind, ntke, & + & ntqv, ntcw, ntiw, ntoz, ntlnc, ntinc, ntwa, ntia, & + & index_of_process_pbl, htrsw, htrlw, xmu, & + & bl_mynn_tkebudget, bl_mynn_tkeadvect, & + & bl_mynn_cloudpdf, bl_mynn_mixlength, & + & bl_mynn_edmf, & + & bl_mynn_edmf_mom, bl_mynn_edmf_tke, & + & bl_mynn_cloudmix, bl_mynn_mixqt, & + & bl_mynn_output, bl_mynn_closure, & + & icloud_bl, do_mynnsfclay, & + & imp_physics, imp_physics_gfdl, & + & imp_physics_thompson, imp_physics_wsm6, & + & chem3d, frp, mix_chem, rrfs_smoke, fire_turb, nchem, ndvel, & + & imp_physics_nssl, nssl_ccn_on, & + & ltaerosol, spp_wts_pbl, spp_pbl, lprnt, huge, errmsg, errflg ) + +! should be moved to inside the mynn: + use machine, only: kind_phys + use bl_mynn_common, only: cp, r_d, grav, g_inv, zero, & + xlv, xlvcp, xlscp + use module_bl_mynn, only: mynn_bl_driver + +!------------------------------------------------------------------- + implicit none +!------------------------------------------------------------------- + + real(kind=kind_phys) :: huge + character(len=*), intent(out) :: errmsg + integer, intent(out) :: errflg + + logical, intent(in) :: lssav, ldiag3d, lsidea, qdiag3d + logical, intent(in) :: cplflx + + !smoke/chem + integer, intent(in) :: nchem, ndvel + integer, parameter :: kdvel=1 + +! NAMELIST OPTIONS (INPUT): + logical, intent(in) :: & + & bl_mynn_tkeadvect, & + & bl_mynn_tkebudget, & + & ltaerosol, & + & lprnt, & + & do_mynnsfclay, & + & flag_for_pbl_generic_tend, & + & nssl_ccn_on + integer, intent(in) :: & + & bl_mynn_cloudpdf, & + & bl_mynn_mixlength, & + & icloud_bl, & + & bl_mynn_edmf, & + & bl_mynn_edmf_mom, & + & bl_mynn_edmf_tke, & + & bl_mynn_cloudmix, & + & bl_mynn_mixqt, & + & bl_mynn_output, & + & imp_physics, imp_physics_wsm6, & + & imp_physics_thompson, imp_physics_gfdl, & + & imp_physics_nssl, & + & spp_pbl + real, intent(in) :: & + & bl_mynn_closure + +!TENDENCY DIAGNOSTICS + real(kind=kind_phys), intent(inout), optional :: dtend(:,:,:) + integer, intent(in) :: dtidx(:,:) + integer, intent(in) :: index_of_temperature, index_of_x_wind + integer, intent(in) :: index_of_y_wind, index_of_process_pbl + integer, intent(in) :: ntoz, ntqv, ntcw, ntiw, ntlnc + integer, intent(in) :: ntinc, ntwa, ntia, ntke + +!MISC CONFIGURATION OPTIONS + INTEGER, PARAMETER :: & + & bl_mynn_mixscalars=1 + LOGICAL :: & + & FLAG_QI, FLAG_QNI, FLAG_QC, FLAG_QNC, & + & FLAG_QNWFA, FLAG_QNIFA, FLAG_OZONE + ! Define locally until needed from CCPP + LOGICAL, PARAMETER :: cycling = .false. + INTEGER, PARAMETER :: param_first_scalar = 1 + INTEGER :: & + & p_qc, p_qr, p_qi, p_qs, p_qg, p_qnc, p_qni + +!MYNN-1D + REAL(kind=kind_phys), intent(in) :: delt, dtf + INTEGER, intent(in) :: im, levs + LOGICAL, intent(in) :: flag_init, flag_restart + INTEGER :: initflag, k, i + INTEGER :: IDS,IDE,JDS,JDE,KDS,KDE, & + & IMS,IME,JMS,JME,KMS,KME, & + & ITS,ITE,JTS,JTE,KTS,KTE + + REAL(kind=kind_phys) :: tem + +!MYNN-3D + real(kind=kind_phys), dimension(:,:), intent(in) :: phii + real(kind=kind_phys), dimension(:,:), intent(inout) :: & + & dtdt, dudt, dvdt, & + & dqdt_water_vapor, dqdt_liquid_cloud, dqdt_ice_cloud, & + & dqdt_cloud_droplet_num_conc, dqdt_ice_num_conc, & + & dqdt_ozone, dqdt_water_aer_num_conc, dqdt_ice_aer_num_conc + real(kind=kind_phys), dimension(:,:), intent(inout) ::dqdt_cccn + real(kind=kind_phys), dimension(:,:), intent(inout) :: & + & qke, qke_adv, EL_PBL, Sh3D, Sm3D, & + & qc_bl, qi_bl, cldfra_bl + !These 10 arrays are only allocated when bl_mynn_output > 0 + real(kind=kind_phys), dimension(:,:), intent(inout) :: & + & edmf_a,edmf_w,edmf_qt, & + & edmf_thl,edmf_ent,edmf_qc, & + & sub_thl,sub_sqv,det_thl,det_sqv + real(kind=kind_phys), dimension(:,:), intent(inout) :: & + & dqke,qWT,qSHEAR,qBUOY,qDISS + real(kind=kind_phys), dimension(:,:), intent(inout) :: & + & t3d,qgrs_water_vapor,qgrs_liquid_cloud,qgrs_ice_cloud + real(kind=kind_phys), dimension(:,:), intent(in) :: & + & u,v,omega, & + & exner,prsl, & + & qgrs_cloud_droplet_num_conc, & + & qgrs_cloud_ice_num_conc, & + & qgrs_ozone, & + & qgrs_water_aer_num_conc, & + & qgrs_ice_aer_num_conc + real(kind=kind_phys), dimension(:,:), intent(in) ::qgrs_cccn + real(kind=kind_phys), dimension(:,:), intent(out) :: & + & Tsq, Qsq, Cov, exch_h, exch_m + real(kind=kind_phys), dimension(:), intent(in) :: xmu + real(kind=kind_phys), dimension(:,:), intent(in) :: htrsw, htrlw + ! spp_wts_pbl only allocated if spp_pbl == 1 + real(kind_phys), dimension(:,:), intent(in) :: spp_wts_pbl + + !LOCAL + real(kind=kind_phys), dimension(im,levs) :: & + & sqv,sqc,sqi,qnc,qni,ozone,qnwfa,qnifa, & + & dz, w, p, rho, th, qv, delp, & + & RUBLTEN, RVBLTEN, RTHBLTEN, RQVBLTEN, & + & RQCBLTEN, RQNCBLTEN, RQIBLTEN, RQNIBLTEN, & + & RQNWFABLTEN, RQNIFABLTEN + real(kind=kind_phys), allocatable :: old_ozone(:,:) + +!smoke/chem arrays + real(kind_phys), dimension(:), intent(inout) :: frp + logical, intent(in) :: mix_chem, fire_turb, rrfs_smoke + real(kind=kind_phys), dimension(:,:,:), intent(inout) :: chem3d + real(kind=kind_phys), dimension(im) :: emis_ant_no + real(kind=kind_phys), dimension(im,ndvel) :: vdep + +!MYNN-2D + real(kind=kind_phys), dimension(:), intent(in) :: & + & dx,zorl,slmsk,tsurf,qsfc,ps, & + & hflx,qflx,ust,wspd,rb,recmol + + real(kind=kind_phys), dimension(:), intent(in) :: & + & dusfc_cice,dvsfc_cice,dtsfc_cice,dqsfc_cice, & + & stress_wat,hflx_wat,qflx_wat, & + & oceanfrac,fice + + logical, dimension(:), intent(in) :: & + & wet, dry, icy + + real(kind=kind_phys), dimension(:), intent(inout) :: & + & pblh,dusfc_diag,dvsfc_diag,dtsfc_diag,dqsfc_diag + real(kind=kind_phys), dimension(:), intent(out) :: & + & ch,dtsfc1,dqsfc1,dusfc1,dvsfc1, & + & dtsfci_diag,dqsfci_diag,dusfci_diag,dvsfci_diag, & + & maxMF + integer, dimension(:), intent(inout) :: & + & kpbl,nupdraft,ktop_plume + + real(kind=kind_phys), dimension(:), intent(inout) :: & + & dusfc_cpl,dvsfc_cpl,dtsfc_cpl,dqsfc_cpl + real(kind=kind_phys), dimension(:), intent(inout) :: & + & dusfci_cpl,dvsfci_cpl,dtsfci_cpl,dqsfci_cpl + + !LOCAL + real, dimension(im) :: & + & hfx,qfx,rmol,xland,uoce,voce,vdfg,znt,ts + integer :: idtend + real, dimension(im) :: dusfci1,dvsfci1,dtsfci1,dqsfci1 + real(kind=kind_phys), allocatable :: save_qke_adv(:,:) + + ! Initialize CCPP error handling variables + errmsg = '' + errflg = 0 + + if (lprnt) then + write(0,*)"==============================================" + write(0,*)"in mynn wrapper..." + write(0,*)"flag_init=",flag_init + write(0,*)"flag_restart=",flag_restart + endif + + if (.not. flag_for_pbl_generic_tend .and. ldiag3d) then + idtend = dtidx(ntke+100,index_of_process_pbl) + if (idtend>=1) then + allocate(save_qke_adv(im,levs)) + save_qke_adv=qke_adv + endif + endif + + ! DH* TODO: Use flag_restart to distinguish which fields need + ! to be initialized and which are read from restart files + if (flag_init) then + initflag=1 + !print*,"in MYNN, initflag=",initflag + else + initflag=0 + !print*,"in MYNN, initflag=",initflag + endif + + !initialize arrays for test + EMIS_ANT_NO = 0. + vdep = 0. ! hli for chem dry deposition, 0 temporarily + + ! Check incoming moist species to ensure non-negative values + ! First, create height (dz) and pressure differences (delp) + ! across model layers + do k=1,levs + do i=1,im + dz(i,k)=(phii(i,k+1) - phii(i,k))*g_inv + enddo + enddo + + do i=1,im + delp(i,1) = ps(i) - (prsl(i,2)*dz(i,1) + prsl(i,1)*dz(i,2))/(dz(i,1)+dz(i,2)) + do k=2,levs-1 + delp(i,k) = (prsl(i,k)*dz(i,k-1) + prsl(i,k-1)*dz(i,k))/(dz(i,k)+dz(i,k-1)) - & + (prsl(i,k+1)*dz(i,k) + prsl(i,k)*dz(i,k+1))/(dz(i,k)+dz(i,k+1)) + enddo + delp(i,levs) = delp(i,levs-1) + enddo + + do i=1,im + call moisture_check2(levs, delt, & + delp(i,:), exner(i,:), & + qgrs_water_vapor(i,:), & + qgrs_liquid_cloud(i,:),& + qgrs_ice_cloud(i,:), & + t3d(i,:) ) + enddo + + FLAG_OZONE = ntoz>0 + + ! Assign variables for each microphysics scheme + if (imp_physics == imp_physics_wsm6) then + ! WSM6 + FLAG_QI = .true. + FLAG_QNI= .false. + FLAG_QC = .true. + FLAG_QNC= .false. + FLAG_QNWFA= .false. + FLAG_QNIFA= .false. + p_qc = 2 + p_qr = 0 + p_qi = 2 + p_qs = 0 + p_qg = 0 + p_qnc= 0 + p_qni= 0 + do k=1,levs + do i=1,im + sqv(i,k) = qgrs_water_vapor(i,k) + sqc(i,k) = qgrs_liquid_cloud(i,k) + sqi(i,k) = qgrs_ice_cloud(i,k) + ozone(i,k) = qgrs_ozone(i,k) + qnc(i,k) = 0. + qni(i,k) = 0. + qnwfa(i,k) = 0. + qnifa(i,k) = 0. + enddo + enddo + elseif (imp_physics == imp_physics_nssl ) then + ! NSSL + FLAG_QI = .true. + FLAG_QNI= .true. + FLAG_QC = .true. + FLAG_QNC= .true. + FLAG_QNWFA= nssl_ccn_on ! ERM: Perhaps could use this field for CCN field? + FLAG_QNIFA= .false. + ! p_q vars not used? + p_qc = 2 + p_qr = 0 + p_qi = 2 + p_qs = 0 + p_qg = 0 + p_qnc= 0 + p_qni= 0 + do k=1,levs + do i=1,im + sqv(i,k) = qgrs_water_vapor(i,k) + sqc(i,k) = qgrs_liquid_cloud(i,k) + sqi(i,k) = qgrs_ice_cloud(i,k) + ozone(i,k) = qgrs_ozone(i,k) + qnc(i,k) = qgrs_cloud_droplet_num_conc(i,k) + qni(i,k) = qgrs_cloud_ice_num_conc(i,k) + qnwfa(i,k) = 0. + IF ( nssl_ccn_on ) THEN + qnwfa(i,k) = qgrs_cccn(i,k) + ENDIF + qnifa(i,k) = 0. + enddo + enddo + elseif (imp_physics == imp_physics_thompson) then + ! Thompson + if(ltaerosol) then + FLAG_QI = .true. + FLAG_QNI= .true. + FLAG_QC = .true. + FLAG_QNC= .true. + FLAG_QNWFA= .true. + FLAG_QNIFA= .true. + p_qc = 2 + p_qr = 0 + p_qi = 2 + p_qs = 0 + p_qg = 0 + p_qnc= 0 + p_qni= 0 + do k=1,levs + do i=1,im + sqv(i,k) = qgrs_water_vapor(i,k) + sqc(i,k) = qgrs_liquid_cloud(i,k) + sqi(i,k) = qgrs_ice_cloud(i,k) + qnc(i,k) = qgrs_cloud_droplet_num_conc(i,k) + qni(i,k) = qgrs_cloud_ice_num_conc(i,k) + ozone(i,k) = qgrs_ozone(i,k) + qnwfa(i,k) = qgrs_water_aer_num_conc(i,k) + qnifa(i,k) = qgrs_ice_aer_num_conc(i,k) + enddo + enddo + else + FLAG_QI = .true. + FLAG_QNI= .true. + FLAG_QC = .true. + FLAG_QNC= .false. + FLAG_QNWFA= .false. + FLAG_QNIFA= .false. + p_qc = 2 + p_qr = 0 + p_qi = 2 + p_qs = 0 + p_qg = 0 + p_qnc= 0 + p_qni= 0 + do k=1,levs + do i=1,im + sqv(i,k) = qgrs_water_vapor(i,k) + sqc(i,k) = qgrs_liquid_cloud(i,k) + sqi(i,k) = qgrs_ice_cloud(i,k) + qnc(i,k) = 0. + qni(i,k) = qgrs_cloud_ice_num_conc(i,k) + ozone(i,k) = qgrs_ozone(i,k) + qnwfa(i,k) = 0. + qnifa(i,k) = 0. + enddo + enddo + endif + elseif (imp_physics == imp_physics_gfdl) then + ! GFDL MP + FLAG_QI = .true. + FLAG_QNI= .false. + FLAG_QC = .true. + FLAG_QNC= .false. + FLAG_QNWFA= .false. + FLAG_QNIFA= .false. + p_qc = 2 + p_qr = 0 + p_qi = 2 + p_qs = 0 + p_qg = 0 + p_qnc= 0 + p_qni= 0 + do k=1,levs + do i=1,im + sqv(i,k) = qgrs_water_vapor(i,k) + sqc(i,k) = qgrs_liquid_cloud(i,k) + sqi(i,k) = qgrs_ice_cloud(i,k) + qnc(i,k) = 0. + qni(i,k) = 0. + qnwfa(i,k) = 0. + qnifa(i,k) = 0. + ozone(i,k) = qgrs_ozone(i,k) + enddo + enddo + else + print*,"In MYNN wrapper. Unknown microphysics scheme, imp_physics=",imp_physics + print*,"Defaulting to qc and qv species only..." + FLAG_QI = .false. + FLAG_QNI= .false. + FLAG_QC = .true. + FLAG_QNC= .false. + FLAG_QNWFA= .false. + FLAG_QNIFA= .false. + p_qc = 2 + p_qr = 0 + p_qi = 0 + p_qs = 0 + p_qg = 0 + p_qnc= 0 + p_qni= 0 + do k=1,levs + do i=1,im + sqv(i,k) = qgrs_water_vapor(i,k) + sqc(i,k) = qgrs_liquid_cloud(i,k) + sqi(i,k) = 0. + qnc(i,k) = 0. + qni(i,k) = 0. + qnwfa(i,k) = 0. + qnifa(i,k) = 0. + ozone(i,k) = qgrs_ozone(i,k) + enddo + enddo + endif + if(ldiag3d .and. dtidx(100+ntoz,index_of_process_pbl)>1) then + allocate(old_ozone(im,levs)) + old_ozone = ozone + endif + if (lprnt)write(0,*)"prepping MYNN-EDMF variables..." + + do k=1,levs + do i=1,im + ! dz(i,k)=(phii(i,k+1) - phii(i,k))*g_inv + th(i,k)=t3d(i,k)/exner(i,k) + ! keep as specific humidity + ! qv(i,k)=qvsh(i,k)/(1.0 - qvsh(i,k)) + ! qc(i,k)=qc(i,k)/(1.0 - qvsh(i,k)) + ! qi(i,k)=qi(i,k)/(1.0 - qvsh(i,k)) + rho(i,k)=prsl(i,k)/(r_d*t3d(i,k)) + w(i,k) = -omega(i,k)/(rho(i,k)*grav) + enddo + enddo + + do i=1,im + if (slmsk(i)==1. .or. slmsk(i)==2.) then !sea/land/ice mask (=0/1/2) in FV3 + xland(i)=1.0 !but land/water = (1/2) in SFCLAY_mynn + else + xland(i)=2.0 + endif + uoce(i)=0.0 + voce(i)=0.0 + vdfg(i)=0.0 + !ust(i) = sqrt(stress(i)) + ch(i)=0.0 + hfx(i)=hflx(i)*rho(i,1)*cp + qfx(i)=qflx(i)*rho(i,1) + + dtsfc1(i) = hfx(i) + dqsfc1(i) = qfx(i)*XLV + dusfc1(i) = -1.*rho(i,1)*ust(i)*ust(i)*u(i,1)/wspd(i) + dvsfc1(i) = -1.*rho(i,1)*ust(i)*ust(i)*v(i,1)/wspd(i) + + !BWG: diagnostic surface fluxes for scalars & momentum + dtsfci_diag(i) = dtsfc1(i) + dqsfci_diag(i) = dqsfc1(i) + dtsfc_diag(i) = dtsfc_diag(i) + dtsfc1(i)*delt + dqsfc_diag(i) = dqsfc_diag(i) + dqsfc1(i)*delt + dusfci_diag(i) = dusfc1(i) + dvsfci_diag(i) = dvsfc1(i) + dusfc_diag(i) = dusfc_diag(i) + dusfci_diag(i)*delt + dvsfc_diag(i) = dvsfc_diag(i) + dvsfci_diag(i)*delt + + znt(i)=zorl(i)*0.01 !cm -> m? + if (do_mynnsfclay) then + rmol(i)=recmol(i) + else + if (hfx(i) .ge. 0.)then + rmol(i)=-hfx(i)/(200.*dz(i,1)*0.5) + else + rmol(i)=ABS(rb(i))*1./(dz(i,1)*0.5) + endif + endif + ts(i)=tsurf(i)/exner(i,1) !theta +! qsfc(i)=qss(i) +! ps(i)=pgr(i) +! wspd(i)=wind(i) + enddo + + ! BWG: Coupling insertion + if (cplflx) then + do i=1,im + if (oceanfrac(i) > zero) then ! Ocean only, NO LAKES + if ( .not. wet(i)) then ! no open water, use results from CICE + dusfci_cpl(i) = dusfc_cice(i) + dvsfci_cpl(i) = dvsfc_cice(i) + dtsfci_cpl(i) = dtsfc_cice(i) + dqsfci_cpl(i) = dqsfc_cice(i) + elseif (icy(i) .or. dry(i)) then ! use stress_ocean for opw component at mixed point + if (wspd(i) > zero) then + dusfci_cpl(i) = -1.*rho(i,1)*stress_wat(i)*u(i,1)/wspd(i) ! U-momentum flux + dvsfci_cpl(i) = -1.*rho(i,1)*stress_wat(i)*v(i,1)/wspd(i) ! V-momentum flux + else + dusfci_cpl(i) = zero + dvsfci_cpl(i) = zero + endif + dtsfci_cpl(i) = cp*rho(i,1)*hflx_wat(i) ! sensible heat flux over open ocean + dqsfci_cpl(i) = XLV*rho(i,1)*qflx_wat(i) ! latent heat flux over open ocean + else ! use results from this scheme for 100% open ocean + dusfci_cpl(i) = dusfci_diag(i) + dvsfci_cpl(i) = dvsfci_diag(i) + dtsfci_cpl(i) = dtsfci_diag(i) + dqsfci_cpl(i) = dqsfci_diag(i) + endif +! + dusfc_cpl (i) = dusfc_cpl(i) + dusfci_cpl(i) * delt + dvsfc_cpl (i) = dvsfc_cpl(i) + dvsfci_cpl(i) * delt + dtsfc_cpl (i) = dtsfc_cpl(i) + dtsfci_cpl(i) * delt + dqsfc_cpl (i) = dqsfc_cpl(i) + dqsfci_cpl(i) * delt + else ! If no ocean + dusfc_cpl(i) = huge + dvsfc_cpl(i) = huge + dtsfc_cpl(i) = huge + dqsfc_cpl(i) = huge + endif ! Ocean only, NO LAKES + enddo + endif ! End coupling insertion + + if (lprnt) then + print* + write(0,*)"===CALLING mynn_bl_driver; input:" + print*,"bl_mynn_tkebudget=",bl_mynn_tkebudget," bl_mynn_tkeadvect=",bl_mynn_tkeadvect + print*,"bl_mynn_cloudpdf=",bl_mynn_cloudpdf," bl_mynn_mixlength=",bl_mynn_mixlength + print*,"bl_mynn_edmf=",bl_mynn_edmf," bl_mynn_edmf_mom=",bl_mynn_edmf_mom + print*,"bl_mynn_edmf_tke=",bl_mynn_edmf_tke + print*,"bl_mynn_cloudmix=",bl_mynn_cloudmix," bl_mynn_mixqt=",bl_mynn_mixqt + print*,"icloud_bl=",icloud_bl + print*,"T:",t3d(1,1),t3d(1,2),t3d(1,levs) + print*,"TH:",th(1,1),th(1,2),th(1,levs) + print*,"rho:",rho(1,1),rho(1,2),rho(1,levs) + print*,"exner:",exner(1,1),exner(1,2),exner(1,levs) + print*,"prsl:",prsl(1,1),prsl(1,2),prsl(1,levs) + print*,"dz:",dz(1,1),dz(1,2),dz(1,levs) + print*,"u:",u(1,1),u(1,2),u(1,levs) + print*,"v:",v(1,1),v(1,2),v(1,levs) + print*,"sqv:",sqv(1,1),sqv(1,2),sqv(1,levs) + print*,"sqc:",sqc(1,1),sqc(1,2),sqc(1,levs) + print*,"sqi:",sqi(1,1),sqi(1,2),sqi(1,levs) + print*,"rmol:",rmol(1)," ust:",ust(1) + print*," dx=",dx(1),"initflag=",initflag + print*,"Tsurf:",tsurf(1)," Thetasurf:",ts(1) + print*,"HFX:",hfx(1)," qfx",qfx(1) + print*,"qsfc:",qsfc(1)," ps:",ps(1) + print*,"wspd:",wspd(1)," rb=",rb(1) + print*,"znt:",znt(1)," delt=",delt + print*,"im=",im," levs=",levs + print*,"PBLH=",pblh(1)," KPBL=",KPBL(1)," xland=",xland(1) + print*,"vdfg=",vdfg(1)," ch=",ch(1) + !print*,"TKE:",TKE_PBL(1,1),TKE_PBL(1,2),TKE_PBL(1,levs) + print*,"qke:",qke(1,1),qke(1,2),qke(1,levs) + print*,"el_pbl:",el_pbl(1,1),el_pbl(1,2),el_pbl(1,levs) + print*,"Sh3d:",Sh3d(1,1),sh3d(1,2),sh3d(1,levs) + !print*,"exch_h:",exch_h(1,1),exch_h(1,2),exch_h(1,levs) ! - intent(out) + !print*,"exch_m:",exch_m(1,1),exch_m(1,2),exch_m(1,levs) ! - intent(out) + print*,"max cf_bl:",maxval(cldfra_bl(1,:)) + endif + + + CALL mynn_bl_driver( & + & initflag=initflag,restart=flag_restart, & + & cycling=cycling, & + & delt=delt,dz=dz,dx=dx,znt=znt, & + & u=u,v=v,w=w,th=th,sqv3D=sqv,sqc3D=sqc, & + & sqi3D=sqi,qnc=qnc,qni=qni, & + & qnwfa=qnwfa,qnifa=qnifa,ozone=ozone, & + & p=prsl,exner=exner,rho=rho,T3D=t3d, & + & xland=xland,ts=ts,qsfc=qsfc,ps=ps, & + & ust=ust,ch=ch,hfx=hfx,qfx=qfx,rmol=rmol, & + & wspd=wspd,uoce=uoce,voce=voce,vdfg=vdfg, & !input + & qke=QKE,qke_adv=qke_adv, & !output + & sh3d=Sh3d,sm3d=Sm3d, & +!chem/smoke + & nchem=nchem,kdvel=kdvel,ndvel=ndvel, & + & Chem3d=chem3d,Vdep=vdep, & + & FRP=frp,EMIS_ANT_NO=emis_ant_no, & + & mix_chem=mix_chem,fire_turb=fire_turb, & + & rrfs_smoke=rrfs_smoke, & +!----- + & Tsq=tsq,Qsq=qsq,Cov=cov, & !output + & RUBLTEN=RUBLTEN,RVBLTEN=RVBLTEN,RTHBLTEN=RTHBLTEN, & !output + & RQVBLTEN=RQVBLTEN,RQCBLTEN=rqcblten, & + & RQIBLTEN=rqiblten,RQNCBLTEN=rqncblten, & !output + & RQNIBLTEN=rqniblten,RQNWFABLTEN=RQNWFABLTEN, & !output + & RQNIFABLTEN=RQNIFABLTEN,dozone=dqdt_ozone, & !output + & EXCH_H=exch_h,EXCH_M=exch_m, & !output + & pblh=pblh,KPBL=KPBL, & !output + & el_pbl=el_pbl, & !output + & dqke=dqke, & !output + & qWT=qWT,qSHEAR=qSHEAR,qBUOY=qBUOY,qDISS=qDISS, & !output + & bl_mynn_tkeadvect=bl_mynn_tkeadvect, & + & bl_mynn_tkebudget=bl_mynn_tkebudget, & !input parameter + & bl_mynn_cloudpdf=bl_mynn_cloudpdf, & !input parameter + & bl_mynn_mixlength=bl_mynn_mixlength, & !input parameter + & icloud_bl=icloud_bl, & !input parameter + & qc_bl=qc_bl,qi_bl=qi_bl,cldfra_bl=cldfra_bl, & !output + & closure=bl_mynn_closure,bl_mynn_edmf=bl_mynn_edmf, & !input parameter + & bl_mynn_edmf_mom=bl_mynn_edmf_mom, & !input parameter + & bl_mynn_edmf_tke=bl_mynn_edmf_tke, & !input parameter + & bl_mynn_mixscalars=bl_mynn_mixscalars, & !input parameter + & bl_mynn_output=bl_mynn_output, & !input parameter + & bl_mynn_cloudmix=bl_mynn_cloudmix, & !input parameter + & bl_mynn_mixqt=bl_mynn_mixqt, & !input parameter + & edmf_a=edmf_a,edmf_w=edmf_w,edmf_qt=edmf_qt, & !output + & edmf_thl=edmf_thl,edmf_ent=edmf_ent,edmf_qc=edmf_qc,&!output + & sub_thl3D=sub_thl,sub_sqv3D=sub_sqv, & + & det_thl3D=det_thl,det_sqv3D=det_sqv, & + & nupdraft=nupdraft,maxMF=maxMF, & !output + & ktop_plume=ktop_plume, & !output + & spp_pbl=spp_pbl,pattern_spp_pbl=spp_wts_pbl, & !input + & RTHRATEN=htrlw, & !input + & FLAG_QI=flag_qi,FLAG_QNI=flag_qni, & !input + & FLAG_QC=flag_qc,FLAG_QNC=flag_qnc, & !input + & FLAG_QNWFA=FLAG_QNWFA,FLAG_QNIFA=FLAG_QNIFA, & !input + & FLAG_OZONE=FLAG_OZONE, & !input + & IDS=1,IDE=im,JDS=1,JDE=1,KDS=1,KDE=levs, & !input + & IMS=1,IME=im,JMS=1,JME=1,KMS=1,KME=levs, & !input + & ITS=1,ITE=im,JTS=1,JTE=1,KTS=1,KTE=levs) !input + + + ! POST MYNN (INTERSTITIAL) WORK: + !update/save MYNN-only variables + !do k=1,levs + ! do i=1,im + ! gq0(i,k,4)=qke(i,k,1) !tke*2 + ! enddo + !enddo + !For MYNN, convert TH-tend to T-tend + do k = 1, levs + do i = 1, im + dtdt(i,k) = dtdt(i,k) + RTHBLTEN(i,k)*exner(i,k) + dudt(i,k) = dudt(i,k) + RUBLTEN(i,k) + dvdt(i,k) = dvdt(i,k) + RVBLTEN(i,k) + enddo + enddo + accum_duvt3dt: if(ldiag3d .or. lsidea) then + call dtend_helper(index_of_x_wind,RUBLTEN) + call dtend_helper(index_of_y_wind,RVBLTEN) + call dtend_helper(index_of_temperature,RTHBLTEN,exner) + if(ldiag3d) then + call dtend_helper(100+ntoz,dqdt_ozone) + ! idtend = dtidx(100+ntoz,index_of_process_pbl) + ! if(idtend>=1) then + ! dtend(:,:,idtend) = dtend(:,:,idtend) + (ozone-old_ozone) + ! deallocate(old_ozone) + ! endif + endif + endif accum_duvt3dt + !Update T, U and V: + !do k = 1, levs + ! do i = 1, im + ! T3D(i,k) = T3D(i,k) + RTHBLTEN(i,k)*exner(i,k)*delt + ! u(i,k) = u(i,k) + RUBLTEN(i,k)*delt + ! v(i,k) = v(i,k) + RVBLTEN(i,k)*delt + ! enddo + !enddo + + !DO moist/scalar/tracer tendencies: + if (imp_physics == imp_physics_wsm6) then + ! WSM6 + do k=1,levs + do i=1,im + dqdt_water_vapor(i,k) = RQVBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_liquid_cloud(i,k) = RQCBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_ice_cloud(i,k) = RQIBLTEN(i,k) !/(1.0 + qv(i,k)) + !dqdt_ozone(i,k) = 0.0 + enddo + enddo + if(ldiag3d .and. .not. flag_for_pbl_generic_tend) then + call dtend_helper(100+ntqv,RQVBLTEN) + call dtend_helper(100+ntcw,RQCBLTEN) + call dtend_helper(100+ntiw,RQIBLTEN) + endif + !Update moist species: + !do k=1,levs + ! do i=1,im + ! qgrs_water_vapor(i,k) = qgrs_water_vapor(i,k) + (RQVBLTEN(i,k)/(1.0+RQVBLTEN(i,k)))*delt + ! qgrs_liquid_cloud(i,k) = qgrs_liquid_cloud(i,k) + RQCBLTEN(i,k)*delt + ! qgrs_ice_cloud(i,k) = qgrs_ice_cloud(i,k) + RQIBLTEN(i,k)*delt + ! !dqdt_ozone(i,k) = 0.0 + ! enddo + !enddo + elseif (imp_physics == imp_physics_thompson) then + ! Thompson-Aerosol + if(ltaerosol) then + do k=1,levs + do i=1,im + dqdt_water_vapor(i,k) = RQVBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_liquid_cloud(i,k) = RQCBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_cloud_droplet_num_conc(i,k) = RQNCBLTEN(i,k) + dqdt_ice_cloud(i,k) = RQIBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_ice_num_conc(i,k) = RQNIBLTEN(i,k) + !dqdt_ozone(i,k) = 0.0 + dqdt_water_aer_num_conc(i,k) = RQNWFABLTEN(i,k) + dqdt_ice_aer_num_conc(i,k) = RQNIFABLTEN(i,k) + enddo + enddo + if(ldiag3d .and. .not. flag_for_pbl_generic_tend) then + call dtend_helper(100+ntqv,RQVBLTEN) + call dtend_helper(100+ntcw,RQCBLTEN) + call dtend_helper(100+ntlnc,RQNCBLTEN) + call dtend_helper(100+ntiw,RQIBLTEN) + call dtend_helper(100+ntinc,RQNIBLTEN) + call dtend_helper(100+ntwa,RQNWFABLTEN) + call dtend_helper(100+ntia,RQNIFABLTEN) + endif + !do k=1,levs + ! do i=1,im + ! qgrs_water_vapor(i,k) = qgrs_water_vapor(i,k) + (RQVBLTEN(i,k)/(1.0+RQVBLTEN(i,k)))*delt + ! qgrs_liquid_cloud(i,k) = qgrs_liquid_cloud(i,k) + RQCBLTEN(i,k)*delt + ! qgrs_ice_cloud(i,k) = qgrs_ice_cloud(i,k) + RQIBLTEN(i,k)*delt + ! qgrs_cloud_droplet_num_conc(i,k) = qgrs_cloud_droplet_num_conc(i,k) + RQNCBLTEN(i,k)*delt + ! qgrs_cloud_ice_num_conc(i,k) = qgrs_cloud_ice_num_conc(i,k) + RQNIBLTEN(i,k)*delt + ! !dqdt_ozone(i,k) = 0.0 + ! !qgrs_water_aer_num_conc(i,k) = qgrs_water_aer_num_conc(i,k) + RQNWFABLTEN(i,k)*delt + ! !qgrs_ice_aer_num_conc(i,k) = qgrs_ice_aer_num_conc(i,k) + RQNIFABLTEN(i,k)*delt + ! enddo + !enddo + else + !Thompson (2008) + do k=1,levs + do i=1,im + dqdt_water_vapor(i,k) = RQVBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_liquid_cloud(i,k) = RQCBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_ice_cloud(i,k) = RQIBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_ice_num_conc(i,k) = RQNIBLTEN(i,k) + !dqdt_ozone(i,k) = 0.0 + enddo + enddo + if(ldiag3d .and. .not. flag_for_pbl_generic_tend) then + call dtend_helper(100+ntqv,RQVBLTEN) + call dtend_helper(100+ntcw,RQCBLTEN) + call dtend_helper(100+ntiw,RQIBLTEN) + call dtend_helper(100+ntinc,RQNIBLTEN) + endif + !do k=1,levs + ! do i=1,im + ! qgrs_water_vapor(i,k) = qgrs_water_vapor(i,k) + (RQVBLTEN(i,k)/(1.0+RQVBLTEN(i,k)))*delt + ! qgrs_liquid_cloud(i,k) = qgrs_liquid_cloud(i,k) + RQCBLTEN(i,k)*delt + ! qgrs_ice_cloud(i,k) = qgrs_ice_cloud(i,k) + RQIBLTEN(i,k)*delt + ! qgrs_cloud_ice_num_conc(i,k) = qgrs_cloud_ice_num_conc(i,k) + RQNIBLTEN(i,k)*delt + ! !dqdt_ozone(i,k) = 0.0 + ! enddo + !enddo + endif !end thompson choice + elseif (imp_physics == imp_physics_nssl) then + ! NSSL + do k=1,levs + do i=1,im + dqdt_water_vapor(i,k) = RQVBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_liquid_cloud(i,k) = RQCBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_cloud_droplet_num_conc(i,k) = RQNCBLTEN(i,k) + dqdt_ice_cloud(i,k) = RQIBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_ice_num_conc(i,k) = RQNIBLTEN(i,k) + IF ( nssl_ccn_on ) THEN ! + dqdt_cccn(i,k) = RQNWFABLTEN(i,k) + ENDIF + enddo + enddo + + elseif (imp_physics == imp_physics_gfdl) then + ! GFDL MP + do k=1,levs + do i=1,im + dqdt_water_vapor(i,k) = RQVBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_liquid_cloud(i,k) = RQCBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_ice_cloud(i,k) = RQIBLTEN(i,k) !/(1.0 + qv(i,k)) + !dqdt_rain(i,k) = 0.0 + !dqdt_snow(i,k) = 0.0 + !dqdt_graupel(i,k) = 0.0 + !dqdt_ozone(i,k) = 0.0 + enddo + enddo + if(ldiag3d .and. .not. flag_for_pbl_generic_tend) then + call dtend_helper(100+ntqv,RQVBLTEN) + call dtend_helper(100+ntcw,RQCBLTEN) + call dtend_helper(100+ntiw,RQIBLTEN) + endif + !do k=1,levs + ! do i=1,im + ! qgrs_water_vapor(i,k) = qgrs_water_vapor(i,k) + (RQVBLTEN(i,k)/(1.0+RQVBLTEN(i,k)))*delt + ! qgrs_liquid_cloud(i,k) = qgrs_liquid_cloud(i,k) + RQCBLTEN(i,k)*delt + ! qgrs_ice_cloud(i,k) = qgrs_ice_cloud(i,k) + RQIBLTEN(i,k)*delt + ! !dqdt_ozone(i,k) = 0.0 + ! enddo + !enddo + else +! print*,"In MYNN wrapper. Unknown microphysics scheme, imp_physics=",imp_physics + do k=1,levs + do i=1,im + dqdt_water_vapor(i,k) = RQVBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_liquid_cloud(i,k) = RQCBLTEN(i,k) !/(1.0 + qv(i,k)) + dqdt_ice_cloud(i,k) = 0.0 + !dqdt_rain(i,k) = 0.0 + !dqdt_snow(i,k) = 0.0 + !dqdt_graupel(i,k) = 0.0 + !dqdt_ozone(i,k) = 0.0 + enddo + enddo + if(ldiag3d .and. .not. flag_for_pbl_generic_tend) then + call dtend_helper(100+ntqv,RQVBLTEN) + call dtend_helper(100+ntcw,RQCBLTEN) + call dtend_helper(100+ntiw,RQIBLTEN) + endif + endif + + if (lprnt) then + print* + print*,"===Finished with mynn_bl_driver; output:" + print*,"T:",t3d(1,1),t3d(1,2),t3d(1,levs) + print*,"TH:",th(1,1),th(1,2),th(1,levs) + print*,"rho:",rho(1,1),rho(1,2),rho(1,levs) + print*,"exner:",exner(1,1),exner(1,2),exner(1,levs) + print*,"prsl:",prsl(1,1),prsl(1,2),prsl(1,levs) + print*,"dz:",dz(1,1),dz(1,2),dz(1,levs) + print*,"u:",u(1,1),u(1,2),u(1,levs) + print*,"v:",v(1,1),v(1,2),v(1,levs) + print*,"sqv:",sqv(1,1),sqv(1,2),sqv(1,levs) + print*,"sqc:",sqc(1,1),sqc(1,2),sqc(1,levs) + print*,"sqi:",sqi(1,1),sqi(1,2),sqi(1,levs) + print*,"rmol:",rmol(1)," ust:",ust(1) + print*,"dx(1)=",dx(1),"initflag=",initflag + print*,"Tsurf:",tsurf(1)," Thetasurf:",ts(1) + print*,"HFX:",hfx(1)," qfx",qfx(1) + print*,"qsfc:",qsfc(1)," ps:",ps(1) + print*,"wspd:",wspd(1)," rb=",rb(1) + print*,"znt:",znt(1)," delt=",delt + print*,"im=",im," levs=",levs + print*,"PBLH=",pblh(1)," KPBL=",KPBL(1)," xland=",xland(1) + print*,"vdfg=",vdfg(1)," ch=",ch(1) + !print*,"TKE:",TKE_PBL(1,1),TKE_PBL(1,2),TKE_PBL(1,levs) + print*,"qke:",qke(1,1),qke(1,2),qke(1,levs) + print*,"el_pbl:",el_pbl(1,1),el_pbl(1,2),el_pbl(1,levs) + print*,"Sh3d:",Sh3d(1,1),sh3d(1,2),sh3d(1,levs) + print*,"exch_h:",exch_h(1,1),exch_h(1,2),exch_h(1,levs) + print*,"exch_m:",exch_m(1,1),exch_m(1,2),exch_m(1,levs) + print*,"max cf_bl:",maxval(cldfra_bl(1,:)) + print*,"max qc_bl:",maxval(qc_bl(1,:)) + print*,"dtdt:",dtdt(1,1),dtdt(1,2),dtdt(1,levs) + print*,"dudt:",dudt(1,1),dudt(1,2),dudt(1,levs) + print*,"dvdt:",dvdt(1,1),dvdt(1,2),dvdt(1,levs) + print*,"dqdt:",dqdt_water_vapor(1,1),dqdt_water_vapor(1,2),dqdt_water_vapor(1,levs) + print*,"ktop_plume:",ktop_plume(1)," maxmf:",maxmf(1) + print*,"nup:",nupdraft(1) + print* + endif + + if(allocated(save_qke_adv)) then + if(ldiag3d .and. .not. flag_for_pbl_generic_tend) then + idtend = dtidx(100+ntke,index_of_process_pbl) + if(idtend>=1) then + dtend(:,:,idtend) = dtend(:,:,idtend) + qke_adv-save_qke_adv + endif + endif + deallocate(save_qke_adv) + endif + + CONTAINS + + SUBROUTINE dtend_helper(itracer,field,mult) + real(kind=kind_phys), intent(in) :: field(im,levs) + real(kind=kind_phys), intent(in), optional :: mult(im,levs) + integer, intent(in) :: itracer + integer :: idtend + + idtend=dtidx(itracer,index_of_process_pbl) + if(idtend>=1) then + if(present(mult)) then + dtend(:,:,idtend) = dtend(:,:,idtend) + field*dtf*mult + else + dtend(:,:,idtend) = dtend(:,:,idtend) + field*dtf + endif + endif + END SUBROUTINE dtend_helper + +! ================================================================== + SUBROUTINE moisture_check2(kte, delt, dp, exner, & + qv, qc, qi, th ) + ! + ! If qc < qcmin, qi < qimin, or qv < qvmin happens in any layer, + ! force them to be larger than minimum value by (1) condensating + ! water vapor into liquid or ice, and (2) by transporting water vapor + ! from the very lower layer. + ! + ! We then update the final state variables and tendencies associated + ! with this correction. If any condensation happens, update theta/temperature too. + ! Note that (qv,qc,qi,th) are the final state variables after + ! applying corresponding input tendencies and corrective tendencies. + + implicit none + integer, intent(in) :: kte + real, intent(in) :: delt + real, dimension(kte), intent(in) :: dp, exner + real, dimension(kte), intent(inout) :: qv, qc, qi, th + integer k + real :: dqc2, dqi2, dqv2, sum, aa, dum + real, parameter :: qvmin1= 1e-8, & !min at k=1 + qvmin = 1e-20, & !min above k=1 + qcmin = 0.0, & + qimin = 0.0 + + do k = kte, 1, -1 ! From the top to the surface + dqc2 = max(0.0, qcmin-qc(k)) !qc deficit (>=0) + dqi2 = max(0.0, qimin-qi(k)) !qi deficit (>=0) + + !update species + qc(k) = qc(k) + dqc2 + qi(k) = qi(k) + dqi2 + qv(k) = qv(k) - dqc2 - dqi2 + !for theta + !th(k) = th(k) + xlvcp/exner(k)*dqc2 + & + ! xlscp/exner(k)*dqi2 + !for temperature + th(k) = th(k) + xlvcp*dqc2 + & + xlscp*dqi2 + + !then fix qv if lending qv made it negative + if (k .eq. 1) then + dqv2 = max(0.0, qvmin1-qv(k)) !qv deficit (>=0) + qv(k) = qv(k) + dqv2 + qv(k) = max(qv(k),qvmin1) + dqv2 = 0.0 + else + dqv2 = max(0.0, qvmin-qv(k)) !qv deficit (>=0) + qv(k) = qv(k) + dqv2 + qv(k-1)= qv(k-1) - dqv2*dp(k)/dp(k-1) + qv(k) = max(qv(k),qvmin) + endif + qc(k) = max(qc(k),qcmin) + qi(k) = max(qi(k),qimin) + end do + + ! Extra moisture used to satisfy 'qv(1)>=qvmin' is proportionally + ! extracted from all the layers that has 'qv > 2*qvmin'. This fully + ! preserves column moisture. + if( dqv2 .gt. 1.e-20 ) then + sum = 0.0 + do k = 1, kte + if( qv(k) .gt. 2.0*qvmin ) sum = sum + qv(k)*dp(k) + enddo + aa = dqv2*dp(1)/max(1.e-20,sum) + if( aa .lt. 0.5 ) then + do k = 1, kte + if( qv(k) .gt. 2.0*qvmin ) then + dum = aa*qv(k) + qv(k) = qv(k) - dum + endif + enddo + else + ! For testing purposes only (not yet found in any output): + ! write(*,*) 'Full moisture conservation is impossible' + endif + endif + + return + + END SUBROUTINE moisture_check2 + + END SUBROUTINE mynnedmf_wrapper_run + +!###================================================================= + +END MODULE mynnedmf_wrapper diff --git a/sorc/ufs_model.fd_gsl/FV3/ccpp/suites/suite_FV3_GFS_v17_p8_gf.xml b/sorc/ufs_model.fd_gsl/FV3/ccpp/suites/suite_FV3_GFS_v17_p8_gf.xml new file mode 100644 index 0000000000..e3854d4300 --- /dev/null +++ b/sorc/ufs_model.fd_gsl/FV3/ccpp/suites/suite_FV3_GFS_v17_p8_gf.xml @@ -0,0 +1,98 @@ + + + + + + + GFS_time_vary_pre + GFS_rrtmg_setup + GFS_rad_time_vary + GFS_phys_time_vary + + + + + GFS_suite_interstitial_rad_reset + sgscloud_radpre + GFS_rrtmg_pre + GFS_radiation_surface + rad_sw_pre + rrtmg_sw + rrtmg_sw_post + rrtmg_lw_pre + rrtmg_lw + sgscloud_radpost + rrtmg_lw_post + GFS_rrtmg_post + + + + + GFS_suite_interstitial_phys_reset + GFS_suite_stateout_reset + get_prs_fv3 + GFS_suite_interstitial_1 + GFS_surface_generic_pre + GFS_surface_composites_pre + dcyc2t3 + GFS_surface_composites_inter + GFS_suite_interstitial_2 + + + + sfc_diff + GFS_surface_loop_control_part1 + sfc_nst_pre + sfc_nst + sfc_nst_post + noahmpdrv + sfc_sice + GFS_surface_loop_control_part2 + + + + GFS_surface_composites_post + sfc_diag + sfc_diag_post + GFS_surface_generic_post + GFS_PBL_generic_pre + satmedmfvdifq + GFS_PBL_generic_post + GFS_GWD_generic_pre + unified_ugwp + unified_ugwp_post + GFS_GWD_generic_post + GFS_suite_stateout_update + ozphys_2015 + h2ophys + get_phi_fv3 + GFS_suite_interstitial_3 + GFS_DCNV_generic_pre + cu_gf_driver_pre + cu_gf_driver + GFS_DCNV_generic_post + GFS_SCNV_generic_pre + GFS_SCNV_generic_post + GFS_suite_interstitial_4 + cnvc90 + GFS_MP_generic_pre + mp_thompson_pre + + + mp_thompson + + + mp_thompson_post + GFS_MP_generic_post + cu_gf_driver_post + maximum_hourly_diagnostics + + + + + GFS_stochastics + phys_tend + + + + diff --git a/sorc/ufs_model.fd_gsl/FV3/ccpp/suites/suite_FV3_GFS_v17_p8_mynn.xml b/sorc/ufs_model.fd_gsl/FV3/ccpp/suites/suite_FV3_GFS_v17_p8_mynn.xml new file mode 100644 index 0000000000..f77c71cc5c --- /dev/null +++ b/sorc/ufs_model.fd_gsl/FV3/ccpp/suites/suite_FV3_GFS_v17_p8_mynn.xml @@ -0,0 +1,98 @@ + + + + + + + GFS_time_vary_pre + GFS_rrtmg_setup + GFS_rad_time_vary + GFS_phys_time_vary + + + + + GFS_suite_interstitial_rad_reset + sgscloud_radpre + GFS_rrtmg_pre + GFS_radiation_surface + rad_sw_pre + rrtmg_sw + rrtmg_sw_post + rrtmg_lw_pre + rrtmg_lw + sgscloud_radpost + rrtmg_lw_post + GFS_rrtmg_post + + + + + GFS_suite_interstitial_phys_reset + GFS_suite_stateout_reset + get_prs_fv3 + GFS_suite_interstitial_1 + GFS_surface_generic_pre + GFS_surface_composites_pre + dcyc2t3 + GFS_surface_composites_inter + GFS_suite_interstitial_2 + + + + sfc_diff + GFS_surface_loop_control_part1 + sfc_nst_pre + sfc_nst + sfc_nst_post + noahmpdrv + sfc_sice + GFS_surface_loop_control_part2 + + + + GFS_surface_composites_post + sfc_diag + sfc_diag_post + GFS_surface_generic_post + + + + mynnedmf_wrapper + GFS_GWD_generic_pre + unified_ugwp + unified_ugwp_post + GFS_GWD_generic_post + GFS_suite_stateout_update + ozphys_2015 + h2ophys + get_phi_fv3 + GFS_suite_interstitial_3 + GFS_DCNV_generic_pre + samfdeepcnv + GFS_DCNV_generic_post + + + + GFS_suite_interstitial_4 + cnvc90 + GFS_MP_generic_pre + mp_thompson_pre + + + mp_thompson + + + mp_thompson_post + GFS_MP_generic_post + maximum_hourly_diagnostics + + + + + GFS_stochastics + phys_tend + + + + diff --git a/ush/cplvalidate.sh b/ush/cplvalidate.sh index a9d124aad1..29db7b3ad9 100755 --- a/ush/cplvalidate.sh +++ b/ush/cplvalidate.sh @@ -14,13 +14,14 @@ return case $confignamevarfornems in 'atm') combination=.false..false..false..false..false.;; 'datm') combination=.true..true..false..false..false.;; - 'atm_aer') combination=.true..false..false..false..true.;; + 'atm_aero') combination=.true..false..false..false..true.;; 'med_atm_ocn_ice') combination=.true..true..true..false..false.;; 'cpld') combination=.true..true..true..false..false.;; 'blocked_atm_wav') combination=.true..false..false..true..false.;; 'leapfrog_atm_wav')combination=.true..false..false..true..false.;; 'med_atm_ocn_ice_wav') combination=.true..true..true..true..false.;; 'cpld_wave') combination=.true..true..true..true..false.;; + 'cpld_aero_wave') combination=.true..true..true..true..true.;; 'medcold_atm_ocn_ice_wav') combination=.true..true..true..true..false.;; 'med_atm_ocn_ice_wav1way') combination=.true..true..true..true..false.;; 'med_atm_ocn_ice_wav1waywcurr') combination=.true..true..true..true..false.;; @@ -29,7 +30,7 @@ case $confignamevarfornems in *) echo "SUB cplvalidate: Combination not supported" exit 1 ;; esac -control=$cpl$cplflx$cplice$cplwav$cplchem +control=$cpl$cplflx$cplice$cplwav$cplchm if [ $control != $combination ]; then echo "SUB cplvalidate: inconsistent cpl setting!" exit 2 diff --git a/ush/forecast_postdet.sh b/ush/forecast_postdet.sh index 06908a0db3..2df631c1c0 100755 --- a/ush/forecast_postdet.sh +++ b/ush/forecast_postdet.sh @@ -186,7 +186,7 @@ EOF done else OROFIX=${OROFIX:-"${FIXfv3}/${CASE}"} - FIX_SFC=${FIX_SFC:-"${FIXgrd}/fix_sfc"} + FIX_SFC=${FIX_SFC:-"${OROFIX}/fix_sfc"} for n in $(seq 1 $ntiles); do $NLN ${OROFIX}/${CASE}_oro_data.tile${n}.nc $DATA/INPUT/oro_data.tile${n}.nc done @@ -217,6 +217,8 @@ EOF iopt_snf=${iopt_snf:-"4"} iopt_tbot=${iopt_tbot:-"2"} iopt_stc=${iopt_stc:-"3"} + IALB=${IALB:-2} + IEMS=${IEMS:-2} elif [ $(grep lsm_ruc ${_suite_file} | wc -l ) -gt 0 ]; then lsm="3" lsoil_lsm=9 @@ -242,40 +244,23 @@ EOF iopt_snf=${iopt_snf:-"4"} iopt_tbot=${iopt_tbot:-"2"} iopt_stc=${iopt_stc:-"1"} + IALB=${IALB:-1} + IEMS=${IEMS:-1} fi - # Scan suite file to determine whether it uses UGWP v1 - if [ $(grep -i ugwpv1_gsldrag ${_suite_file} | wc -l ) -gt 0 ]; then - gwd_opt="2" - knob_ugwp_version="1" - OROFIX_ugwd=${OROFIX_ugwd:-"${FIX_DIR}/fix_ugwd"} + # Files for GWD + OROFIX_ugwd=${OROFIX_ugwd:-"${FIX_DIR}/fix_ugwd"} + if [[ "$CCPP_SUITE" != "FV3_RAP_cires_ugwp" && "$CCPP_SUITE" != "FV3_RAP_noah_sfcdiff_unified_ugwp" && "$CCPP_SUITE" != "FV3_RAP_noah_sfcdiff_ugwpv1" ]] ; then ## JKH + $NLN ${OROFIX_ugwd}/ugwp_limb_tau.nc $DATA/ugwp_limb_tau.nc - for n in $(seq 1 $ntiles); do - $NLN ${OROFIX_ugwd}/$CASE/${CASE}_oro_data_ls.tile${n}.nc $DATA/INPUT/oro_data_ls.tile${n}.nc - $NLN ${OROFIX_ugwd}/$CASE/${CASE}_oro_data_ss.tile${n}.nc $DATA/INPUT/oro_data_ss.tile${n}.nc - done - # Scan suite file to determine whether it uses Unified UGWP - # JKH -- 'workaround' to get correct settings for unified_ugwp in parsing_namelist_FV3.sh for GSL - # (gwd_opt will be set to "2" in namelist) - elif [ $(grep -i unified_ugwp ${_suite_file} | wc -l ) -gt 0 ]; then - gwd_opt="3" - knob_ugwp_version="0" - launch_level=${launch_level:-$(echo "$LEVS/2.35" |bc)} - OROFIX_ugwd=${OROFIX_ugwd:-"${FIX_DIR}/fix_ugwd"} - for n in $(seq 1 $ntiles); do - $NLN ${OROFIX_ugwd}/$CASE/${CASE}_oro_data_ls.tile${n}.nc $DATA/INPUT/oro_data_ls.tile${n}.nc - $NLN ${OROFIX_ugwd}/$CASE/${CASE}_oro_data_ss.tile${n}.nc $DATA/INPUT/oro_data_ss.tile${n}.nc - done - else - gwd_opt="1" - knob_ugwp_version="0" - launch_level=${launch_level:-$(echo "$LEVS/2.35" |bc)} fi + for n in $(seq 1 $ntiles); do + $NLN ${OROFIX_ugwd}/$CASE/${CASE}_oro_data_ls.tile${n}.nc $DATA/INPUT/oro_data_ls.tile${n}.nc + $NLN ${OROFIX_ugwd}/$CASE/${CASE}_oro_data_ss.tile${n}.nc $DATA/INPUT/oro_data_ss.tile${n}.nc + done # GFS standard input data - IALB=${IALB:-1} - IEMS=${IEMS:-1} ISOL=${ISOL:-2} IAER=${IAER:-1011} ICO2=${ICO2:-2} @@ -289,13 +274,12 @@ EOF #### # copy CCN_ACTIVATE.BIN for Thompson microphysics if [ $imp_physics -eq 8 ]; then - $NCP $FV3INP/CCN_ACTIVATE.BIN CCN_ACTIVATE.BIN - #### - $NCP $FIX_AM/freezeH2O.dat . - $NCP $FIX_AM/qr_acr_qg.dat . - $NCP $FIX_AM/qr_acr_qs.dat . - sleep 60 + $NLN $FIX_AM/CCN_ACTIVATE.BIN $DATA/CCN_ACTIVATE.BIN + $NLN $FIX_AM/freezeH2O.dat $DATA/freezeH2O.dat + $NLN $FIX_AM/qr_acr_qg.dat $DATA/qr_acr_qg.dat + $NLN $FIX_AM/qr_acr_qs.dat $DATA/qr_acr_qs.dat fi + $NLN $FIX_AM/${O3FORC} $DATA/global_o3prdlos.f77 $NLN $FIX_AM/${H2OFORC} $DATA/global_h2oprdlos.f77 $NLN $FIX_AM/global_solarconstant_noaa_an.txt $DATA/solarconstant_noaa_an.txt @@ -369,31 +353,17 @@ EOF FNSNOC=${FNSNOC:-"$FIX_AM/global_snoclim.1.875.grb"} FNZORC=${FNZORC:-"igbp"} FNAISC=${FNAISC:-"$FIX_AM/CFSR.SEAICE.1982.2012.monthly.clim.grb"} - if [ $cplflx = ".false." ] ; then - FNALBC2=${FNALBC2:-"$FIX_AM/global_albedo4.1x1.grb"} - FNTG3C=${FNTG3C:-"$FIX_AM/global_tg3clim.2.6x1.5.grb"} - FNVEGC=${FNVEGC:-"$FIX_AM/global_vegfrac.0.144.decpercent.grb"} - FNMSKH=${FNMSKH:-"$FIX_AM/global_slmask.t1534.3072.1536.grb"} - FNVMNC=${FNVMNC:-"$FIX_AM/global_shdmin.0.144x0.144.grb"} - FNVMXC=${FNVMXC:-"$FIX_AM/global_shdmax.0.144x0.144.grb"} - FNSLPC=${FNSLPC:-"$FIX_AM/global_slope.1x1.grb"} - FNALBC=${FNALBC:-"$FIX_AM/global_snowfree_albedo.bosu.t${JCAP}.${LONB}.${LATB}.rg.grb"} - FNVETC=${FNVETC:-"$FIX_AM/global_vegtype.igbp.t${JCAP}.${LONB}.${LATB}.rg.grb"} - FNSOTC=${FNSOTC:-"$FIX_AM/global_soiltype.statsgo.t${JCAP}.${LONB}.${LATB}.rg.grb"} - FNABSC=${FNABSC:-"$FIX_AM/global_mxsnoalb.uariz.t${JCAP}.${LONB}.${LATB}.rg.grb"} - else - FNALBC2=${FNALBC2:-"${FIX_SFC}/${CASE}.facsf.tileX.nc"} - FNTG3C=${FNTG3C:-"${FIX_SFC}/${CASE}.substrate_temperature.tileX.nc"} - FNVEGC=${FNVEGC:-"${FIX_SFC}/${CASE}.vegetation_greenness.tileX.nc"} - FNMSKH=${FNMSKH:-"$FIX_AM/global_slmask.t1534.3072.1536.grb"} - FNVMNC=${FNVMNC:-"${FIX_SFC}/${CASE}.vegetation_greenness.tileX.nc"} - FNVMXC=${FNVMXC:-"${FIX_SFC}/${CASE}.vegetation_greenness.tileX.nc"} - FNSLPC=${FNSLPC:-"${FIX_SFC}/${CASE}.slope_type.tileX.nc"} - FNALBC=${FNALBC:-"${FIX_SFC}/${CASE}.snowfree_albedo.tileX.nc"} - FNVETC=${FNVETC:-"${FIX_SFC}/${CASE}.vegetation_type.tileX.nc"} - FNSOTC=${FNSOTC:-"${FIX_SFC}/${CASE}.soil_type.tileX.nc"} - FNABSC=${FNABSC:-"${FIX_SFC}/${CASE}.maximum_snow_albedo.tileX.nc"} - fi + FNALBC2=${FNALBC2:-"${FIX_SFC}/${CASE}.facsf.tileX.nc"} + FNTG3C=${FNTG3C:-"${FIX_SFC}/${CASE}.substrate_temperature.tileX.nc"} + FNVEGC=${FNVEGC:-"${FIX_SFC}/${CASE}.vegetation_greenness.tileX.nc"} + FNMSKH=${FNMSKH:-"$FIX_AM/global_slmask.t1534.3072.1536.grb"} + FNVMNC=${FNVMNC:-"${FIX_SFC}/${CASE}.vegetation_greenness.tileX.nc"} + FNVMXC=${FNVMXC:-"${FIX_SFC}/${CASE}.vegetation_greenness.tileX.nc"} + FNSLPC=${FNSLPC:-"${FIX_SFC}/${CASE}.slope_type.tileX.nc"} + FNALBC=${FNALBC:-"${FIX_SFC}/${CASE}.snowfree_albedo.tileX.nc"} + FNVETC=${FNVETC:-"${FIX_SFC}/${CASE}.vegetation_type.tileX.nc"} + FNSOTC=${FNSOTC:-"${FIX_SFC}/${CASE}.soil_type.tileX.nc"} + FNABSC=${FNABSC:-"${FIX_SFC}/${CASE}.maximum_snow_albedo.tileX.nc"} FNSMCC=${FNSMCC:-"$FIX_AM/global_soilmgldas.statsgo.t${JCAP}.${LONB}.${LATB}.grb"} # If the appropriate resolution fix file is not present, use the highest resolution available (T1534) @@ -546,12 +516,6 @@ EOF #------------------------------------------------------------------ # make symbolic links to write forecast files directly in memdir cd $DATA - if [ "$CCPP_SUITE" = "FV3_RAP_cires_ugwp" -o "$CCPP_SUITE" = "FV3_RAP_noah_sfcdiff_unified_ugwp" -o "$CCPP_SUITE" = "FV3_RAP_noah_sfcdiff_ugwpv1" ]; then - $NLN $FIX_AM/CCN_ACTIVATE.BIN CCN_ACTIVATE.BIN - $NLN $FIX_AM/freezeH2O.dat freezeH2O.dat - $NLN $FIX_AM/qr_acr_qg.dat qr_acr_qg.dat - $NLN $FIX_AM/qr_acr_qs.dat qr_acr_qs.dat - fi affix="nc" if [ "$OUTPUT_FILE" = "nemsio" ]; then @@ -788,7 +752,13 @@ MOM6_postdet() { $NCP -pf $FIXmom/$OCNRES/* $DATA/INPUT/ # Copy coupled grid_spec - $NCP -pf $FIX_DIR/fix_cpl/a${CASE}o${OCNRES}/grid_spec.nc $DATA/INPUT/ + spec_file="$FIX_DIR/fix_cpl/a${CASE}o${OCNRES}/grid_spec.nc" + if [ -s $spec_file ]; then + $NCP -pf $spec_file $DATA/INPUT/ + else + echo "FATAL ERROR: grid_spec file '$spec_file' does not exist" + exit 3 + fi # Copy mediator restart files to RUNDIR if [ $warm_start = ".true." -o $RERUN = "YES" ]; then @@ -896,7 +866,7 @@ CICE_postdet() { FRAZIL_FWSALT=${FRAZIL_FWSALT:-".true."} ktherm=${ktherm:-2} - tfrz_option=${tfrz_option:-"mushy"} + tfrz_option=${tfrz_option:-"'mushy'"} tr_pond_lvl=${tr_pond_lvl:-".true."} # Use level melt ponds tr_pond_lvl=true # restart_pond_lvl (if tr_pond_lvl=true): @@ -983,17 +953,46 @@ GOCART_rc() { # this variable is platform-dependent and should be set via a YAML file # link directory containing GOCART input dataset, if provided - if [ ! -z "${CHM_INPDIR}" ]; then - $NLN -sf ${CHM_INPDIR} $DATA + if [ ! -z "${AERO_INPUTS_DIR}" ]; then + $NLN -sf ${AERO_INPUTS_DIR} $DATA/ExtData status=$? [[ $status -ne 0 ]] && exit $status fi # copying GOCART configuration files - if [ ! -z "${CHM_CFGDIR}" ]; then - $NCP ${CHM_CFGDIR}/*.rc $DATA + if [ ! -z "${AERO_CONFIG_DIR}" ]; then + $NCP ${AERO_CONFIG_DIR}/*.rc $DATA status=$? [[ $status -ne 0 ]] && exit $status + # attempt to generate ExtData configuration file if not provided + if [ ! -f $DATA/AERO_ExtData.rc ]; then + { \ + echo "PrimaryExports%%" ; \ + cat ${AERO_CONFIG_DIR}/ExtData.other ; \ + cat ${AERO_CONFIG_DIR}/ExtData.${AERO_EMIS_FIRE:-none} ; \ + echo "%%" ; \ + } > $DATA/AERO_ExtData.rc + [[ $status -ne 0 ]] && exit $status + fi fi } +GOCART_postdet() { + echo "SUB ${FUNCNAME[0]}: Linking output data for GOCART" + + [[ ! -d $COMOUT/chem ]] && mkdir -p $COMOUT/chem + + for fhr in $fhrlst; do + if [ $fhr = 'anl' ]; then + continue + fi + VDATE=$($NDATE $fhr $IDATE) + YYYY=$(echo $VDATE | cut -c1-4) + MM=$(echo $VDATE | cut -c5-6) + DD=$(echo $VDATE | cut -c7-8) + HH=$(echo $VDATE | cut -c9-10) + SS=$((10#$HH*3600)) + + $NLN $COMOUT/chem/gocart.inst_aod.${YYYY}${MM}${DD}_${HH}00z.nc4 $DATA/gocart.inst_aod.${YYYY}${MM}${DD}_${HH}00z.nc4 + done +} diff --git a/ush/hpssarch_gen.sh b/ush/hpssarch_gen.sh index a1ea8685e9..6b7c595dfb 100755 --- a/ush/hpssarch_gen.sh +++ b/ush/hpssarch_gen.sh @@ -271,6 +271,17 @@ if [ $type = "gfs" ]; then echo "${dirname}ice*nc " >>ice.txt fi + if [ $DO_AERO = "YES" ]; then + dirpath="gfs.${PDY}/${cyc}/chem" + dirname="./${dirpath}" + + head="gocart" + + rm -f chem.txt + touch chem.txt + + echo "${dirname}/${head}*" >> chem.txt + fi #----------------------------------------------------- fi ##end of gfs diff --git a/ush/nems.configure.atm.IN b/ush/nems.configure.atm.IN index 5fbecb03cd..0d95533fa3 100644 --- a/ush/nems.configure.atm.IN +++ b/ush/nems.configure.atm.IN @@ -1,3 +1,6 @@ +# ESMF # +logKindFlag: ESMF_LOGKIND_MULTI + EARTH_component_list: ATM ATM_model: fv3 runSeq:: diff --git a/ush/nems.configure.atm_aero.IN b/ush/nems.configure.atm_aero.IN new file mode 100644 index 0000000000..b253255106 --- /dev/null +++ b/ush/nems.configure.atm_aero.IN @@ -0,0 +1,47 @@ +############################################# +#### NEMS Run-Time Configuration File ##### +############################################# + +# ESMF # + logKindFlag: ESMF_LOGKIND_MULTI + +# EARTH # +EARTH_component_list: ATM CHM +EARTH_attributes:: + Verbosity = max +:: + +# ATM # +ATM_model: @[atm_model] +ATM_petlist_bounds: @[atm_petlist_bounds] +ATM_attributes:: + Verbosity = max +:: + +# CHM # +CHM_model: @[chm_model] +CHM_petlist_bounds: @[chm_petlist_bounds] +CHM_attributes:: + Verbosity = max +:: + +# Run Sequence # +runSeq:: + @@[coupling_interval_fast_sec] + ATM phase1 + ATM -> CHM + CHM + CHM -> ATM + ATM phase2 + @ +:: + +# CMEPS variables + +DRIVER_attributes:: + mediator_read_restart = .false. +:: + +ALLCOMP_attributes:: + start_type = startup +:: diff --git a/ush/nems.configure.blocked_atm_wav.IN b/ush/nems.configure.blocked_atm_wav.IN index 8883d8dc22..4435cf793e 100644 --- a/ush/nems.configure.blocked_atm_wav.IN +++ b/ush/nems.configure.blocked_atm_wav.IN @@ -2,6 +2,9 @@ #### NEMS Run-Time Configuration File ##### ############################################# +# ESMF # + logKindFlag: ESMF_LOGKIND_MULTI + # EARTH # EARTH_component_list: ATM WAV EARTH_attributes:: diff --git a/ush/nems.configure.cpld.IN b/ush/nems.configure.cpld.IN index 57e7085cad..e38f5774a0 100644 --- a/ush/nems.configure.cpld.IN +++ b/ush/nems.configure.cpld.IN @@ -2,6 +2,9 @@ #### NEMS Run-Time Configuration File ##### ############################################# +# ESMF # +logKindFlag: ESMF_LOGKIND_MULTI + # EARTH # EARTH_component_list: MED ATM OCN ICE EARTH_attributes:: diff --git a/ush/nems.configure.cpld_aero_wave.IN b/ush/nems.configure.cpld_aero_wave.IN new file mode 100644 index 0000000000..166a51ae09 --- /dev/null +++ b/ush/nems.configure.cpld_aero_wave.IN @@ -0,0 +1,136 @@ +############################################# +#### NEMS Run-Time Configuration File ##### +############################################# + +# ESMF # + logKindFlag: ESMF_LOGKIND_MULTI + +# EARTH # +EARTH_component_list: MED ATM CHM OCN ICE WAV +EARTH_attributes:: + Verbosity = 0 +:: + +# MED # +MED_model: @[med_model] +MED_petlist_bounds: @[med_petlist_bounds] +:: + +# ATM # +ATM_model: @[atm_model] +ATM_petlist_bounds: @[atm_petlist_bounds] +ATM_attributes:: + Verbosity = 0 + DumpFields = @[DumpFields] + ProfileMemory = false + OverwriteSlice = true +:: + +# CHM # +CHM_model: @[chm_model] +CHM_petlist_bounds: @[chm_petlist_bounds] +CHM_attributes:: + Verbosity = 0 +:: + +# OCN # +OCN_model: @[ocn_model] +OCN_petlist_bounds: @[ocn_petlist_bounds] +OCN_attributes:: + Verbosity = 0 + DumpFields = @[DumpFields] + ProfileMemory = false + OverwriteSlice = true + mesh_ocn = @[MESH_OCN_ICE] +:: + +# ICE # +ICE_model: @[ice_model] +ICE_petlist_bounds: @[ice_petlist_bounds] +ICE_attributes:: + Verbosity = 0 + DumpFields = @[DumpFields] + ProfileMemory = false + OverwriteSlice = true + mesh_ice = @[MESH_OCN_ICE] + stop_n = @[RESTART_N] + stop_option = nhours + stop_ymd = -999 +:: + +# WAV # +WAV_model: @[wav_model] +WAV_petlist_bounds: @[wav_petlist_bounds] +WAV_attributes:: + Verbosity = 0 + OverwriteSlice = false +:: + +# CMEPS warm run sequence +runSeq:: +@@[coupling_interval_slow_sec] + MED med_phases_prep_ocn_avg + MED -> OCN :remapMethod=redist + OCN -> WAV + WAV -> OCN :srcMaskValues=1 + OCN + @@[coupling_interval_fast_sec] + MED med_phases_prep_atm + MED med_phases_prep_ice + MED -> ATM :remapMethod=redist + MED -> ICE :remapMethod=redist + WAV -> ATM :srcMaskValues=1 + ATM -> WAV + ICE -> WAV + ATM phase1 + ATM -> CHM + CHM + CHM -> ATM + ATM phase2 + ICE + WAV + ATM -> MED :remapMethod=redist + MED med_phases_post_atm + ICE -> MED :remapMethod=redist + MED med_phases_post_ice + MED med_phases_prep_ocn_accum + @ + OCN -> MED :remapMethod=redist + MED med_phases_post_ocn + MED med_phases_restart_write +@ +:: + +# CMEPS variables + +DRIVER_attributes:: +:: +MED_attributes:: + ATM_model = @[atm_model] + ICE_model = @[ice_model] + OCN_model = @[ocn_model] + history_n = 0 + history_option = nhours + history_ymd = -999 + coupling_mode = @[CPLMODE] + history_tile_atm = @[ATMTILESIZE] +:: +ALLCOMP_attributes:: + ScalarFieldCount = 2 + ScalarFieldIdxGridNX = 1 + ScalarFieldIdxGridNY = 2 + ScalarFieldName = cpl_scalars + start_type = @[RUNTYPE] + restart_dir = RESTART/ + case_name = ufs.cpld + restart_n = @[RESTART_N] + restart_option = nhours + restart_ymd = -999 + dbug_flag = @[cap_dbug_flag] + use_coldstart = @[use_coldstart] + use_mommesh = @[use_mommesh] + eps_imesh = @[eps_imesh] + stop_n = @[FHMAX] + stop_option = nhours + stop_ymd = -999 +:: diff --git a/ush/nems.configure.cpld_wave.IN b/ush/nems.configure.cpld_wave.IN index 949e3c8199..d99d1c3bf4 100644 --- a/ush/nems.configure.cpld_wave.IN +++ b/ush/nems.configure.cpld_wave.IN @@ -2,6 +2,9 @@ #### NEMS Run-Time Configuration File ##### ############################################# +# ESMF # +logKindFlag: ESMF_LOGKIND_MULTI + # EARTH # EARTH_component_list: MED ATM OCN ICE WAV EARTH_attributes:: diff --git a/ush/nems.configure.leapfrog_atm_wav.IN b/ush/nems.configure.leapfrog_atm_wav.IN index 6229deae2a..5c01c08a4f 100644 --- a/ush/nems.configure.leapfrog_atm_wav.IN +++ b/ush/nems.configure.leapfrog_atm_wav.IN @@ -2,6 +2,9 @@ #### NEMS Run-Time Configuration File ##### ############################################# +# ESMF # + logKindFlag: ESMF_LOGKIND_MULTI + # EARTH # EARTH_component_list: ATM WAV EARTH_attributes:: diff --git a/ush/nems_configure.sh b/ush/nems_configure.sh index e0b3bab05f..d8de1d849d 100755 --- a/ush/nems_configure.sh +++ b/ush/nems_configure.sh @@ -58,7 +58,13 @@ wav_petlist_bounds=${wav_petlist_bounds:-"$(( $ATMPETS+$OCNPETS+$ICEPETS )) $(( chm_petlist_bounds=${chm_petlist_bounds:-"0 $(( $CHMPETS-1 ))"} # Copy the selected template into run directory -cp $SCRIPTDIR/nems.configure.$confignamevarfornems.IN tmp1 +infile="$SCRIPTDIR/nems.configure.$confignamevarfornems.IN" +if [ -s $infile ]; then + cp $infile tmp1 +else + echo "FATAL ERROR: nem.configure template '$infile' does not exist!" + exit 1 +fi sed -i -e "s;@\[med_model\];cmeps;g" tmp1 sed -i -e "s;@\[atm_model\];$ATM_model;g" tmp1 sed -i -e "s;@\[med_petlist_bounds\];$med_petlist_bounds;g" tmp1 @@ -98,7 +104,7 @@ if [ $cplice = .true. ]; then sed -i -e "s;@\[MESH_OCN_ICE\];$MESH_OCN_ICE;g" tmp1 sed -i -e "s;@\[FHMAX\];$FHMAX_GFS;g" tmp1 fi -if [ $cplchem = .true. ]; then +if [ $cplchm = .true. ]; then sed -i -e "s;@\[chm_model\];$CHM_model;g" tmp1 sed -i -e "s;@\[chm_petlist_bounds\];$chm_petlist_bounds;g" tmp1 sed -i -e "s;@\[coupling_interval_fast_sec\];$CPL_FAST;g" tmp1 diff --git a/ush/parsing_model_configure_FV3.sh b/ush/parsing_model_configure_FV3.sh index 94a79c95c5..4c35179e90 100755 --- a/ush/parsing_model_configure_FV3.sh +++ b/ush/parsing_model_configure_FV3.sh @@ -14,7 +14,6 @@ FV3_model_configure(){ rm -f model_configure cat >> model_configure <> model_configure < ice_in < diag_table << EOF @@ -31,22 +34,36 @@ EOF cat $DIAG_TABLE >> diag_table fi +if [ ! -z "${AERO_DIAG_TABLE}" ]; then + cat ${AERO_DIAG_TABLE} >> diag_table +fi + cat $DIAG_TABLE_APPEND >> diag_table +# copy data table $NCP $DATA_TABLE data_table -$NCP $FIELD_TABLE field_table -cat > input.nml < 0 )); then + prec=$( grep -F -n TRACER ${FIELD_TABLE} 2> /dev/null | tail -n ${dnats} | head -1 | cut -d: -f1 ) + prec=${prec:-0} + prec=$(( prec > 0 ? prec - 1 : prec )) + fi + { \ + head -n ${prec} ${FIELD_TABLE} ; \ + cat ${AERO_FIELD_TABLE} ; \ + tail -n $(( nrec - prec )) ${FIELD_TABLE} ; \ + } > field_table + # add non-prognostic tracers from additional table + dnats=$(( dnats + dnats_aero )) +else + $NCP $FIELD_TABLE field_table +fi +cat > input.nml < input.nml <> input.nml << EOF nwat = ${nwat:-2} na_init = $na_init d_ext = 0. - dnats = ${dnats:-0} + dnats = ${dnats} fv_sg_adj = ${fv_sg_adj:-"450"} d2_bg = 0. nord = ${nord:-3} @@ -197,7 +215,7 @@ EOF ltaerosol = ${ltaerosol:-".F."} lradar = ${lradar:-".F."} dt_inner = ${dt_inner:-"40."} - ttendlim = ${ttendlim:-0.005} + ttendlim = ${ttendlim:-"-999"} oz_phys = ${oz_phys:-".false."} oz_phys_2015 = ${oz_phys_2015:-".true."} lsoil_lsm = ${lsoil_lsm:-"4"} @@ -215,6 +233,17 @@ EOF bl_mynn_mixqt = ${bl_mynn_mixqt:="0"} bl_mynn_output = ${bl_mynn_output:="0"} bl_mynn_closure = ${bl_mynn_closure:="2.6"} + do_ugwp = ${do_ugwp:-".false."} + do_tofd = ${do_tofd:-".true."} + gwd_opt = ${gwd_opt:-"2"} + do_ugwp_v0 = ${do_ugwp_v0:-".true."} + do_ugwp_v1 = ${do_ugwp_v1:-".false."} + do_ugwp_v0_orog_only = ${do_ugwp_v0_orog_only:-".false."} + do_ugwp_v0_nst_only = ${do_ugwp_v0_nst_only:-".false."} + do_gsl_drag_ls_bl = ${do_gsl_drag_ls_bl:-".false."} + do_gsl_drag_ss = ${do_gsl_drag_ss:-".true."} + do_gsl_drag_tofd = ${do_gsl_drag_tofd:-".true."} + do_ugwp_v1_orog_only = ${do_ugwp_v1_orog_only:-".false."} min_lakeice = ${min_lakeice:-"0.15"} min_seaice = ${min_seaice:-"0.15"} use_cice_alb = ${use_cice_alb:-".false."} @@ -255,6 +284,38 @@ EOF bl_mynn_edmf_mom = ${bl_mynn_edmf_mom:-"1"} min_lakeice = ${min_lakeice:-"0.15"} min_seaice = ${min_seaice:-"0.15"} +EOF + ;; + FV3_GFS_v17*) + cat >> input.nml << EOF + iovr = ${iovr:-"3"} + ltaerosol = ${ltaerosol:-".false."} + lradar = ${lradar:-".false."} + ttendlim = ${ttendlim:-"-999"} + dt_inner = ${dt_inner:-"$(echo "$DELTIM/2" |bc)"} + oz_phys = ${oz_phys:-".false."} + oz_phys_2015 = ${oz_phys_2015:-".true."} + lsoil_lsm = ${lsoil_lsm:-"4"} + do_mynnedmf = ${do_mynnedmf:-".false."} + do_mynnsfclay = ${do_mynnsfclay:-".false."} + icloud_bl = ${icloud_bl:-"1"} + bl_mynn_edmf = ${bl_mynn_edmf:-"1"} + bl_mynn_tkeadvect = ${bl_mynn_tkeadvect:-".true."} + bl_mynn_edmf_mom = ${bl_mynn_edmf_mom:-"1"} + do_ugwp = ${do_ugwp:-".false."} + do_tofd = ${do_tofd:-".false."} + gwd_opt = ${gwd_opt:-"2"} + do_ugwp_v0 = ${do_ugwp_v0:-".true."} + do_ugwp_v1 = ${do_ugwp_v1:-".false."} + do_ugwp_v0_orog_only = ${do_ugwp_v0_orog_only:-".false."} + do_ugwp_v0_nst_only = ${do_ugwp_v0_nst_only:-".false."} + do_gsl_drag_ls_bl = ${do_gsl_drag_ls_bl:-".false."} + do_gsl_drag_ss = ${do_gsl_drag_ss:-".true."} + do_gsl_drag_tofd = ${do_gsl_drag_tofd:-".true."} + do_ugwp_v1_orog_only = ${do_ugwp_v1_orog_only:-".false."} + min_lakeice = ${min_lakeice:-"0.15"} + min_seaice = ${min_seaice:-"0.15"} + use_cice_alb = ${use_cice_alb:-".false."} EOF ;; *) @@ -319,7 +380,6 @@ cat >> input.nml <> input.nml <> input.nml <> input.nml << EOF &cires_ugwp_nml - knob_ugwp_version = ${knob_ugwp_version:-0} knob_ugwp_solver = ${knob_ugwp_solver:-2} knob_ugwp_source = ${knob_ugwp_source:-1,1,0,0} knob_ugwp_wvspec = ${knob_ugwp_wvspec:-1,25,25,25} @@ -396,27 +448,15 @@ case ${gwd_opt:-"2"} in knob_ugwp_doheat = ${knob_ugwp_doheat:-1} knob_ugwp_dokdis = ${knob_ugwp_dokdis:-1} knob_ugwp_ndx4lh = ${knob_ugwp_ndx4lh:-1} + knob_ugwp_version = ${knob_ugwp_version:-0} launch_level = ${launch_level:-54} - $cires_ugwp_nml / - EOF - ;; - 2) +fi + +if [ $knob_ugwp_version -eq 1 ]; then cat >> input.nml << EOF - gwd_opt = 2 - do_ugwp = .false. - do_ugwp_v0 = .false. - do_ugwp_v1 = .true. - do_tofd = .false. - do_ugwp_v1_orog_only = .false. - do_gsl_drag_ls_bl = ${do_gsl_drag_ls_bl:-".true."} - do_gsl_drag_ss = ${do_gsl_drag_ss:-".true."} - do_gsl_drag_tofd = ${do_gsl_drag_tofd:-".true."} - $gfs_physics_nml -/ &cires_ugwp_nml - knob_ugwp_version = ${knob_ugwp_version:-1} knob_ugwp_solver = ${knob_ugwp_solver:-2} knob_ugwp_source = ${knob_ugwp_source:-1,1,0,0} knob_ugwp_wvspec = ${knob_ugwp_wvspec:-1,25,25,25} @@ -427,7 +467,7 @@ EOF knob_ugwp_doheat = ${knob_ugwp_doheat:-1} knob_ugwp_dokdis = ${knob_ugwp_dokdis:-2} knob_ugwp_ndx4lh = ${knob_ugwp_ndx4lh:-4} - knob_ugwp_palaunch = ${knob_ugwp_palaunch:-275.0e2} + knob_ugwp_palaunch = ${knob_ugwp_palaunch:-275.0e2} knob_ugwp_nslope = ${knob_ugwp_nslope:-1} knob_ugwp_lzmax = ${knob_ugwp_lzmax:-15.750e3} knob_ugwp_lzmin = ${knob_ugwp_lzmin:-0.75e3} @@ -436,45 +476,11 @@ EOF knob_ugwp_tauamp = ${knob_ugwp_tauamp:-3.0e-3} knob_ugwp_lhmet = ${knob_ugwp_lhmet:-200.0e3} knob_ugwp_orosolv = ${knob_ugwp_orosolv:-'pss-1986'} - $cires_ugwp_nml / EOF - ;; - 3) - cat >> input.nml <> input.nml @@ -485,10 +491,10 @@ cat >> input.nml <> input.nml <> input.nml <> input.nml <{tasks}' - if task in ['arch', 'earc', 'getic']: + #JKHif task in ['arch', 'earc', 'getic']: + if task in ['arch', 'earc']: queuestr = '&QUEUE;' if scheduler in ['slurm'] else '&QUEUE_SERVICE;' else: queuestr = '&QUEUE;'